简介:CTF线下AWD脚本合集是一份面向网络攻防竞赛选手的实战工具包,尤其适合刚接触AWD模式、不熟悉自编脚本的新手,以及希望提升攻防效率的进阶选手。AWD要求参赛队伍在攻击对手系统的同时保护自身服务,对脚本化操作依赖较高,该合集正是为节省编写与调试时间而整理。压缩包共34个文件,约3.18MB,以Python脚本、PHP木马与Webshell、TXT说明文档为主,辅以pyc编译文件、Markdown说明及少量rar、exe工具,覆盖扫描探测、自动化攻击、不死马与WAF防御、日志分析、Flag获取等环节,目录结构清晰,便于按攻防阶段快速取用。目前已有3010人学习下载,读者可借此熟悉常见漏洞利用流程、理解攻防对抗逻辑,并参考现成脚本优化自身战术,在合法CTF比赛环境中提升表现。
1. 从一次被打穿的 AWD 说起:这套脚本到底能救什么场
打过线下 AWD 的人大概都有过这种体验:开局十分钟,自己的靶机被人种了不死马,Web 目录被翻了个底朝天,而你还在手忙脚乱地敲命令找 flag。CTF 线下攻防(Attack With Defense)拼的从来不是单点技术,而是开局三十分钟内的自动化响应速度——谁能更快地批量改密、批量查杀、批量提交 flag,谁就能把分数稳住。这份CTF线下AWD脚本合集.zip就是冲着这个场景来的:它不是某一个漏洞的利用工具,而是一整套围绕 AWD 与 AWDPLUS 赛制整理的运维向脚本集合,覆盖批量 SSH 改密、Web 后门查杀、flag 自动提交、流量与进程监控这些高频动作。
它适合两类人:一类是刚接触 CTF 网络攻防、想搞明白线下赛到底在忙什么的新手,照着脚本能跑通一遍完整流程;另一类是打过几场但每次开局都靠手速硬扛的老选手,可以拿它当模板改造成自己队伍的响应框架。需要说清楚的是,脚本本身不产生分数,它解决的是「重复劳动拖慢响应」这个问题。下面我按实际拆包和复现的顺序,把这份合集里最值得用的几个模块讲透,包括参数怎么改、坑在哪。
2. 拆开压缩包先看结构:AWD 脚本的分类与选型逻辑
拿到一个脚本合集,最忌讳的就是上来chmod +x全跑一遍。AWD 环境里靶机是共享的,一个乱跑的脚本可能把队友的利用链一起干掉。所以第一步永远是先看清楚它由哪几类脚本组成,各自对应赛制里的哪个环节。
2.1 按赛制阶段给脚本分类
AWD 和 AWDPLUS 的节奏不一样,脚本的用途也不一样。常见的分类方式是按「开局—对抗—收尾」三个阶段来切:
| 阶段 | 典型脚本类型 | 主要作用 | 执行频率 |
|---|---|---|---|
| 开局加固 | 批量改密、SSH 密钥替换、权限收紧 | 抢在对手扫描前把默认口令换掉 | 每轮开局一次 |
| 持续对抗 | Web 后门查杀、进程监控、文件完整性校验 | 发现并清除对手种下的 webshell | 定时循环 |
| 得分动作 | flag 自动提交、flag 路径扫描 | 把拿到的 flag 及时换成分数 | 高频轮询 |
| 收尾复盘 | 日志收集、流量抓包归档 | 赛后分析对手打法 | 每轮结束 |
这份合集里的脚本基本能对上这张表。选型的时候有个原则:开局加固类脚本必须自己改过再用,因为默认密码、密钥路径这些参数一旦和你的靶机环境对不上,轻则无效,重则把自己锁在门外。查杀类和提交类脚本相对通用,但也要确认 flag 路径和提交接口。
2.2 判断一个脚本能不能直接用的三个检查点
在真正执行前,我一般会做三个检查,避免踩到合集里那些「作者环境能用、你环境翻车」的坑。
第一个检查点是硬编码路径。很多脚本会把 Web 根目录写死成/var/www/html,但实际靶机可能是/var/www/html/upload或者 nginx 的/usr/share/nginx/html。用grep -rn "/var/www" *.sh先扫一遍,把所有硬编码路径列出来。
第二个检查点是依赖命令是否存在。脚本里常见的inotifywait、sshpass、curl、python3并不是每台靶机都装了。可以先跑一条探测命令:
# 检查脚本常用依赖是否齐全,缺哪个补哪个 for cmd in sshpass inotifywait curl python3 crontab; do if command -v $cmd >/dev/null 2>&1; then echo "[OK] $cmd -> $(command -v $cmd)" else echo "[MISS] $cmd 未安装" fi done这段逻辑很直白:command -v返回命令路径就说明存在,否则标记缺失。参数上没什么可调的,重点是把输出结果记下来,缺sshpass就装sshpass,缺inotifywait就装inotify-tools。注意有些比赛靶机不允许联网装包,那就得提前把静态编译的二进制带进去。
第三个检查点是提交接口的地址和 token。flag 提交脚本里通常有一个 URL 和一个队伍 token,这两个值每个赛事都不一样,必须替换。如果脚本里写的是示例地址,直接跑只会得到一堆 404。
提示:拆包后先别急着执行,把每个
.sh和.py用head -50看一遍头部注释和变量定义,能省掉后面大量排错时间。
3. 批量改密与后门查杀:开局三十分钟的自动化脚本怎么写
开局阶段最值钱的就是时间。对手的扫描器不会等你,所以改密和查杀这两件事必须自动化。这一章把这两类脚本的核心逻辑拆开讲,给出可以直接抄的代码骨架。
3.1 批量 SSH 改密脚本的参数与边界
AWD 里靶机通常给你一个初始账号密码,所有队伍共用同一套镜像,意味着初始口令是公开的。开局第一件事就是批量改密。核心工具是sshpass配合ssh,逻辑是遍历主机列表,逐个执行passwd或直接改/etc/shadow。
#!/bin/bash # batch_chpasswd.sh - 批量修改靶机 SSH 密码 # 用法: ./batch_chpasswd.sh hosts.txt oldpass newpass HOSTS_FILE=$1 # 主机列表,每行一个 IP OLD_PASS=$2 # 初始密码 NEW_PASS=$3 # 新密码,建议每台不同 while read -r ip; do [ -z "$ip" ] && continue # -o StrictHostKeyChecking=no 跳过首次连接的指纹确认,避免卡住 sshpass -p "$OLD_PASS" ssh -o StrictHostKeyChecking=no \ -o ConnectTimeout=5 "root@$ip" \ "echo 'root:$NEW_PASS' | chpasswd" \ && echo "[OK] $ip 改密成功" \ || echo "[FAIL] $ip 改密失败,检查网络或初始密码" done < "$HOSTS_FILE"逻辑说明:while read逐行读主机列表,sshpass -p把密码喂给 ssh 的非交互式登录,登录后执行chpasswd完成改密。参数上有几个关键点:ConnectTimeout=5防止某台机器不通时整个脚本卡死;StrictHostKeyChecking=no跳过指纹确认,否则第一次连接会交互式询问,脚本直接挂住。边界在于,如果靶机禁用了密码登录只允许密钥,这个脚本就失效了,得换成先传公钥再改密的两步走。
改密之后要立刻验证,别改完就不管了:
# 用新密码回连验证,确认没把自己锁在门外 sshpass -p "$NEW_PASS" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 \ "root@$ip" "whoami" && echo "[VERIFY] $ip 新密码可用"这一步是血泪经验:曾经有队伍改密脚本跑完没验证,结果密码里带了特殊字符被 shell 转义,全员被锁在门外,眼睁睁看着分数掉。
3.2 Web 后门查杀:特征匹配与文件完整性两条路
查杀 webshell 有两条主流思路。一条是特征匹配,用已知 webshell 的关键字去 grep;另一条是文件完整性校验,赛前对 Web 目录做一次哈希快照,之后定时比对,发现新增或改动的文件就告警。前者快但漏报多,后者准但需要赛前准备。
特征匹配的脚本骨架:
#!/bin/bash # webshell_scan.sh - 基于关键字扫描可疑 PHP 文件 WEB_ROOT="/var/www/html" # 常见 webshell 特征,按需增删 PATTERNS="eval\(|assert\(|system\(|passthru\(|shell_exec\(|base64_decode\(|\\\$_(POST|GET|REQUEST)\[" grep -rEn "$PATTERNS" "$WEB_ROOT" --include="*.php" 2>/dev/null | \ while IFS=: read -r file line content; do echo "[SUSPECT] $file:$line" echo " $content" done逻辑说明:grep -rEn递归扫描并输出行号,--include="*.php"限定文件类型避免扫到日志。PATTERNS里把eval、assert、system这些危险函数和$_POST这类超全局变量组合起来,能命中大部分一句话木马。参数上要注意转义,\$是为了让$_POST里的$不被 shell 提前解析。这条路的坑是误报:正常业务代码里也可能有system(),所以扫出来的结果必须人工过一遍,不能直接删。
文件完整性校验更适合对抗阶段定时跑:
#!/bin/bash # integrity_check.sh - 比对 Web 目录哈希快照 WEB_ROOT="/var/www/html" SNAP="/tmp/web_snapshot.md5" if [ ! -f "$SNAP" ]; then # 首次运行生成基线快照 find "$WEB_ROOT" -type f -exec md5sum {} \; > "$SNAP" echo "[INIT] 基线快照已生成,共 $(wc -l < "$SNAP") 个文件" else # 后续运行比对,输出新增和改动 md5sum -c "$SNAP" 2>/dev/null | grep -v ": OK" fi逻辑说明:首次运行用find加md5sum生成基线,之后用md5sum -c校验,grep -v ": OK"过滤掉正常的文件,只留失败项。参数上WEB_ROOT必须和实际站点目录一致。这个脚本的边界是:如果对手改了文件又改回原样,哈希比对发现不了;如果对手在目录外种马,也扫不到。所以它和特征匹配要配合用,不能只靠一个。
注意:查杀脚本扫到可疑文件后,先备份再删除,别直接
rm。AWD 里误删业务文件导致服务起不来,扣的分比被种马还多。
4. flag 自动提交与流量监控:把得分动作和对抗动作串起来
查杀和改密是防守,真正拿分还得靠 flag 提交。这一章讲提交脚本怎么写、轮询频率怎么定,以及怎么用流量监控发现对手的利用行为。
4.1 flag 自动提交脚本的接口适配
不同赛事的 flag 提交接口格式不一样,有的是 GET 带参数,有的是 POST 带 JSON。脚本的核心是把「找到 flag」和「提交 flag」两步串起来。先看一个通用的提交函数:
#!/usr/bin/env python3 # submit_flag.py - 轮询 flag 文件并提交 import requests import time import re import os SUBMIT_URL = "http://10.0.0.1/api/submit" # 替换为实际提交接口 TOKEN = "your_team_token_here" # 替换为队伍 token FLAG_PATHS = ["/flag", "/tmp/flag", "/var/www/html/flag.php"] def find_flags(): """扫描常见 flag 路径,用正则提取 flag 字符串""" flags = set() pattern = re.compile(r"flag\{[^}]+\}", re.IGNORECASE) for path in FLAG_PATHS: if os.path.isfile(path): try: with open(path, "r", errors="ignore") as f: flags.update(pattern.findall(f.read())) except PermissionError: pass return flags def submit(flag): """提交单个 flag,返回是否成功""" try: resp = requests.post(SUBMIT_URL, data={"token": TOKEN, "flag": flag}, timeout=5) return resp.status_code == 200 except requests.RequestException: return False if __name__ == "__main__": submitted = set() while True: for flag in find_flags(): if flag not in submitted: ok = submit(flag) print(f"[{'OK' if ok else 'FAIL'}] {flag}") if ok: submitted.add(flag) time.sleep(10) # 轮询间隔,按赛事节奏调整逻辑说明:find_flags用正则flag\{[^}]+\}从文件里提取 flag,submit用 POST 提交。参数上有三个必须改:SUBMIT_URL、TOKEN、FLAG_PATHS。submitted集合做去重,避免同一个 flag 反复提交被接口限流。轮询间隔time.sleep(10)是个权衡:太短会给提交接口压力甚至被封,太长会错过对手刚种下的 flag。常见做法是 5 到 15 秒之间,看赛事方给的限流说明。
这里有个容易翻车的点:flag 路径不是固定的。有的赛事 flag 在/flag,有的在数据库里,有的需要先通过漏洞拿到。所以FLAG_PATHS要按实际赛题补充,不能只靠默认值。
4.2 用流量监控发现对手的利用行为
防守方最被动的地方是不知道对手什么时候打进来。流量监控能把这个黑匣子打开一点。靶机上如果没有 tcpdump,可以用ss和netstat做轻量监控,看异常连接。
#!/bin/bash # conn_monitor.sh - 监控异常外连和监听端口变化 BASELINE="/tmp/port_baseline.txt" if [ ! -f "$BASELINE" ]; then ss -tulnp | awk '{print $1,$5}' | sort > "$BASELINE" echo "[INIT] 端口基线已记录" else ss -tulnp | awk '{print $1,$5}' | sort > /tmp/port_now.txt # 对比基线,输出新增的监听端口 diff "$BASELINE" /tmp/port_now.txt | grep "^>" | \ while read -r _ proto addr; do echo "[ALERT] 新增监听: $proto $addr" done fi逻辑说明:ss -tulnp列出所有 TCP/UDP 监听和连接,awk提取协议和地址字段,sort后和基线diff。新增的监听端口往往意味着对手起了后门或者反弹 shell。参数上-tulnp里的p需要 root 权限才能看到进程名,非 root 跑会缺信息。这个脚本的边界是:它只能发现监听变化,发现不了已经建立的短连接,所以适合定时跑而不是实时抓。
提示:流量监控脚本建议放在 cron 里每分钟跑一次,输出重定向到日志文件,赛后复盘时这些日志就是对手打法的第一手资料。
5. 避坑与排查:AWD 脚本跑不起来时的五条血泪记录
脚本合集最大的价值不是代码本身,而是别人踩过的坑。这一章按「现象 → 原因 → 解决」整理五条最常见的翻车记录,都是实际比赛里遇到过的。
第一条:脚本执行后 SSH 全部连不上。现象是改密脚本跑完,所有靶机 SSH 拒绝连接。原因通常是新密码里带了$、!、&这类 shell 特殊字符,在echo 'root:$NEW_PASS' | chpasswd里被提前解析,实际设置的密码和预期不一致。解决办法是改密时用单引号包裹并转义,或者干脆用chpasswd从标准输入读,避免 shell 解析。更稳妥的做法是密码只用大小写字母加数字。
第二条:查杀脚本把正常业务文件删了。现象是查杀后网站 500,业务功能挂掉。原因是特征匹配误报,把正常代码里的system()调用当成 webshell 删了。解决办法是查杀脚本只做「标记」不做「删除」,把可疑文件列表输出到文件,人工确认后再处理。如果非要自动删,至少先cp到隔离目录。
第三条:flag 提交脚本疯狂报 429。现象是提交接口返回 429 Too Many Requests。原因是轮询间隔太短,或者同一个 flag 重复提交。解决办法是加去重集合,把间隔调到 10 秒以上,并且对 429 响应做退避处理,遇到限流就暂停一段时间再试。
第四条:cron 里的脚本不执行。现象是手动跑脚本正常,放进 crontab 就没反应。原因是 cron 的环境变量和登录 shell 不一样,脚本里用的相对路径或者依赖的PATH找不到。解决办法是在脚本开头显式设置PATH,所有路径用绝对路径,并且在 crontab 里把输出重定向到日志文件方便排查。
第五条:监控脚本自己把靶机跑卡了。现象是开了监控后靶机负载飙升。原因是grep -r全盘扫描或者find遍历大目录,IO 吃满。解决办法是限定扫描目录和文件类型,用--include和-maxdepth控制范围,监控类脚本的扫描间隔不要低于 30 秒。
6. 把脚本改造成自己的响应框架:参数化与验证的两个技巧
合集里的脚本能直接用,但真正拉开差距的是把它改成适配自己队伍流程的框架。这里分享两个我常用的改造技巧,一个是参数化配置,一个是提交前的本地验证。
参数化的核心是把所有环境相关的值抽到一个配置文件里,脚本只读配置不写死。比如建一个awd.conf:
# awd.conf - 环境配置,所有脚本 source 这个文件 WEB_ROOT="/var/www/html" FLAG_PATHS="/flag /tmp/flag" SUBMIT_URL="http://10.0.0.1/api/submit" TEAM_TOKEN="your_token" HOSTS_FILE="/root/hosts.txt" SCAN_INTERVAL=30然后每个脚本开头加一行source /root/awd.conf,把原来写死的路径全换成变量。这样做的好处是换一场比赛只改一个文件,不用逐个脚本去翻。参数命名上建议统一前缀,避免和系统变量冲突。
第二个技巧是提交前的本地验证。flag 提交是有次数限制的,提交错了浪费机会。所以在真正 POST 之前,先用正则校验 flag 格式:
import re FLAG_PATTERN = re.compile(r"^flag\{[0-9a-f]{32}\}$", re.IGNORECASE) def validate_flag(flag): """提交前校验格式,避免浪费提交次数""" if not FLAG_PATTERN.match(flag): print(f"[SKIP] 格式不符: {flag}") return False return True逻辑说明:FLAG_PATTERN按实际赛事的 flag 格式写,常见的是flag{32位十六进制}。validate_flag在提交前拦一道,格式不对的直接跳过。参数上正则要按赛事调整,有的比赛 flag 是flag{uuid}格式,那就换成对应的正则。这个技巧看起来简单,但能省下大量无效提交。
从那以后我每次拿到新的 AWD 脚本合集,都强制走一遍「读配置 → 改路径 → 本地验证 → 小范围试跑」这个流程,再也没出现过开局全员被锁在门外的情况。这套脚本合集的价值不在于它多完整,而在于它把 AWD 里那些重复动作固化成了可复用的骨架,你只需要按自己的赛制填参数。希望帮到你。
本文还有配套的精品资源,点击获取