☰
AWD攻防赛脚本集合实战:从批量探测到防御加固全解析
2026/10/7 6:16:30 网站建设 项目流程

简介:AWD攻防赛脚本集合,专为AWD(Attack With Defense)攻防对抗赛选手与CTF安全爱好者准备,围绕常见赛题场景整合了从信息收集、GetFlag到不死马对抗的一线实战脚本。压缩包体积约3.14MB,内含33个文件,以Python自动化脚本(12个py)、PHP马与Web工具(6个php)、TXT配置与备忘(7个txt)为主,另有少量pyc编译脚本、RAR安全分析工具集与exe辅助程序,覆盖攻击、防御、日志排查等维度。已有408人学习下载。具体来看,Attack目录下准备了上传Shell、批量生成不死马、隐藏不死马等进攻脚本;Defense目录则提供Linux文件监控、WAF策略、修改curl、日志地址等防御措施,并附带“克制不死马”思路与Web日志安全分析工具,便于读者在演练环境中快速部署、相互验证攻防手法。这份集合适合有一定Web安全基础、希望在AWD赛前打磨攻防效率的选手直接取用。

1. AWD攻防赛脚本集合是什么:先搞清楚它到底在解决什么问题

AWD(Attack With Defense,攻防兼备)是CTF里竞争最激烈、节奏最紧的一种赛制:每支队伍维护自己的靶机,跑着好几个Web服务,既要去打别人的服务拿flag,又要守住自己的服务不被别人打穿。AWD攻防赛脚本集合.7z正是围绕这种赛制沉淀下来的“工具包”,里面装了批量探测、批量利用、批量提交flag、不死马查杀、文件监控、日志审计等一类脚本,解决的都是重复且必须做的操作。它的核心价值,是把开局前15分钟的抢分和保命动作压缩成一条命令;适合第一次打AWD的CTF新手,也适合参赛队拿来做赛前模拟和现场支撑。这篇就围绕这份脚本集合展开,讲清它包含什么、怎么跑起来、参数怎么调、坑又踩在哪。

2. 攻防节奏决定脚本选型:AWD比赛的时间线里,攻击和防御脚本各司其职

2.1 比赛里的“黄金时间”只是前20分钟

常见的AWD赛制两小时起步,真正决定排名的往往是最初20分钟。开局大家同时登上靶机,第一件事是检查自己的Web服务有没有被预留后门,其次才是批量探测别人的存活服务和开放端口。手敲命令不是不行,问题在于两条内网六七十个目标,一个目标敲几条命令,等到探测完,别人已经把flag捞走好几轮了。

脚本集合的价值在黄金期放到最大:一轮批量探测,把可达的ip:port筛出来;再挂上批量利用,拿flag并自动提交。手工做至少15分钟,脚本化后压缩到几十秒。防守端同样如此——开局该做的是立刻挂文件监控和进程监控,不然自己的服务被种了不死马,后面几轮会一直被人持续拿分,防守脚本就是那个“后悔药”:发现得越早,损失越小。

2.2 攻击脚本和防御脚本的分工与联系

脚本集合里常见脚本类别,按目标和运行时机可以这样分:

脚本类别主要目标运行时机
批量服务探测收集存活端口、服务版本开局第一轮
批量漏洞利用拿目标WebShell或RCE开局至拉锯期
批量flag提交快速把多个flag送交到计分平台全程
文件监控/哈希快照发现WebShell被植入、文件被篡改开局后就挂上
进程监控发现不死马、异常进程全程
日志审计从访问日志还原攻击路径出现失分时

攻击脚本解决“打别人”的收益问题,防御脚本解决“被打”的止损问题。一套脚本其实可以两头用:批量探测工具既能扫别人,也能扫自己做资产盘点;日志审计既能看别人怎么打进来的,也能在失分后复盘攻击者做了什么。选型时要清楚,AWD拿分靠的是快速且稳定的自动化,不是单一花哨的利用链。

2.3 选型上的两个常见误区和高手习惯

第一个误区是只准备“打”的脚本,忽略“防御”脚本。实际比赛里,守不住自己的一方会被反复“薅羊毛”,即使攻击再猛,防御失分也是持续流血的。第二个误区是过度相信现成脚本——语法和依赖状态都不测,比赛现场翻车概率极高:Python 2/3混跑、缺requests模块、Windows下写的文件直接复制到Linux跑,这些黑匣子问题会让脚本一到赛场就废。

