简介:面向CTF网络安全大赛的参赛者、安全研究者和高校网安学生,这份工具包围绕逆向工程、Web安全、密码学与二进制利用等常见考查方向整理,将比赛所需的软件工具、脚本插件与参考文档统一归集,方便按需选用。整包包含23183个文件,压缩后约869.22MB,其中class文件达到20336个,是Java相关工具链与库组件的主体;同时配有exe、dll、jar、py等可执行文件与脚本,以及大量html、php、asp、asm等源码和配置,另有png、gif、jpg图示和txt、pdf说明,覆盖面相当完整。从预览可看到内附汇编宏插件、MASM插件、API接口定义及ASP脚本等内容,能够在Windows环境中直接支撑逆向调试和Web类题目分析,文本与示例文件也为工具环境搭建、参数配置和排错提供了必要参考。当前已有9314人学习下载,目录按工具类型与用途分类,可大幅节省检索与配置时间,尤其适合需要集中备赛、快速搭建可用工具链的CTF选手与安全工程师。
1. CTF工具包不是越多越好:这是一场环境碾压战的起点
在CTF网络安全大赛里,我第一次拿到装满几十个工具的自制工具包时,心里只有一个感觉:稳了。结果那场比赛我一题没解出来,原因是工具版本冲突,某个依赖库的更新直接把隐写分析脚本打崩,我花了四十分钟修环境,最后眼睁睁看着别的队伍提交flag。从那天起我才明白,CTF工具包真正决定胜负的不是工具数量,而是可复现、可快速切换、参数顺手。一个朝令夕改的工具包,比赛时就是一颗定时炸弹。CTF网络安全大赛工具包集合,本质上是把逆向、密码学、Web渗透、取证、杂项等方向的高频工具按规则收拢成一套能在最短时间内启动、查错并出结果的方案。这篇文章会从环境搭建、方向选型、实战流程到踩坑复盘,讲清楚一套真正能上场的工具包该怎么建。适合那些已经认识几个工具但始终觉得比赛时手忙脚乱的选手,也适合想系统整理自己工具库的从业者。
2. 搭建可复现的CTF工具箱:Docker镜像与依赖锁定
2.1 用Docker一次性拉起常用工具的镜像
常见的做法是直接把工具包打进Docker镜像,比赛前拉下来就能用。我一般会维护一个基础镜像,里面装好Python 3和常用的CTF必备库,再按方向拆成多个子镜像。这样Pwn和密码学之间不会互相污染依赖,也不会出现为了装某个老版本gdb把系统库搞坏的情况。
# 基础镜像:只装Python环境和基础库 FROM kalilinux/kali-rolling RUN apt-get update && apt-get install -y \ python3 python3-pip git vim netcat-openbsd \ binutils file foremost binwalk \ && pip3 install --no-cache-dir \ pwntools pycryptodome gmpy2 z3-solver \ ropgadget one_gadget \ && rm -rf /var/lib/apt/lists/*这段Dockerfile里有几个值得注意的参数:--no-cache-dir让pip不缓存安装包,镜像能小一些;rm -rf /var/lib/apt/lists/*用在同一个RUN里,是为了干掉apt的索引文件,减少一层体积。netcat-openbsd是很多Pwn题本地调试刚需,foremost和binwalk则是Misc和取证方向高频工具。这里没有选择一次性把所有工具全装进一个镜像,因为你做题时会发现,某个工具只在一个方向里需要,而它的依赖可能和另一个方向冲突。
拉镜像和进容器的命令也要固定下来,否则每次敲都会忘。我习惯用下面这套别名。
alias ctf-docker='docker run -it --rm \ -v "$PWD":/work -w /work \ --network host \ myctf:latest /bin/bash'-v "$PWD":/work把当前目录挂载进容器,这样题目文件可以直接访问,不用复制来复制去。--network host在Linux下直接复用宿主机网络,Pwn题的localhost连接就不会有NAT映射问题。--rm确保退出后容器自动删除,不会在机器上堆一堆容器浪费磁盘。
2.2 本机必备的五类基础工具
Docker解决依赖隔离,但有些场景必须在本机装原生工具,尤其是USB外设、GUI图形化调试这类没法在容器里顺畅跑的东西。我会在本机保留五类工具,每一类有明确用途。
第一类是十六进制编辑与文件识别,hexdump、xxd、file属于这类,胜负手往往是一个字节的差异。第二类是网络抓包与代理,tcpdump和Burp Suite,一个处理原始流量,一个处理HTTP应用层。第三类是调试器与反汇编,gdb配合pwndbg插件,或者radare2,都能在Pwn题里直接看栈布局。第四类是密码学计算,openssl和Python的Crypto库覆盖了大数运算、编码转换、哈希计算。第五类是隐写与文件提取,strings、binwalk、foremoststeghide这条线几乎能应付八成Misc题。
这些工具不需要全都装最新版。比如有些题的题目环境基于较老的内核,新版binwalk默认会尝试提取熵较高的文件,反而容易把正常文件误判成压缩包。我会固定几个常用版本,不是特殊需求绝不升级。这不是保守,是比赛时稳定比新功能更重要。
2.3 锁定工具版本:requirements.txt与你自己的靶场环境
工具包的依赖锁定期望达到一个目标:任何时间、任何机器上,运行同样命令得到同样结果。Python方向的依赖用requirements.txt锁版本,系统级工具则在Dockerfile里固定镜像标签,不使用latest。
pip freeze > requirements.txt但这行命令有个坑,它会把你机器上所有Python包都导出来,包括不需要的。更精确的做法是只导出当前虚拟环境里的依赖,或者在Docker容器里执行后拷贝出来。我一般用pipreqs按项目目录扫描,但CTF场景下更实用的是在Dockerfile里手动写明工具版本。
pwntools==4.9.0 pycryptodome==3.19.1 gmpy2==2.1.5 z3-solver==4.12.2.0版本号本身不必追求最新,有些老Pwn题的环境用的还是Python 3.7,pwntools新版在某些结构体解析上会有差异。我的习惯是准备两套镜像:一套偏最新,用于新题;另一套固定在某几个稳定版本,用于复现往年经典题。两套镜像的Dockerfile单独放,标注清楚用途,避免比赛时拿错镜像浪费十几分钟。
3. 按赛题方向选对工具与参数:Pwn、密码学与Web
3.1 Pwn方向:pwntools与gdb的常用参数
Pwn方向最核心的组合是pwntools加gdb。pwntools负责交互、构造payload和计算偏移,gdb负责在崩溃点看寄存器与栈。第一次用pwntools的人最容易犯的错是直接send而不看回显,导致脚本卡在某个交互环节。我写利用脚本时会习惯性地先开一个日志级别。
from pwn import * context.log_level = 'debug' context.arch = 'amd64' context.terminal = ['tmux', 'splitw', '-h']log_level='debug'会把发送和接收的字节全部打印出来,你能直观看到当前交互到了哪一步。terminal数组让gdb.debug()在tmux里自动分屏,而不是另开一个窗口。实际调试时,我通常用gdb.attach()附加到单个运行进程上。
gdb ./pwn set follow-fork-mode childfollow-fork-mode child是遇到fork出的子进程时要设的,不然gdb还在跟父进程,Pwn题的交互逻辑经常在子进程里跑。另一个高频参数是禁用ASLR,本地调试时我一般配合setarch命令一起用,确保每次运行的地址一致。
setarch $(uname -m) -R ./pwn-R参数表示关闭地址随机化。本地调试通过后,再回到正常环境测试脚本,因为远程服务器上ASLR是开启的,很多利用思路在关闭状态下成立,开启状态就失效了。
3.2 密码学方向:从openssl到哈希爆破的选型
密码学题目的工具选型要看具体算法。编码类首选CyberChef,但我更常用命令行工具组合,因为比赛时GUI操作容易浪费时间。openssl几乎覆盖了所有常见加解密算法,拿到的文件要先判断是Base64还是Hex,再决定是否用openssl。
cat cipher.txt | base64 -d > decoded.bin openssl enc -d -aes-256-cbc -in decoded.bin -out flag.txt -pass pass:secretopenssl enc -d的-d表示解密,-aes-256-cbc指定算法,-pass pass:secret直接传口令。这里有个坑:如果文件是二进制,用cat直接看会乱码,必须先做格式判断。哈希爆破方向,hashcat是主力,但CPU跑不快时我会先用John或纯Python脚本验证。
hashcat -m 0 -a 3 hash.txt '?d?d?d?d?d?d' --force-m 0是MD5类型,-a 3表示暴力破解,?d?d?d?d?d?d是六位数字掩码。--force是为了绕过一些平台检测,但正式使用要确认机器支持。很多密码学题目的瓶颈不是工具跑不快,而是没有识别出算法类型。我拿到密文第一件事是看长度和字符集,长度32通常是MD5,64通常是SHA-256,再结合题目描述判断是否需要拼接盐。
3.3 Web方向:Burp Suite代理与字典的配合
Web方向工具包的核心是Burp Suite,代理抓包、改包、重放,一套流程可以覆盖大部分题目。新装Burp时最需要调的是代理监听地址,默认是127.0.0.1:8080,但如果你在Docker容器里跑Burp,宿主机的浏览器访问容器IP时会连不上。
bp_proxy --host 0.0.0.0 --port 8080不过Burp本身是GUI工具,命令行启动方式按版本不同有差异,我更推荐在宿主机跑Burp,再用Docker跑题目环境,两者网络隔离开,只用HTTP代理互通。字典选择是Web题里最容易忽略的参数,爆破登录或目录时,字典太小扫不出东西,太大又会浪费时间。我会准备三本字典:常见用户名加密码组合、目录爆破专用、参数名推断专用。
ffuf -u http://target/FUZZ -w dirs.txt -mc 200,301,302-mc 200,301,302表示只显示这些状态码,过滤掉大量404,让结果可读。-w指定字典,FUZZ是占位符,ffuf会逐个替换。这里的关键是字典不能从网上随便下载一个几十万行的,大部分无效条目会拖慢速度。我维护字典时会把题目博客里出现过的路径和参数名补进去,这种针对性往往比海量字典有效得多。
4. 用工具包解一道Misc杂项题:完整排查流程
4.1 从file命令开始确定文件类型
拿到未知文件时,第一个命令是file,而不是急着用hexdump或者strings。file会根据文件头判断真实类型,而扩展名是可以随意改的。有个典型场景:题目给了一个flag.png,用file一看实际是Zip压缩包,直接改个后缀就能解出内容。
file flag.png # 输出:flag.png: Zip archive data, at least v2.0 to extract看到这种结果,直接用mv flag.png flag.zip && unzip flag.zip。这个操作简单,但很多人会跳过file直接去看图片,浪费了时间。file的输出还能告诉你文件的大致结构,比如有没有附加数据、是不是ELF、是不是加密容器。这些信息决定了后续用哪一类工具。
4.2 用binwalk与foremost抽取隐藏文件
如果file显示是一个看起来正常的图片或压缩包,但题目显然藏着东西,就需要用binwalk扫描文件里是否存在嵌套文件。binwalk会根据文件头特征去匹配,比如能在图片中识别出另一个Zip或PNG的起始位置。
binwalk suspect.bin DECIMAL HEXADECIMAL DESCRIPTION 0 0x0 PNG image, 800 x 600 30456 0x7700 Zip archive data这里30456偏移处出现了Zip头,说明图片尾部被拼上了一个压缩包。下一步用dd从偏移处把Zip提取出来,或者直接用foremost自动按类型抽取。foremost更适合需要提取多个文件的场景,它会自动识别多种文件头,并把提取结果放到output/目录。
foremost -t zip,png,jpg -i suspect.bin -o output-t指定要提取的文件类型,-i是输入文件,-o是输出目录。如果只写-t zip,可能会漏掉某些变体;通常我会把题目类型相关的格式都写上,提取后统一检查。
4.3 结合strings和hexdump定位真实线索
抽出来的文件如果还是没头绪,就要回到最原始的strings和hexdump。strings会打印文件中所有可打印字符串,很多题目会把flag藏在ASCII文本里,甚至直接用echo往文件尾部追加了一行。
strings suspect.bin | grep -iE 'flag|key|ctf'-iE表示忽略大小写且启用正则。如果grep没有结果,再看文件的尾部有没有大段可打印字符,以及用hexdump -C查看文件头后紧接的十六进制是否异常。hexdump -C按十六进制和ASCII两栏显示,能快速定位文件头是不是被篡改过。
hexdump -C flag.png | head -20head -20只看前20行,大多数图片文件头是89 50 4E 47,如果看到别的字节,说明文件不是标准PNG,可能需要修复文件头。这个排查流程的核心是每个步骤都要有明确的结果判断,不要无脑跑完所有工具,而是跑一步看一步,根据输出决定下一步动作。工具包的真正价值就在于你能熟练地在这些工具之间切换,而不是会某个工具的花哨参数。
5. 工具包使用避坑指南:四个常见翻车点
5.1 现象:pwntools脚本连不上本地服务
比赛现场最让我崩溃的场景是脚本在本机能跑,队友的机器上却报EOFError或Connection refused。有一次某个Pwn题本地服务监听在0.0.0.0:9999,但我在容器里运行脚本时用localhost连接,容器内访问宿主机的localhost指向容器自己,自然连不上。
原因有两类:一是容器网络隔离,二是服务启动时监听的地址不匹配。解决方法是先确认服务监听地址。
ss -tlnp | grep 9999看到127.0.0.1:9999就说明服务只回环监听,可以用--network host启动容器,或者把题目服务换成0.0.0.0。如果服务本身没问题,还要检查脚本里socket连接的目标地址,是否写成了远程靶机地址而不是本地。
5.2 现象:隐写工具解出但打开报错
用binwalk提取出一个ZIP,unzip却提示invalid compressed data。我遇到过几次这种题,原因是文件被故意追加了垃圾数据,或者标记了加密位但密码是空的。unzip要求文件末尾的中央目录记录正确,追加垃圾字节会破坏它。
解决思路是先复制一份干净文件,再尝试zip -FF修复。
cp extracted.zip fix.zip zip -FF fix.zip --out final.zip如果还不行,就检查binwalk -e输出的原始偏移,用dd按偏移精确截取。这里要养成的习惯是保留原始文件,不在原文件上做修改,因为每一步提取都可能破坏后续线索。比赛里对文件做备份是最便宜的后悔药,拷贝一份原文件只要几秒钟,重做一步却可能要半小时。
5.3 现象:HTTPS证书导致流量捕获不全
抓取HTTPS流量时,Burp提示证书不受信,浏览器直接拦截,导致后续请求全部失败。原因通常是没安装Burp的CA证书,或者安装后没有启用代理的证书校验。
解决方法是先导出CA证书,再在浏览器或系统里安装并信任。具体到工具包,我会把CA证书放在固定目录,比赛前专门检查一次。另一个常见问题是目标站点使用了证书固定,即使安装了CA也会校验失败,这时候就需要用mitmproxy配合ssl-inject之类的方式,或者回到tcpdump抓原始流量再离线分析。比赛时不要死磕一个工具,抓不到明文就换个角度。
5.4 现象:工具包移植到新电脑后命令找不到
把Docker镜像导出到另一台电脑加载后,发现有些命令不存在,最典型的是foremost或gdb。原因是我制作的镜像里有些层被清理掉了,或者导出时用了--squash参数把多个层合并,导致某些文件路径被覆盖。
解决方法是每次导出镜像后做一次冒烟测试。
docker run --rm myctf:test sh -c "which gdb && which binwalk && which foremost"如果输出缺少某个命令,说明镜像构建阶段漏装了或安装失败。我习惯把Dockerfile写在代码仓库里,每次改工具都走一次构建流程,而不是直接进入容器装完再commit。用commit生成的镜像是黑匣子,只有构建日志能告诉你每一步干了什么。
6. 把工具包变成自己的:自动化识别脚本与利用模板
6.1 批量识别题目文件的类型与特征
比赛一开始会下发多个文件,逐个执行file太浪费时间。我会写一个小脚本,批量收集文件类型、大小、以及是否有可疑的字符串特征,一次性输出一个总览。
#!/bin/bash for f in "$@"; do echo "=== $f ===" file "$f" strings "$f" | grep -oE 'flag\{[^}]+\}' | head -3 binwalk "$f" | tail -2 done这个脚本不到十行,但在比赛开始的前几分钟能给我一个全局视野,哪些文件是常规格式,哪些文件里面暗藏flag格式的字符串。grep -oE 'flag\{[^}]+\}'会提取flag格式内容,head -3只显示前三个候选,防止大量误报。我把这个脚本放在工具包的根目录,每次新题下发都会先跑一遍。
6.2 为自己的比赛习惯写pwntools模板
Pwn题的利用脚本结构相似,我会固定一个模板,避免每次重新写交互框架。
from pwn import * context.arch = 'amd64' context.log_level = 'info' def start(): if args.GDB: return gdb.debug(binary, gdbscript='b *main') elif args.REMOTE: return remote('target', 1337) else: return process(binary) io = start() io.recvuntil(b'> ') io.sendline(b'%33$p') io.interactive()args.GDB和args.REMOTE是pwntools内置的动态参数,命令行加GDB=1或REMOTE=1就能切换模式,不用改代码。recvuntil指定了接收分隔符,避免只靠recvline()导致无法对齐交互状态。这个模板我会根据不同题目微调,但总体框架不变,能省下大量重复打字时间。
6.3 定期更新与回滚的固定节奏
工具包不能永远停在某个版本,但也不能随用随更。我给自己定的节奏是每个月整理一次,把新的题解脚本和工具参数记录到笔记里,然后更新Docker镜像并打上新的tag。每次更新后做一次回归测试,拿几道经典老题跑一遍,确认没有破坏旧的方向。
更新时只改有明确收益的工具,比如某个插件解决了以前没法读的格式,而不是因为看到“新版本发布”就顺手升级。回滚路径要保持简单,旧镜像的tag不删除,只追加新的。这套固定节奏让我在比赛时敢放心使用工具包,不会担心某个工具因为更新而突然变成不可用状态。真正的工具包维护不是囤积,而是把每次比赛的经验沉淀成可复用的环境与参数,这也是我从连续翻车到稳定出题解的转折点。希望帮到你。
本文还有配套的精品资源,点击获取