And as you can see this is pretty much the algorithm used by ZyXEL the only difference is the MAGIC VALUE (
). You can use the script below to generate your own password for ATEN (remember if you will not use the ATSE command your seed will be zero).
In my case the magic command was "ATEN1,508fad63" and this is what "ATHE" produced afterwards:
======= Debug Command Listing =======
AT just answer OK
ATHE print help
ATBAx change baudrate. 1:38.4k, 2:19.2k, 3:9.6k 4:57.6k 5:115.2k
ATENx,(y) set BootExtension Debug Flag (y=password)
ATSE show the seed of password generator
ATTI(h,m,s) change system time to hour:min:sec or show current time
ATDA(y,m,d) change system date to year/month/day or show current date
ATDS dump RAS stack
ATDT dump Boot Module Common Area
ATDUx,y dump memory contents from address x for length y
ATWBx,y write address x with 8-bit value y
ATWWx,y write address x with 16-bit value y
ATWLx,y write address x with 32-bit value y
ATRBx display the 8-bit value of address x
ATRWx display the 16-bit value of address x
ATRLx display the 32-bit value of address x
ATGO(x) run program at addr x or boot router
ATGR boot router
ATGT run Hardware Test Program
AT%Tx Enable Hardware Test Program at boot up
ATBTx block0 write enable (1=enable, other=disable)
ATRTw,x,y(,z) RAM test level w, from address x to y (z iterations)
ATWEa(,b,c,d) write MAC addr, Country code, EngDbgFlag, FeatureBit to flash ROM
ATCUx write Country code to flash ROM
ATCB copy from FLASH ROM to working buffer
ATCL clear working buffer
ATSB save working buffer to FLASH ROM
ATBU dump manufacturer related data in working buffer
ATSH dump manufacturer related data in ROM
ATWMx set low 6 digits MAC address in working buffer
ATMHx set hight 6 digits MAC address in working buffer
ATBS show the bootbase seed of password generator
ATLBx xmodem upload bootbase,x is password
ATSMx set 6 digits MAC address in working buffer
ATCOx set country code in working buffer
ATFLx set EngDebugFlag in working buffer
ATSTx set ROMRAS address in working buffer
ATSYx set system type in working buffer
ATVDx set vendor name in working buffer
ATPNx set product name in working buffer
ATFEx,y,... set feature bits in working buffer
ATMP check & dump memMapTab
ATDOx,y download from address x for length y to PC via XMODEM
ATTD download router configuration to PC via XMODEM
ATUPx,y upload to RAM address x for length y from PC via XMODEM
ATUR upload router firmware to flash ROM
ATDC hardware version check disable during uploading firmware
ATLC upload router configuration file to flash ROM
ATUXx(,y) xmodem upload from flash block x to y
ATERx,y erase flash rom from block x to y
ATWFx,y,z copy data from addr x to flash addr y, length z
ATXSx xmodem select: x=0: CRC mode(default); x=1: checksum mode
ATLD Upload Configuration File and Default ROM File to Flash
ATBR Reset to default Romfile
ATCD Convert Running ROM File to Default ROM File into Flash
Now we have access to few other interesting commands. Remember the objective is to patch the ROM-0 download vulnerability.
Patching the vulnerability (virtually)
In order to patch the vulnerability we need to patch the stage2 firmware (we are still on the stage1 level - minimal boot firmware). You can go to the stage2 firmware by typing ATGR (boot router) command. The bad thing about that is after this step we will have no access to stage 1 commands. So we can't really patch :(
(Compressed)
Version: ADSL ATU-R, start: bfc5d430
Length: 390890, Checksum: 08F1
Compressed Length: D7150, Checksum: DBE7
Copyright (c) 2001 - 2006 TP-LINK TECHNOLOGIES CO., LTD
initialize ch = 0, IP175C, ethernet address: 00:XX:XX:XX:XX:41
initialize ch = 1, ethernet address: 00:XX:XX:XX:XX:41
Wan Channel init ........ done
Initializing ADSL F/W ........ done
ANNEXAL
set try multimode number to 3 (dropmode try num 3)
Syncookie switch On!
Press ENTER to continue...
Valid commands are:
sys exit ether wan
ip bridge dot1q pktqos
show set lan
tc>
So what happened? The compressed stage 2 firmware got decompressed and executed. Knowing the ATGR command boots up the router let's find the instruction that passes the execution to the stage 2 firmware. This seems to be the best option because the firmware at that point is already unpacked.
ROM:80011E8C loc_80011E8C: # CODE XREF: LoadAndRunImage+D8j
ROM:80011E8C jal sub_80009B24
ROM:80011E90 li $a0, 0x1F4
ROM:80011E94
ROM:80011E94 execute_image: # execute image
ROM:80011E94 jalr $s0 # jump and link to address in $s0
ROM:80011E98 nop
Now the question is what is the address stored in $s0? Obviously we still can't set a breakpoint, we can't view the registers so we need to leak it using some trick. But wait we can read and write memory so take a look on the following command:
ATWL 80011E94, ae30001c
ATGR
This command patches the instruction at 0x80011E94 with ae30001c (the byte representation of MIPS "sw $s0, 0x1C($s1)" instruction). By overwriting the original jalr $s0 with sw $s0, 0x1C($s1) we have forced the program to store the contents of $s0 register to 0x8001FF1C memory address. So now stage 2 firmware will not be executed instead the "ERROR" message will be presented and we will still have the access to the stage1 console! So let's take a look what was stored at 0x8001FF1C.
ATRL 8001FF1C
8001FF1C: 80020000
So now when we have leaked stage 2 address (0x80020000) we can dump it and analyse it properly).
So keeping in mind that we want to block ROM-0 from being downloadable I have found (using trail and error and xrefs) following code fragment that was a great candidate for patching:
ROM:80248C98 la $a1, aRom0_8 # "/rom-0"
ROM:80248CA0 jal sub_802D0F2C
ROM:80248CA4 move $a0, $s2
ROM:80248CA8 beqz $v0, loc_80248CB8 # NOP THIS SIR
ROM:80248CAC li $v1, 0xE
ROM:80248CB0 j loc_exit
ROM:80248CB4 sw $v1, 0x64($s3)
ROM:80248DEC loc_exit: # CODE XREF: sub_80248AA8+208j
ROM:80248DEC # sub_80248AA8+21Cj ...
ROM:80248DEC lw $ra, 0x28+var_4($sp)
ROM:80248DF0 lw $s0, 0x28+var_28($sp)
ROM:80248DF4 lw $s1, 0x28+var_24($sp)
ROM:80248DF8 lw $s2, 0x28+var_20($sp)
ROM:80248DFC lw $s3, 0x28+var_1C($sp)
ROM:80248E00 lw $s4, 0x28+var_18($sp)
ROM:80248E04 lw $s5, 0x28+var_14($sp)
ROM:80248E08 lw $s6, 0x28+var_10($sp)
ROM:80248E0C lw $fp, 0x28+var_8($sp)
ROM:80248E10 nop
ROM:80248E14 jr $ra
ROM:80248E18 addiu $sp, 0x28
So the beqz $v0, loc_80248CB8 instruction jumps to loc_80248CB8 and we simply don't want that. So instead when the condition is not met this procedure jumps to loc_exit which is basically saying "goodbye". So let's patch the beqz with NOP:
ATEN 1,508fad63
ATWL 80011E94, 00000000
ATGR
ATWL 80248CA8, 00000000
ATGO 80020000
So now when the stage 2 firmware is patched in memory and booted it is time to test it (for the record: 192.168.2.1 - is my router addr)!
SUCCESS!
Conclusion
This simple patch was done in virtual memory only meaning everything will come back to the state it was before applying it after your router reboots. This is convenient from one side and inconvenient from the other. It is certainly possible to flash the device with modified firmware but I will not provide such methods here. One way or another I hope you have enjoyed the journey as I did. There are probably a plenty more vulnerabilities like this one somewhere inside every router so sometimes such ghetto hacks may become necessary :-) (let's hope not!). Obviously this was just one simply patch and it wasn't heavily tested in action. Please use this method as a way of learning something NOT as an actual remedy (probably there are plenty of other bugs that need to be patched as well). - PIOTR 31.01.2014 / EOF.
Bonus Binwalk
Some additional material from binwalk:
root@ubuntu:/home/die/binwalk/firmware# binwalk firmware.bin
DECIMAL HEX DESCRIPTION
-------------------------------------------------------------------------------------------------------------------
84992 0x14C00 ZynOS header, header size: 48 bytes, rom image type: ROMBIN, uncompressed size: 66696,
compressed size: 16845, uncompressed checksum:
0xC92E, compressed checksum: 0xC9CE, flags: 0xE0, uncompressed checksum is valid, the binary is compressed, compressed
checksum is valid, memory map table address: 0x0
85043 0x14C33 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes,
uncompressed size: 66696 bytes
128002 0x1F402 GIF image data, version "89a", 200 x 50
136194 0x21402 GIF image data, version "89a", 560 x 50
349184 0x55400 ZynOS header, header size: 48 bytes, rom image type: ROMBIN, uncompressed size: 3737744,
compressed size: 880976, uncompressed checksum:
0x8F1, compressed checksum: 0xDBE7, flags: 0xE0, uncompressed checksum is valid, the binary is compressed, compressed
checksum is valid, memory map table address: 0x0
349235 0x55433 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes,
uncompressed size: 3737744 bytes
dd if=firmware.bin bs=1 skip=85043 of=image1.lzma
dd if=firmware.bin bs=1 skip=349235 of=image2.lzma
root@ubuntu:/home/die/binwalk/firmware# lzma -d image1.lzma
root@ubuntu:/home/die/binwalk/firmware# lzma -d image2.lzma
die@ubuntu:~/binwalk/firmware$ binwalk image2
DECIMAL HEX DESCRIPTION
-------------------------------------------------------------------------------------------------------------------
2840932 0x2B5964 Copyright string: " (c) 1994 - 2004 ZyXEL Communications Corp."
2840988 0x2B599C Copyright string: " (c) 2001 - 2006 TrendChip Technologies Corp."
2841044 0x2B59D4 Copyright string: " (c) 2001 - 2006 "
2865263 0x2BB86F LZMA compressed data, properties: 0x48, dictionary size: 33554432 bytes,
uncompressed size: 16777216 bytes
3037283 0x2E5863 LZMA compressed data, properties: 0x5C, dictionary size: 33554432 bytes,
uncompressed size: 16777216 bytes
3037543 0x2E5967 LZMA compressed data, properties: 0xB4, dictionary size: 33554432 bytes,
uncompressed size: 16777216 bytes
3209377 0x30F8A1 LZMA compressed data, properties: 0x40, dictionary size: 2097152 bytes,
uncompressed size: 2097216 bytes
3350495 0x331FDF LZMA compressed data, properties: 0xB8, dictionary size: 16777216 bytes,
uncompressed size: 538981760 bytes
3445523 0x349313 LZMA compressed data, properties: 0x40, dictionary size: 16777216 bytes,
uncompressed size: 949368448 bytes
3563851 0x36614B LZMA compressed data, properties: 0x90, dictionary size: 16777216 bytes,
uncompressed size: 33554432 bytes
3652626 0x37BC12 GIF image data, version "89a", 16 x 16
3653238 0x37BE76 GIF image data, version "89a", 16 x 16
3654302 0x37C29E GIF image data, version "89a", 16 x 16
3655370 0x37C6CA GIF image data, version "89a", 16 x 16
3734691 0x38FCA3 LZMA compressed data, properties: 0x88, dictionary size: 33554432 bytes,
uncompressed size: 167772160 bytes
3737404 0x39073C Copyright string: " (c) 1996-2000 Express Logic Inc. * ThreadX R3900/Green Hills "