高手习惯是:拿到AWD脚本集合后,第一件事不是看利用链多华丽,而是把这套东西装进一个干净Linux环境,从requirements到入口脚本完整做一遍冒烟测试。测试通过之后,再把IP、端口、flag提交接口等配置抽出来做成单独配置,绝不在比赛现场改代码。这样多花半小时,现场能省出十倍的调试时间。

3. 从7z解压到跑通环境:Linux下的解压与依赖配置

3.1 在Linux中解压7z文件:p7zip命令与常用参数

大多数赛事环境是Linux,第一步就是在Linux里解压7z文件。常见做法是安装p7zip-full,用7z x命令解压,而不是用Windows里的压缩软件解压后再传上来,Windows解压容易丢文件权限。命令如下:

# 在 Ubuntu / Debian 上安装 p7zip-full sudo apt-get update sudo apt-get install -y p7zip-full # 解压到 awd_tools 目录 7z x "AWD攻防赛脚本集合.7z" -o~/awd_tools

参数说明:7z x保留原目录结构,-o指定输出目录,这里输出到用户目录下的awd_tools;如果省略-o,默认解压到当前目录,内容会直接混进工作目录,后期清理麻烦。解压后先确认里面是否又套了一层目录,很多资源包外层会再套一层日期或版本目录,需要看一眼再决定以哪个为根。

解压完成后,用下面的命令检查文件:

find ~/awd_tools -maxdepth 1 -type f | head -20 find ~/awd_tools -maxdepth 1 -type d | sort

可以看到脚本入口、README和子目录。如果发现某个脚本没有可执行权限,也不用着急,bash xxx.sh和python3 xxx.py两种运行方式本来就不依赖可执行位。

3.2 脚本依赖的环境:Python 3、bash与第三方库

这套脚本集合里,大半是Python脚本,另外是bash脚本。先确认Python版本,再补第三方依赖,顺序不能反:先改代码再装环境,会把报错混在一起。

# 确认解释器版本 python3 --version bash --version # 安装常见依赖 python3 -m pip install --user requests aiohttp paramiko

依赖说明:其中requests用于HTTP探测和flag提交,几乎每个联网脚本都会用到;aiohttp出现在协程并发的采集脚本里;paramiko出现在批量SSH执行脚本里。如果你打开脚本看到import paramiko,说明它需要SSH口令登录靶机,这类脚本在比赛里最容易被防火墙拦,需要额外确认端口和出入口策略。

shell脚本的坑在于解释器路径。开头是#!/bin/bash的不会有问题,但如果看到#!/bin/sh而脚本里用了bash的数组语法,在部分环境会报错。我一般建议把这类脚本统一改成bash调用:bash 脚本名.sh,绕开默认shell差异。

3.3 目录结构和启动顺序

一类脚本集合的目录结构,通常长这样:

awd_tools/ ├── attack/ │ ├── scan.py # 批量服务探测 │ ├── exp_poc_example.py # 批量利用入口 │ └── flag_submitter.py # 批量提交flag ├── defense/ │ ├── file_monitor.py # 文件哈希监控 │ ├── proc_monitor.sh # 进程监控 │ └── clean_webshell.py # WebShell清理 ├── lib/ │ ├── config.py # 公共配置 │ └── network.py # 网络请求封装 ├── logs/ └── run.sh # 统一入口

启动顺序我一般固定为:先改lib/config.py,把本机IP、flag提交接口、队伍token填好;然后启动defense/下的监控脚本;最后再跑attack/下的攻击脚本。这个顺序有讲究——监控脚本先挂上,意味着自己一旦被入侵,日志至少会留下痕迹;如果先跑攻击,注意力全在别人身上,自己的靶机被种了后门还要过几十秒才被发现。

4. 核心脚本的运行与参数设置:批量探测、文件监控、批量修补和flag提交

4.1 批量探测脚本:从IP列表到存活目标列表

