首页 / 资讯中心 / 文章详情

自建CTF战队级工具箱:从目录结构到Docker环境与脚本组织

自建CTF战队级工具箱:从目录结构到Docker环境与脚本组织 ★ FEATURED ARTICLE
简介面向CTF竞赛选手与安全初学者这份战队内部专用工具整合包集中解决比赛中高频出现的编码解码、进制转换、维吉尼亚密码暴力破解、USB流量识别、曼彻斯特解码、CRC32爆破与zip伪加密破解等需求适合赛前快速搭建本地工具环境。压缩包内共92个文件涵盖23份Java语言源码、16个可直接调用的jar工具包、13个sample样例、2个pcap流量包以及fxml界面、图片素材与Git仓库元数据整体大小为46.24MB目录中保留完整工程结构。内置Stegsolve、JPK、zip4j等常用组件覆盖隐写分析、古典密码还原、USB键盘流量解析与压缩包伪加密绕过同时提供多个sample样例和pcap流量包便于按题型分段练习与验证。从编码转换到暴力破解均有对应模块特别是维吉尼亚密码与CRC32校验的爆破入口对明文未知或校验值碰撞类题目非常实用样例图片与fxml文件也能帮助理解工具界面和运行方式。已有1947人学习下载适合希望系统梳理CTF常见工具、快速应对密码、流量与压缩包题型的选手作为参考工具集收藏。1. “某战队内部专用CTF万能工具箱.zip”到底封装了什么打开任何一个CTF交流群隔三差五就有人甩一个“某战队内部专用CTF万能工具箱.zip从未外漏”的压缩包出来。文件名很唬人解压以后要么是几个G的“全家桶”要么是一堆散落的脚本、字典和Docker镜像。追到根上它不过是一个战队把平时比赛用到的东西——解题脚本、环境模板、字典、常用工具——按自己的习惯打包到了一起。刚入门的选手拿到它容易当黑匣子双击运行里面密密麻麻的命令行根本不知道从哪下手。老手拿到它看的是目录结构和脚本思路因为这些东西每个队都能自己攒一套。这篇文章不打算假装弄到了那个zip而是从“战队内部为什么需要这样一个工具箱”出发带你把一套同等级别的CTF解题环境从零搭出来。你会看到一个CTF工具箱按什么逻辑分区才顺手、核心脚本怎么组织、Docker和参数要怎么调、以及那些让一个箱子在比赛现场翻车的坑。适合两类人想系统入门CTF但每次装环境装到怀疑人生的新手和带队伍想统一工具链、减少重复劳动的队长。2. 从zip结构反推战队的解题工作流工具箱按什么逻辑组装2.1 一个可复现的目录骨架按题目类型分区而不是按工具名分区我研究过不少战队的工具箱包括网上能找到的图吧工具箱这类硬件检测工具集和各类“CTF合集包”它们的通病是按工具名堆目录比如tools/wireshark、tools/burp。这种排法对自己熟悉电脑的人没门槛但换一台电脑、换一个人就乱了。真正的战队内部包几乎都按题目类型分区。一个典型的骨架长这样ctf-toolkit/ ├── bin/ # 统一入口脚本 │ ├── toolbox.py │ └── selfcheck.sh ├── env/ # 环境模板 │ ├── web/ # docker-compose.yml 等 │ ├── pwn/ │ └── forensic/ ├── scripts/ # 解题脚本按题型细分 │ ├── misc/ │ ├── crypto/ │ ├── reverse/ │ └── web/ ├── dicts/ # 字典与规则 │ ├── passwords/ │ └── wordlists/ ├── resources/ # 固定资源ida 插件、ghidra 脚本 └── README.md按题型分区的逻辑很简单CTF比赛里一个选手先判断“这是什么题”然后才决定用什么工具。misc、crypto、reverse、web、pwn是比赛里固定的五大方向目录跟着题型走人脑的切换成本最低。以ctf杂项题为例解包、流量分析、文件恢复都会在这一类里反复用到把这些脚本和工具集中放在scripts/misc/下找起来就是一次cd的事。新手一旦熟悉这个结构遇到任何题目都能快速定位到对应的工具链。2.2 命令入口与最小化配置把“查版本、起服务”变成一条命令一个工具箱最容易被低估的部分是入口。很多“万能工具箱”解压后要在十几个目录里来回翻找一个工具要先读半天的README这其实是最劝退的体验。战队内部包通常会提供一个bin/toolbox.py这样的统一入口把高频操作收敛成子命令。选手拿到箱子后只需要一条命令python3 toolbox.py info这行命令打印当前环境里所有关键工具的版本和可用状态。再比如比赛现场经常要快速起一个Web服务来复现题目或者要开一个特定版本的PHP环境直接python3 toolbox.py env up --typeweb原理很简单用Python的argparse把子命令梳理出来内部再去调对应的脚本和docker-compose。好处是新人不用懂环境细节照着一个usage就能干活老手也可以绕过入口直接进目录里调底层工具。这条命令看起来不起眼但它决定了这个箱子是“工具合集”还是“能用的工具箱”。我一般还会在入口里加一个selfcheck子命令它会去检查关键依赖python版本、docker是否在运行、常用命令如tshark、binwalk、gdb是否在PATH里。检查结果直接用表格打出来绿勾红叉一目了然。这样在比赛前夜跑一次就能把缺失的依赖提前暴露出来而不是等到开赛了才在现场装环境。2.3 为什么战队工具箱偏爱Docker而非直接装裸机如果你只在本地自己玩裸机装工具没问题。但一个要传给队友、甚至在比赛服务器上复现的工具箱最怕的就是“在我电脑上是好的”。Docker在这个场景下几乎是唯一解镜像本身就是环境快照里面Python版本、依赖库、服务配置全部锁定任何人拿到镜像都能得到一模一样的运行环境。以Web方向为例题目经常是“PHP 7.0 Nginx 某个特定扩展”这种组合。裸机环境往往已经是PHP 8.x直接跑旧题直接报错。而用Docker一个docker-compose.yml就把服务编排起来起停都是一条命令的事。更重要的是隔离pwn题要调试的二进制、恶意样本都应该在容器里跑避免污染宿主机的系统库。后面第3章会给出具体的compose模板和参数说明。3. 自建一个够用的CTF工具箱落地脚本与参数设置3.1 用一个Python脚本做统一入口工具箱的核心不一定要多复杂关键是入口要稳。我习惯把toolbox.py写成纯标准库实现不依赖第三方包这样在任何一台机器上clone下来就能跑。下面是一个最小可用的骨架#!/usr/bin/env python3 CTF Toolkit 统一入口只做三件事——查环境、起服务、跑脚本。 import argparse import subprocess import sys from pathlib import Path ROOT Path(__file__).resolve().parent.parent def cmd_info(args): 打印关键工具的版本状态。 tools { python3: [--version], docker: [--version], tshark: [--version], binwalk: [--help], gdb: [--version], } for name, args_list in tools.items(): try: out subprocess.run( [name] args_list, capture_outputTrue, textTrue, timeout5 ) version (out.stdout or out.stderr).split(\n)[0].strip() print(f[OK] {name:10} {version}) except (FileNotFoundError, subprocess.TimeoutExpired): print(f[MISS] {name:10} 未安装或不在 PATH 中) def cmd_env(args): 进入 env 目录执行 docker-compose 操作。 env_dir ROOT / env / args.type if not env_dir.exists(): sys.exit(f未找到环境模板{env_dir}) subprocess.run( [docker-compose, args.action, -d] if args.action up else [docker-compose, args.action], cwdenv_dir, checkTrue, ) def cmd_solve(args): 按题型调用 scripts 下的解题脚本。 script ROOT / scripts / args.category / args.name if not script.exists(): sys.exit(f脚本不存在{script}) subprocess.run([sys.executable, str(script)] args.args, checkTrue) def main(): parser argparse.ArgumentParser(progtoolbox, descriptionCTF 工具箱入口) sub parser.add_subparsers(destcommand, requiredTrue) sub.add_parser(info, help检查环境) p_env sub.add_parser(env, help管理 docker 环境) p_env.add_argument(action, choices[up, down, ps]) p_env.add_argument(--type, requiredTrue, help环境类型web/pwn/forensic) p_solve sub.add_parser(solve, help运行解题脚本) p_solve.add_argument(category, help题型目录如 misc/crypto) p_solve.add_argument(name, help脚本文件名) p_solve.add_argument(args, nargs*, help传给脚本的参数) args parser.parse_args() if args.command info: cmd_info(args) elif args.command env: cmd_env(args) elif args.command solve: cmd_solve(args) if __name__ __main__: main()入口脚本的逻辑分三层解析参数、路由到具体函数、在函数里调用底层命令。ROOT用Path(__file__).resolve().parent.parent来定位工具箱根目录这样不管从哪里调用脚本都能找到自己的家目录不会因为当前工作目录不同而找不到文件。参数设计上有几个可以被新手的点。subparsers的requiredTrue要求必须输入子命令避免用户直接敲python toolbox.py得到一个空提示timeout5是给cmd_info里的探测命令设的防止某个工具卡死把整个入口拖住docker-compose up后面拼接-d让服务在后台运行终端不会被日志刷屏。如果哪天要支持pwn这类需要交互的环境可以在cmd_env里再扩展参数把-it传进去入口本身不用大改。3.2 为Web题准备的环境模板Nginx PHP MySQL 的 comfy 组合Web方向是CTF里出题数量最多、环境依赖最杂的方向。一个稳定的env/web/docker-compose.yml应该同时覆盖源码泄露、php弱类型、命令执行、phar反序列化这些高频考点。我这里给一个模板version: 3.8 services: nginx: image: nginx:1.22-alpine ports: - 8080:80 volumes: - ./html:/usr/share/nginx/html:ro - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - php php: image: php:7.4-fpm volumes: - ./html:/var/www/html environment: - FLAGflag{test_flag_do_not_use} db: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORDroot - MYSQL_DATABASEctf ports: - 3306:3306这个模板的思路和裸机装环境完全不同。Nginx只负责静态文件和反向代理把.php请求转给PHP-FPM容器PHP容器挂载同一个html目录这样改代码和配置不需要重新构建镜像MySQL单独成一个服务题目里的SQL注入考点就有了完整的数据库后端。三个容器彼此通过depends_on控制启动顺序volume做数据共享端口映射只把真正需要访问的口暴露到宿主机。参数选择的考量再展开一点。PHP镜像锁定7.4不是随便选的phpwrapper和php://filter相关的经典题目在PHP 8.0以上版本里很多行为变了用7.4能最大程度复现比赛常见环境。MySQL用5.7而非8.x是因为很多旧题里WHERE 11、联合注入的返回结构依赖5.7的默认排序和报错信息。FLAG通过环境变量注入比写死在文件里更安全也能模拟在线比赛的环境变量读取方式。如果你日常编程用的是新版本注意在容器里跑题目时别让本地的PHP版本干扰判断一切以容器内版本为准。3.3 把解题脚本收进scripts目录并统一调用环境只是工具箱的地基真正体现“万能”的是积累下来的解题脚本。ctf逆向和ctf密码学方向的选手各有自己的一堆脚本但如果没有组织很快会变成一坨无人能懂的“祖传代码”。我的习惯是每个脚本必须能在命令行直接跑输入输出都走标准流不弹交互窗口然后通过toolbox.py solve统一调用。python3 toolbox.py solve crypto factor -- -n 1234567890 -e 65537这条命令会调用scripts/crypto/factor.py把-n和-e透传给脚本做RSA分解尝试。透传的逻辑在cmd_solve里已经写了用args.args把剩余的未知参数原样传给子脚本这样子脚本自己用argparse或者sys.argv就能继续解析。统一入口带来的直接好处脚本的调用方式标准化了每个人写的脚本都长得一样新人看几条就能举一反三。脚本本身也建议遵循同样的输入输出规范。比如一个流量分析的脚本输入是pcap文件路径输出是把HTTP请求里的可疑字符串、上传的文件内容提取到当前目录。不要做成“脚本内部写死文件名”否则换个题目就得改代码。更省事的做法是在scripts/每个子目录放一个README.md写清楚每个脚本的输入输出和依赖比事后看代码猜意图省太多时间。4. 真实题目场景下的“万能”组装按题型配参数4.1 Web题从源码泄露到命令执行工具链怎么串Web题是最考验“临场拼工具”能力的。以命令执行为例一个典型的流程是先找到注入点再确认过滤规则然后构造payload。很多新手拿到一个?cmdid的题目第一反应就是手搓编码但效率太低。工具箱里应该准备的是一个“编码-尝试-回显”一体的小脚本比如scripts/web/cmd_payload.py它能根据目标过滤情况自动生成空格变体、关键字替代、php://filter配合phpwrapper读取源码的payload再逐个请求测试。参数习惯上我通常会这样组织参数取值示例说明--urlhttp://target/?cmd注入点完整前缀--filterspace已确认被过滤的字符/关键字--enginephp/bash/python目标后端的解析器--methodGET/POST请求方式涉及参数位置--proxy127.0.0.1:8080挂代理看原始流量排错用这个参数表对应的是一个“半自动”工具它不负责帮你判断注入点而是在你确认注入点后把编码、拼接、请求、回显这几步机械化。很多老手翻车就翻在只盯着--url和--filter忽略了--engine导致bash的$IFS拼到了PHP的payload里白折腾半小时。另外Web题里的.zip源码包泄露也值得留意用binwalk或者grep -r flag{扫一遍下载下来的备份文件往往比死磕注入点更快。4.2 流量与取证题pcap、磁盘镜像、内存分析的参数习惯ctf杂项Misc里流量分析和取证是重头戏。拿到一个capture.pcap最常见的处理链路是先看整体协议分布再定位可疑流最后提取文件或字符串。这个链路对应到命令上有固定的参数习惯# 1. 看协议占比和异常长度包 tshark -r capture.pcap -q -z io,phs -z io,stat,ip # 2. 过滤HTTP请求提取URI和UA tshark -r capture.pcap -Y http.request -T fields -e http.host -e http.uri -e http.user_agent # 3. 导出HTTP对象包括隐藏的文件 tshark -r capture.pcap --export-objects http,exported-q是安静模式只输出统计结果不刷每个包-z io,phs按协议分层统计能一眼看到DNS、TCP、HTTP各占多少-Y是显示过滤只保留HTTP请求包--export-objects把HTTP传输过的文件全部导出很多题目把flag藏在图片或压缩包里。这三个命令组合起来能在1分钟内判断这个pcap是不是“流量题”以及大概的藏法深度。取证题里磁盘镜像和内存镜像又是另一个套路。磁盘镜像优先用binwalk做文件提取再用strings配合grep找线索内存镜像涉及进程和网络连接的状态就得用Volatility的特定profile参数。这里最容易踩的坑是版本和profile不匹配Volatility 2和Volatility 3的参数风格完全不同--profile如果指定错了直接报错或者输出一片乱码。我的建议是在scripts/misc/下放几个包装脚本把常见的“Win7 x64”“Ubuntu 16.04”等profile写成参数预设而不是让选手现场去回忆命令。4.3 密码学题从古典密码到现代加密的快速识别路径ctf密码学题目看着唬人但解起来很讲究“识别优先”。拿到一串密文别急着跑脚本先判断它是什么类型。我给你一个按特征筛选的路径长度很短且只有字母的可能是凯撒或维吉尼亚出现大量-/和等号的优先想Base64以i、j、k开头的URL安全字符可能是Base64的变体十六进制串丢给xxd -r转回原始字节再看首字节是位数如果是PK开头那就是zip文件可能要结合压缩包伪加密处理RSA题目给的n、e、c是十进制大数先用factor.py试小因子分解分解不动再考虑yafu跑Fermat或Pollard rho。工具箱在crypto方向最值得沉淀的不是某个加密算法脚本而是“识别-尝试-出参”这一步。我写过一个小工具scripts/crypto/identify.py它接受任意文本输出几行猜测python3 toolbox.py solve crypto identify --input U2FsdGVkX1...这个脚本做的事情不复杂统计字符集、计算熵值、检查常见编码前缀、打印最可能的前三种类型。它不会帮你直接解出flag但能把你从“看到密文无从下手”的卡壳里拉出来。很多入门选手花几小时死磕一个维吉尼亚其实那只是个套了两层Base64的明文亏就亏在第一步没识别出来。5. 避坑箱子越大越容易翻车——CTF工具箱的常见问题与排查5.1 Docker镜像拉取慢或失败现象在比赛现场执行docker-compose up卡在拉取镜像那一步或者报dial tcp: lookup超时。越到赛前越想快点起环境越容易卡在这一步。原因多半是默认镜像源在高峰期不稳定或者本机之前配置过的源已失效。解决提前在/etc/docker/daemon.json里配置可用的镜像加速地址同时对所有镜像在入库时锁定具体tag不要用latest否则今天和明天拉下来的环境可能不同跑了半个月的复现脚本一次升级后就失效了。5.2 脚本里Python2和Python3混用现象跑一个老牌misc脚本报SyntaxError: print syntax invalid或者ImportError: no module named SocketServer。原因这个脚本是Python 2时代写的你的工具箱默认解释器是Python 3。解决在toolbox.py的cmd_solve里加一个判活逻辑按脚本第一行注释比如#!/usr/bin/env python2选择解释器。更省心的做法是给这类脚本套一层容器把Python 2.7的环境做成镜像而不是在宿主机里硬装一个python2命令否则pip依赖会互相污染。5.3 字典和脚本更新后旧版本被覆盖现象某个wordlist昨天还能跑今天再调就少了一部分或者一个reverse脚本改完后历史题解不出来了。原因工具箱目录本身就是个git仓库但大家习惯直接覆盖文件没有提交历史意识。解决把dicts/、scripts/、env/纳入git管理每次变更必须提交commit message写清楚“改了哪个题型的哪个脚本”。比赛现场如果发现某个脚本坏了git log -p直接看最近改动一条git checkout就能把后悔药吃到。没有git的工具箱时间一长就是一堆来历不明的黑匣子。5.4 现场才发现某工具缺失现象解一道pwn题需要checksec结果命令not found或者一个流量镜像需要老版本的foremost现场装又遇到编译依赖。原因装箱和验箱是两回事复制了目录不代表环境完整。解决把selfcheck.sh跑一遍纳入赛前固定流程。这个脚本检查的不只是命令存在还要检查版本号是否匹配比如binwalk --help第一行如果显示2.x而题目预期是1.x就该提前想好替代方案或更新脚本。比赛前夜跑一次花3分钟省得开赛后手忙脚乱。5.5 容器内服务端口与宿主机冲突现象Web题环境起在8080端口本地正好有个开发服务占用了8080docker-compose up直接报port is already allocated。原因本地开发环境杂乱固定端口被占用。解决在env/web/.env文件里用变量管理端口映射比如WEB_PORT8081compose文件里写成${WEB_PORT}:80启动前先看看占不占。另外MySQL的3306也是高频冲突端口建议全部改成映射到5xxxx段高位端口减小和本地数据库撞车的概率。6. 把工具箱变成“训练场”历史题目回放做验证工具箱搭建完第一件该做的事不是继续加工具而是用往届题做一次完整的回放演练。挑5道分别覆盖web、misc、crypto、reverse方向的旧题从解压题目、起环境、调脚本到拿下flag全程只用这个箱子里的东西。哪一步卡住了就说明箱子的哪个环节有缺口补上再跑。这个验证过程能让工具箱从“装了一堆软件”变成“真正能出活的工作台”。我有一次带着刚攒的箱子去线下赛结果第一道misc题要求解一个LFSR序列箱子里的脚本跑出来的结果跟预期对不上现场翻代码才发现是参数位数写死了。从那以后我每收一个脚本进箱子都会顺手用历史题跑一遍输入输出规范、参数边界一次定清楚。版本锁定是第二件值得做的事。env目录下的每个镜像tag要固定requirements.txt里每个Python依赖要精确到版本不要用。工具箱是消耗品今天能用不代表半年后还能用系统的包管理器一升级很多老题的环境就崩了。合理的做法是每年比赛季开始前把这个箱子整体构建一次更新基础镜像和脚本兼容性而不是到了现场才发现问题。我会把这件事记在备忘里每次赛季初花半天时间跑一遍全量验证。最后给这套箱子写一份“面向新人”的README。把你的toolbox.py info输出截图放进去再把每个子命令的用法列成表格写清楚“什么场景用什么命令”。别高估队友的动手能力也别低估一套清晰文档的价值。我见过太多箱子因为缺文档最终变成队长一个人的玩具其他队员宁可自己上网现搜脚本也不愿意用。工具箱只有被全队用起来才算真正完成。如果你也从零开始攒这个箱子我的建议是不要贪多先把env和scripts两个目录的骨架立住再逐步往里填东西。这活儿没有捷径但确实值得做因为比赛现场的时间永远应该花在解题上而不是花在装环境上。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站