7

I just got a Zte ZXHN F660 GPON ONT Wireless router with my optic fiber internet provided by my telecom provider.

My first discovery was that the eth Lan ports are bricked wich means I can only use one Lan at time and there's a telnet default user and password i cannot remove or change - well i can do but only provisory:
I was able to bypass the problem using telnet and busybox console but the fix is provisory unfortunately. Each time the router reboots the old config gets installed, wich is normal I think.

I decided to investigate a bit more and made a config backup threw the webui wich gives me a .bin file. Using binwalk i saw that there is 3 data blocks compressed with zlib. I made a quick and dirty script in python to unpack them and concatenate them in a XML file wich provides all the router's setup.

I managed to modify and repack it, but here's the problem: the header contain some informations that make the config upgrade fail each time. With an hex editor I noticed that before each compressed zlib block there is the hex values of the pack inflated and deflated, so I was able to correct them. There's also in the header a CRC32 of the concatenation of each zlib compressed packs without headers, so I was also able to correct it when I repack the new file.
But next to it there's what I believe is an other CRC32, but I can't find out what that CRC32 is about so any help is welcome to this point.

I've downloaded the httpd and cspd files from my router and try to disassemble them using IDA, but the loops I find in gives me the seasick and it's beyond my actual skills so that would be great to get some help and explaination about how to repack the xml file into the bin file.

Thanks for any help or advice.

Here the first offsets of my dummy config.bin file:

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000  04 03 02 01 00 00 00 00 00 00 00 04 46 36 36 30  ............F660
00000010  01 02 03 04 00 00 00 00 00 02 37 AB 00 00 36 79  ..........7«..6y
00000020  00 01 00 00 97 10 5B C9 6E 6F 53 12 00 00 00 00  ....—.[ÉnoS.....
00000030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000040  00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00  ................
00000050  00 00 16 B8 00 00 17 00 78 DA ED 3D 5B 77 DB 38  ...¸....xÚí=[wÛ8
00000060  73 EF FD 15 69 DA 3E 75 D7 E6 4D F4 E5 7C DB 53  sïý.iÚ>u×æMôå|ÛS
00000070  99 94 1D 9D 95 14 46 94 AD AF FB E2 03 53 B0 CC  ™”..•.F”.¯ûâ.S°Ì
00000080  13 1A 64 49 CA 97 FD F5 05 A8 1B 05 02 20 48 C9  ..dIÊ—ýõ.¨... HÉ
00000090  B2 94 D2 49 6C 47 33 04 06 83 B9 01 1C 60 FE 61  ²”ÒIlG3..ƒ¹..`þa
000000A0  5F FD D7 BF FC 63 F4 10 7C 41 E0 19 FE F1 D5 BE  _ý׿ücô.|Aà.þñÕ¾
000000B0  BA 02 09 FC FA 65 18 BE 5A E1 0C A5 7F 7C 55 BF  º..üúe.¾Zá.¥.|U¿
000000C0  62 04 FC DF 2F 83 F0 8F AF 0A F9 8F DD 5F 20 77  b.üß/ƒð.¯.ù.Ý_ w
000000D0  AF BB E8 31 FC FA E5 05 04 18 A4 E2 3F 9A A2 64  ¯»è1üúå...¤â?š¢d
000000E0  FF 94 EC 67 DB 30 F0 67 F8 CB 50 8C 6B F2 F3 EB  ÿ”ìgÛ0ðgøËPŒkòóë
000000F0  29 7E FC 14 37 46 7E E0 4E 37 BA 1E B7 07 76 BE  )~ü.7F~àN7º.·.v¾
00000100  63 8D DF F1 9D 0F 5F 07 F8 B7 45 D7 DD 1B FB 64  c.ßñ.._.ø·E×Ý.ûd
00000110  6C AB 59 EB 2B 1C D2 DC E8 3D 5A E2 18 05 A8 65  l«Yë+.ÒÜè=Zâ..¨e
00000120  5B 20 F2 FC F4 7D 81 41 3D 3F 8A FD E9 14 C6 1D  [ òüô}.A=?Šýé.Æ.
00000130  04 1E 02 B8 81 B2 18 C0 92 34 B5 9C 34 4D 48 9A  ...¸.².À’4µœ4MHš

Here a copy of a dummy config.bin, cspd and httpd files of my router:

config.bin,cspd,httpd files

5

The second CRC is the CRC of the header started from 0x10 to 0x28. The following script checks the config file based on the dbcCfgFileDecry function, which verify and decompress the config file from offset 0x10.

import sys
import binascii
import struct
import zlib

if (len(sys.argv) < 1):
    print 'usage: check_config.py config_file'

cf = open(sys.argv[1], 'rb')
h = cf.read(0x4c)

#--------------------
# read the header
if (h[0:4] != '\x04\x03\x02\x01'):
    print 'Invalid magic'
    sys.exit(-1)

if (h[0x10:0x14] != '\x01\x02\x03\x04'):
    print 'Invalid magic'
    sys.exit(-1)

h2 = h[0x10:]
hcrc_calc = binascii.crc32(h2[0:0x18])&0xffffffff
hcrc_store = struct.unpack('!L', h2[0x18:0x1c])[0]
print 'calc: %x - stored: %x'%(hcrc_calc, hcrc_store)
if (hcrc_calc != hcrc_store):
    print 'Invalid header CRC'
    sys.exit(-2)

block_buffer_size = struct.unpack('!L', h2[0x10:0x14])[0]
# used to allocate memory for temp buffers
print 'block buffer size: %x'%(block_buffer_size)

#--------------------
# read the blocks
fout = open('%s.xml'%(sys.argv[1]), 'wb')
cumulate_crc = 0
while (True):
    bheader = cf.read(0x0c)
    if (len(bheader) == 0):
        break
    block_size = struct.unpack('!L', bheader[0x04:0x08])[0]
    print 'block size: %x'%(block_size)

    # read the whole block to previously allocated buffer
    # Possible heap based buffer overflow, because the size was not checked in
    # the dbcCfgFileDecry function!
    block = cf.read(block_size)
    cumulate_crc = binascii.crc32(block, cumulate_crc)&0xffffffff
    decompressed = zlib.decompress(block)
    fout.write(decompressed)

stored_cumulate_crc = struct.unpack('!L', h2[0x14:0x18])[0]
print 'cumulate crc: calc: %x - stored: %x'%(cumulate_crc, stored_cumulate_crc)
if (cumulate_crc != stored_cumulate_crc):
    print 'Invalid cumulate CRC'
    sys.exit(-3)

cf.close()
fout.close()

As a side note, the dbcCfgFileDecry function contains a heap-based buffer overflow vulnerability, because it did not check whether the current block will fit into the allocated buffer.

  • Thank you very much ebux ! That match perfectly, do you mind to explain me the process you went threw to find out so i can learn more. Thank you :) – SebastienDuval Aug 30 '16 at 10:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.