比赛第一件事是对目标做批量存活探测。AWD的靶机内网一般是10.x.x.x网段,服务端口多为80、8080、8888之类。探测脚本的作用是把“ip:port列表”过滤成“能访问的Web服务列表”,顺带返回状态码和页面大小,方便判断服务是否异常。

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 服务存活探测:输入目标文件,输出可达服务的状态与页面大小 import argparse import requests from concurrent.futures import ThreadPoolExecutor, as_completed def check_one(item, timeout): ip, port, path = item url = f"http://{ip}:{port}{path}" try: r = requests.get(url, timeout=timeout, verify=False) return (ip, port, r.status_code, len(r.content)) except requests.RequestException: return (ip, port, None, 0) def main(): parser = argparse.ArgumentParser(description="AWD服务存活探测") parser.add_argument("--targets", required=True, help="目标文件,每行 ip:port") parser.add_argument("--path", default="/", help="探测时使用的 URL 路径") parser.add_argument("--timeout", type=float, default=3.0, help="单请求超时秒数") parser.add_argument("--threads", type=int, default=20, help="并发线程数") args = parser.parse_args() items = [] with open(args.targets, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue parts = line.split(":") items.append((parts[0], parts[1], args.path)) with ThreadPoolExecutor(max_workers=args.threads) as pool: futures = [pool.submit(check_one, item, args.timeout) for item in items] for fut in as_completed(futures): ip, port, status, size = fut.result() print(f"{ip}:{port}\t{status}\t{size} bytes") if __name__ == "__main__": main()

逻辑说明:脚本读入--targets文件,每行格式为ip:port,跳过空行和#注释行;然后用ThreadPoolExecutor并发发起HTTP GET请求;最后打印ip:port、HTTP状态码和页面大小。状态码是200或302说明Web服务活着,500说明服务正在报错,可以优先列入漏洞利用目标。

参数设置上,--threads一般给10到20,不要盲目调大。目标内网机器配置参差不齐,并发过高会把低配靶机连接数打满,导致服务直接崩掉,这种“翻车”在批量探测阶段很常见。--timeout用3秒左右够用,太短误报多,太长拖慢整轮扫描。verify=False是因为比赛内网很多服务用的自签名证书,不校验证书能减少网络层的额外干扰。

4.2 文件监控脚本:用哈希快照发现WebShell

防守端最推荐常驻的脚本是文件监控。攻击者打进服务后,最常见的动作是写一个WebShell文件或修改现有PHP文件,文件内容的变化往往就发生在几秒内。文件监控脚本通过周期性计算网站目录的文件哈希,把“新增、变更、删除”三类变化实时打印出来,适合用来快速定位被种了后门的文件。

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 文件哈希监控:第一次运行建立基准,之后周期性比对 import argparse import hashlib import os import time def file_hash(path): h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(65536), b""): h.update(chunk) return h.hexdigest() def build_snapshot(root): snap = {} for dirpath, _, files in os.walk(root): for name in files: full = os.path.join(dirpath, name) try: snap[full] = file_hash(full) except (OSError, PermissionError): continue return snap def main(): parser = argparse.ArgumentParser(description="Web目录文件监控") parser.add_argument("--root", required=True, help="网站根目录") parser.add_argument("--interval", type=float, default=10, help="检测间隔秒数") args = parser.parse_args() base = build_snapshot(args.root) print(f"[*] 基准快照建立完成,共 {len(base)} 个文件") while True: time.sleep(args.interval) now = build_snapshot(args.root) for path in now: if path not in base: print(f"[新增] {path}") elif base[path] != now[path]: print(f"[变更] {path}") for path in set(base) - set(now): print(f"[删除] {path}") base = now if __name__ == "__main__": main()

逻辑说明:脚本第一次运行时会遍历网站根目录,生成所有文件的SHA256哈希作为基准;之后每过--interval秒重新扫描,对比当前快照和上一次快照,输出新增、变更、删除的文件路径。

这里有两个重要细节。第一个是基准本身要“干净”:如果靶机开局时已经被种了后门,第一次快照会把后门文件也哈希进基准里,后续监控就永远发现不了它。所以比赛开局应当先从备份目录恢复一次干净源码,再挂监控。第二个是--interval不用设太短,10秒足够发现绝大多数WebShell变更,太短会把磁盘I/O拉满,影响服务响应。

4.3 批量修补脚本:把加固动作批量送到每台靶机

AWD比赛中,修补漏洞的速度几乎等于防守得分。批量修补脚本的一般做法是:把需要执行的操作整理成一个shell函数,再用SSH批量送到多台靶机上执行。这里以去除上传目录执行权限和修正Web目录属主为例:

#!/bin/bash # 批量加固脚本:用于比赛靶环境的防御操作 # 用法: ./hardening.sh <hostlist.txt> <user> <password> HOSTLIST=${1} USER=${2} PASS=${3} while IFS= read -r host; do echo "=== 处理 $host ===" sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no "$USER@$host" ' # 去掉上传目录的执行权限,阻止WebShell落地 find /var/www/html -type d -name "upload" -exec chmod 700 {} \; 2>/dev/null # 修正属主,让Web服务用户只有必要写权限 chown -R www-data:www-data /var/www/html echo "[OK] $host" ' || echo "[FAIL] $host" done < "$HOSTLIST"

逻辑说明:脚本以hostlist.txt为输入,每行一个靶机地址,用sshpass配合密码登录,再在远端执行引号里的加固命令。StrictHostKeyChecking=no省去首次连接时的指纹确认交互,适合这种批量场景。

这个脚本的使用前提是已经掌握了靶机SSH凭据,也就是在比赛授权范围内操作。实际跑的时候最需要注意三点:一是确认目标SSH端口不是22时要写成-p 端口;二是批量脚本必须输出FAIL清单,否则有些主机没连上你也不知道;三是find命令在目录不存在时会报错,所以先确认Web根目录路径再加固。

4.4 批量flag提交器:别让到手的flag砸手里

AWD拿分靠提交flag,但手动一个flag一个flag地在平台上提交,既慢又容易漏。脚本集合里的flag提交器,作用是把从各目标处收集到的flag批量POST到计分平台。这里给出一个通用实现思路:

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 批量flag提交:从文件读取flag,逐个提交到计分平台 import argparse import requests import time def submit_one(api, token, flag): payload = {"flag": flag.strip(), "token": token} r = requests.post(api, json=payload, timeout=5) try: return r.json().get("success", False) except ValueError: return False def main(): parser = argparse.ArgumentParser(description="AWD flag批量提交") parser.add_argument("--api", required=True, help="flag提交接口地址") parser.add_argument("--token", default="", help="队伍token") parser.add_argument("--flagfile", required=True, help="flag文件,每行一个") args = parser.parse_args() with open(args.flagfile, "r", encoding="utf-8") as f: flags = [line.strip() for line in f if line.strip()] ok = 0 for flag in flags: try: if submit_one(args.api, args.token, flag): ok += 1 else: print(f"[重复或失败] {flag}") except requests.RequestException as e: print(f"[接口异常] {flag}: {e}") time.sleep(0.2) print(f"[*] 尝试提交 {len(flags)} 个flag,成功 {ok} 个") if __name__ == "__main__": main()

逻辑说明:脚本读入--flagfile文件,每行一个flag,然后循环调用submit_one函数,把flag和token以JSON格式POST到计分接口;根据返回的success字段统计成功数量,并对接口异常做延迟重处理。

参数上,--api填写比赛平台提供的提交接口,--token填队伍标识,--flagfile指向脚本集合里攻击脚本写入的flag结果文件。time.sleep(0.2)是刻意加的——不加间隔把所有flag瞬间打过去,容易被计分平台判定为异常流量,限制提交频率反而更稳妥。

5. 避坑/常见问题/排查:跑脚本时真正会翻车的五个地方

5.1 在Windows下解压直接运行:脚本命令闪退

现象:拿到脚本集合先在本机用压缩软件解压,然后双击run.bat,黑窗口一闪而过,什么都没执行。

原因:脚本面向Linux环境编写,运行命令是bash或python3,Windows的cmd不能识别这些解释器,闪退其实是解释器解析失败。

解决:在WSL、Linux虚拟机或比赛跳板上解压运行;如果暂时只能在Windows测试,使用Git Bash,并显式写成bash run.sh。这个坑在筹备AWD期间最常发生,建议从一开始就用Linux作为统一环境。

5.2 Python脚本报UnicodeDecodeError

现象:运行python3 flag_submitter.py时报UnicodeDecodeError: 'gbk' codec can't decode byte,脚本直接中断。

原因:脚本默认使用平台编码读取文件,Windows下是GBK,Linux下是UTF-8;flag文件里若有中文注释或特殊字符,编码对不上就报错。

解决:在脚本里统一指定encoding="utf-8",或者在运行时把数据文件转成UTF-8。用代码排查时可以先执行file flags.txt,确认文件编码后再让脚本读。比赛现场没有时间做编码侦探,提前统一成UTF-8是理性的选择。

5.3 批量探测并发太高:目标服务被“探测”崩了

现象:探测脚本运行几分钟后,发现若干目标访问超时,页面打不开;连后面想提交flag都因为目标不可达而失败。

原因:并发线程数开太高,低配靶机连接数被打满;或者短时间大量请求触发了对方防火墙,直接把扫描方IP封掉。

解决:把--threads控制在10到20,--timeout设为3到5秒;如果目标已经全部存活且稳定,就先停止探测,不要无脑循环扫描。AWD是持久战,目标崩了等于自己少了一个“提款机”,这比被反打更亏。

5.4 权限不足:删掉的WebShell几秒后又长出来

现象:手动删除WebShell文件后,过几秒文件又出现,文件监控脚本持续报警;有时候删除命令直接报Permission denied。

原因:删除操作使用的用户权限不足,或有不死马进程在持续重建文件;如果只是删文件没杀进程,等于白删。

解决:先定位进程再删除文件,最后做目录权限收紧。典型顺序是:ps aux | grep php找到异常进程,kill -9进程,再删除WebShell文件,然后用chattr +i锁定关键目录,这样文件不会被再次写入。要注意的是,chattr +i也会锁住正常部署,操作前必须确认这些目录不再有合法写入。

5.5 硬编码IP和路径:换一个比赛环境脚本就报废

现象:脚本在自己电脑上跑得好好的,换到比赛内网后全部连接失败,打开源码发现IP和路径写死在代码里。

原因:脚本作者为方便直接硬编码了比赛平台的地址、路径等环境参数,没有做成可配置项。

解决:拿到脚本集合的第一件事,搜索所有明文IP、域名、端口和路径,统一改成通过配置文件或命令行参数传入。常见的做法是单独维护一个config.py,把HOST、PORT、API_URL、TOKEN全放进去,脚本只引用配置不写死值。这个习惯能省掉比赛现场大量无意义调试。

6. 把脚本集合改造成自己的“武器库”:几个长期有效的验证技巧

6.1 在Docker里自建靶场做冒烟测试

脚本集合再好,没验证过的脚本在比赛现场就是黑匣子。我会在赛前用Docker起一个最简靶场,把脚本完整跑一遍,验证监控脚本能不能报警、提交脚本能不能连通接口。这个做法花费很小,收益非常直接。

# 起一个最简Web容器做测试 docker run -d --name awd_test -p 18080:80 php:5.6-apache # 把网站目录挂出来,用于放测试文件 docker exec awd_test mkdir -p /var/www/html/test

然后在容器里放一个模拟文件,运行文件监控脚本,看到“新增”事件正常打印后,再手动删除文件,确认“删除”事件也能抓到,整个冒烟测试才算通过。比赛现场最怕的就是防御脚本形同虚设,提前验证至少能保证“报警”这一步不会掉链子。

6.2 用run.sh统一入口,避免比赛现场手忙脚乱

脚本一多,现场容易记不住参数。我的习惯是写一个统一入口脚本,把常用的调用方式固定下来,比赛时就只需要记一个命令:

#!/bin/bash # 统一入口:run.sh <monitor|scan|hardening|submit> set -e case "$1" in monitor) python3 defense/file_monitor.py --root "${2:-/var/www/html}" --interval "${3:-10}" ;; scan) python3 attack/scan.py --targets "$2" --threads "${3:-20}" ;; hardening) bash defense/hardening.sh "$2" "$3" "$4" ;; submit) python3 attack/flag_submitter.py --api "$2" --token "$3" --flagfile "$4" ;; *) echo "用法: $0 {monitor|scan|hardening|submit}" echo "示例: $0 monitor /var/www/html 10" exit 1 ;; esac

这个入口做的事很简单:把每个脚本的常用参数与默认值绑定,调用时只需传入最少的参数;set -e确保脚本出错时立刻退出,不会继续执行后续命令、误导判断。

养成这个习惯之后,比赛现场的操作就变成了记四个子命令:monitor挂监控,scan扫目标,hardening做加固,submit交flag。我现在拿到任何脚本集合,做的第一件事不是看漏洞利用链,而是先把入口、依赖和环境变量理清楚,再在本地容器做一遍冒烟测试。AWD现场没有太多调试时间,一次脚本翻车,代价可能就是几个flag的分数。希望这套思路能帮你在下一场比赛前把脚本整理好,让现场操作更稳。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询