AWDP赛制详解与实战防御指南
一、AWDP赛制概述
AWDP(Attack With Defense Plus)是一种综合考核参赛团队攻击、防御技术能力、即时策略的攻防兼备比赛模式。每个参赛队互为攻击方和防守方,充分体现比赛的实战性、实时性和对抗性。
比赛组成
- Break:使用自己的payload打通目标系统
- Fix:使主办方的payload无法打通自己的系统
策略建议:
- Fix通常比Break容易,优先Fix
- 如果是分时进行的,Break时建议同时思考Fix方案
赛前准备
- 离线语言手册(如utools)
- 安全文章库
- 各语言WAF(Web应用防火墙)模板
二、常见防御手段
通用防御
- 上WAF
- 注释漏洞语句
语言/漏洞特定防御
PHP特性
- 基本无特殊防御意义
SQL注入
- WAF
addslashes()函数过滤- 预处理(最佳方案)
SSTI(服务器端模板注入)
- WAF(仅过滤
{不够)
原型链污染
- 注释污染相关代码
文件上传
- 后缀强校验
- 文件内容WAF
- MIME头校验(建议同时实施)
Java
- 注释漏洞代码
- 上调库版本
- WAF
代码审计
- WAF
- 注释漏洞代码
三、WAF编写指南
通用注意事项
- 注意语法正确性,本地测试
- 单个语法错误可能导致全部防御失效
- 平时积累有效的WAF规则
PHP WAF示例
基础过滤:
$str1 = "";
foreach ($_POST as $key => $value) {
$str1.=$key;
$str1.=$value;
}
$str2 = "";
foreach ($_GET as $key => $value) {
$str2.=$key;
$str2.=$value;
}
if (preg_match("/system|tail|flag|exec|base64|phpinfo|<\?|\"/i", $str1)||preg_match("/system|tail|flag|exec|base64|phpinfo|<\?|\"/i", $str2)) {
die('no!');
}
RCE防御:
function wafrce($str){
return !preg_match("/openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|scandir|assert|pcntl_exec|fwrite|curl|system|eval|assert|flag|passthru|exec|chroot|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore/i", $str);
}
SQL注入防御:
function wafsqli($str){
return !preg_match("/select|and|\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleexml|extractvalue|+|regex|copy|read|file|create|grand|dir|insert|link|server|drop|i", $str);
}
XSS防御:
function wafxss($str){
return !preg_match("/\'|http|\"|\`|cookie|<|>|script/i", $str);
}
Go WAF示例
依赖strings库版本:
import (
"fmt"
"strings"
)
func main() {
var input string
fmt.Print("请输入一个字符串:")
fmt.Scanln(&input)
maliciousStrings := []string{
"union", "select", "delete", "insert", "update", "truncate",
"drop", "create", "\"", "'", " ", "{{", "}}", ".", "{", "}", "flag"}
input = strings.ToLower(input)
for _, s := range maliciousStrings {
if strings.Contains(input, s) {
return
}
}
}
不依赖库版本:
import (
"fmt"
)
func main() {
var input string
fmt.Print("请输入一个字符串:")
fmt.Scanln(&input)
maliciousStrings := []string{
"union", "select", "delete", "insert", "update", "truncate",
"drop", "create", "\"", "'", " ", "{{", "}}", ".", "{", "}", "flag"}
if isMalicious(input, maliciousStrings) {
return
}
}
func isMalicious(input string, maliciousStrings []string) bool {
input = stringToLower(input)
for _, s := range maliciousStrings {
if stringContains(input, s) {
return true
}
}
return false
}
func stringToLower(str string) string {
runes := []rune(str)
for i, r := range runes {
if r >= 'A' && r <= 'Z' {
runes[i] = r + ('a' - 'A')
}
}
return string(runes)
}
func stringContains(str string, substr string) bool {
strRunes := []rune(str)
substrRunes := []rune(substr)
for i := 0; i <= len(strRunes)-len(substrRunes); i++ {
found := true
for j := 0; j < len(substrRunes); j++ {
if strRunes[i+j] != substrRunes[j] {
found = false
break
}
}
if found {
return true
}
}
return false
}
Node.js WAF示例
const input = "awdwawdd";
const maliciousStrings = [
"__proto__", "constructor", "prototype", "insert", "update", "truncate",
"drop", "create", "\"", "'", " ", "{{", "}}", "union", "select", "delete",
"\"", "'", " ", "{{", "}}", ".", "{", "}", "flag"
];
function isMalicious(input, maliciousStrings) {
input = input.toLowerCase();
for (let i = 0; i < maliciousStrings.length; i++) {
const pattern = new RegExp(maliciousStrings[i], "i");
if (pattern.test(input)) {
return true;
}
}
return false;
}
if (isMalicious(input, maliciousStrings)) {
console.log("输入参数包含恶意字符串");
} else {
console.log("输入参数安全");
}
Java WAF示例
import java.util.regex.Pattern;
public class MaliciousInputChecker {
public static void main(String[] args) {
String input = "SELECT * FROM users WHERE id = 1 OR 1=1";
}
public static boolean isMalicious(String input, String[] maliciousStrings) {
input = input.toLowerCase();
for (int i = 0; i < maliciousStrings.length; i++) {
Pattern pattern = Pattern.compile(maliciousStrings[i], Pattern.CASE_INSENSITIVE);
if (pattern.matcher(input).find()) {
return true;
}
}
return false;
}
}
Python WAF示例
input_str = "awdawafaunonwdwa"
malicious_strings = [
"__proto__", "constructor", "prototype", "insert", "update", "truncate",
"drop", "create", " \" ", "'", " ", "{{", "}}", "union", "select", "delete",
" \" ", "'", " ", "{{", "}}", ".", "{", "}", "flag"
]
for s in malicious_strings:
if input_str.lower().find(s) != -1:
exit()
black_list = ["{{", "}}", "'", '"', '_', '[', '.', '%', '+', '|', '(', ')', '{', '}', ' \\ ', '/']
for tmp in black_list:
if tmp in v:
raise ValueError("note cannot contain a special character")
四、其他防御手段
PHP防御
SQL注入防御
addslashes()函数:
$username = $_GET['username'];
$password = $_GET['password'];
$username = addslashes($username);
$password = addslashes($password);
- 全局过滤:
foreach($_REQUEST as $key=>$value) {
$_POST[$key] = addslashes($value);
$_GET[$key] = addslashes($value);
$_REQUEST[$key] = addslashes($value);
}
- 预处理(最佳方案):
// MySQLi预处理
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
.htaccess防护
- 完全禁止访问:
<IfModule mod_rewrite.c>
deny from all
</IfModule>
- 禁止访问ph开头的文件:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule \.ph.*$ - [F]
</IfModule>
- 防不死马:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^\.index\.php$ - [F]
</IfModule>
ThinkPHP框架防护
foreach($_REQUEST as $key=>$value) {
$_POST[$key] = preg_replace("/construct|get|call_user_func|load|invokefunction|Session|phpinfo|param1|Runtime|assert|input|dump|checkcode|union|select|updatexml|@/i",'',$value);
$_GET[$key] = preg_replace("/construct|get|call_user_func|load|invokefunction|Session|phpinfo|param1|Runtime|assert|input|dump|checkcode|union|select|updatexml|@/i",'',$value);
}
Java防御
文件上传监控
./monitor-Go
命令黑名单
String a = "123";
String[] blacklist = {"Runtime", "\\u", "exec", "\"", "+", "'", "", "(", ")", "\\", "<", ">"};
for (int i = 0; i < blacklist.length; i++){
if (a.contains(blacklist[i])){
throw new Exception("");
}
}
反序列化防护
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.HashSet;
import java.util.Set;
public class NewObjectInputStream extends ObjectInputStream {
private static final Set<String> BLACKLISTED_CLASSES = new HashSet();
static {
BLACKLISTED_CLASSES.add("java.lang.Runtime");
BLACKLISTED_CLASSES.add("java.lang.ProcessBuilder");
BLACKLISTED_CLASSES.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
BLACKLISTED_CLASSES.add("java.security.SignedObject");
}
public NewObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (BLACKLISTED_CLASSES.contains(desc.getName())) {
throw new SecurityException("");
}
return super.resolveClass(desc);
}
}
Node.js防御
参数白名单
- 将merge参数改为白名单,只能传入admin
黑名单过滤
const blacklist = [' exec ',' \' ',' "']
const a = " 123 "
for (const pattern of blacklist){
if (pattern.includes(a)){
throw new Error(``);
}
}
Python防御
SQL注入防护
conn = sqlite3.connect('db.sqlite3')
cursor = conn.cursor()
cursor.execute("INSERT INTO sys_users (username, password, role) VALUES (?, ?, ?)",
(username, password, role))
黑名单过滤
content = ''
blacklist = [' \' ', 'union', ' \" ', 'select', '(', ')', ',', ' ', '%']
for i in blacklist:
if i in content.lower():
exit()
五、AWDP比赛实战经验
安恒AWDP比赛特点
- 历史比赛:羊城杯2023、楚慧杯2024、CISCN2024华东南
- 赛制特点:
- Break和Fix分开进行
- 通过SSH连接自行修改代码
- 每小时Check一次,统一检查修复情况
- 题目类型:
- 签到题
- 文件上传
- 代码审计
- Python漏洞
- 压轴题
- 策略建议:
- 第一轮修出3个基本能拿证书
- 签到和文件上传通常能快速解决
永信至诚AWDP比赛特点
- 历史比赛:CISCN2023、春秋杯夏季赛
- 赛制特点:
- Fix给15-20次机会,可随时使用
- 上传压缩文件包(patch包)
- 检测状态:
- exp利用成功
- 检测/运行异常
- 防御成功
- 服务检测异常(可能原因):
- 过滤了正常流量中的某些字符
- 页面返回结果不符合逻辑
Patch包制作指南
- PHP示例:
#!/bin/bash
cp /index.php /var/www/html/index.php
- Python示例:
#!/bin/sh
cp /app.py /app/app.py
ps -ef | grep python | grep -v grep | awk '{print $2}' | xargs kill -9
cd /app && nohup python app.py >> /opt/app.log 2>&1 &
- Go示例:
#!/bin/bash
kill -9 $(pidof app)
cp ezgo_patch /app
chmod +x /app/app 2>&1 >/dev/null &
- Node.js示例:
#!/bin/sh
cp server.js /app/server.js
ps -ef | grep node | grep -v grep | awk '{print $2}' | xargs kill -9
cd /app && nohup node server.js >> /opt/aa.log 2>&1 &
- 文件移动:
mv -f explorer.php /www/html/
# 或
mv -f explorer.php $(dirname `find / -name 'explorer.php' 2>/dev/null`)/explorer.php
打包命令
tar -zcvf patch.tar.gz main.py patch.sh
杀线程命令
# JavaScript
ps -ef|grep npm|grep -v grep |awk '{print $2}'|xargs kill -9
ps -ef|grep node|grep -v grep |awk '{print $2}'|xargs kill -9
# 根据目录杀线程
ps -ef|grep app|grep -v grep |awk '{print $2}'|xargs kill -9
权限设置
sudo chmod -R 777 /app/*
六、实战案例解析
案例1:[羊城杯-2023-决赛] ezSSTI
Break阶段:
- 使用焚靖工具快速检测SSTI漏洞:
python -m fenjing crack --method GET --inputs name --url 'http://10.1.110.2:20000/'
Fix阶段:
- 加强正则过滤:
if (!re.findall(r"'|_|\\x|\\u|{{|\+|attr|\.| |class|init|globals|popen|system|env|exec|shell_exec|flag|passthru|proc_popen|{|set|\[|\(|%7b|eval|1|2|3|4|5|6|7|8|9", name)):
案例2:[羊城杯-2023-决赛] easyupload
Break阶段:
- 利用Apache解析漏洞:
- 上传文件名:
shell.php.txt - Payload:
1=system('tac /flag.txt');
- 上传文件名:
Fix阶段:
- 黑名单改白名单:
$deny_ext = array(".jpg", ".png", ".jpeg");
- 限制文件名只能有一个点:
if (in_array($file_ext, $deny_ext) && substr_count($_FILES['upload_file']['name'], '.') === 1)
案例3:[羊城杯-2023-决赛] BabyMemo
Break阶段:
- 利用session文件伪造:
- 用户名设为
sess - 文件名格式:
sess_PHPSESSID - 写入内容:
admin|b:1;username|s:4:"sess";memos|a:2:{i:0;s:3:"aaa";i:1;s:3:"aaa";}
- 用户名设为
Fix阶段:
- 禁止用户名为
sess:
if ($_POST['username'] != "sess"){
$_SESSION['username'] = $_POST['username'];
}
案例4:[羊城杯-2023-决赛] fuzee_rce
Break阶段:
- 绕过数字检查(科学计数法):
- Payload:
?w1key=1e9
- Payload:
- 自增RCE:
- Payload:
$%ff=_(%ff/%ff)[%ff];%2b%2b$%ff;$_=$%ff.$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;2b%2b$%ff.%2b%2b$%ff;
- Payload:
\[_[%ff] ``` 传参:`%ff=system&_=tac /flag` #### Fix阶段: - 加强WAF过滤: ```php if (!preg_match("/[1-9a-zA-Z! \" |`~ \\\\ _$%]/", $w1key)) ``` ### 案例5:[羊城杯-2023-决赛] Oh! My PDF #### Break阶段: 1. 伪造JWT(空密钥): - Header:`{"typ": "JWT","alg": "HS256"}` - Payload:`{"username": "admin","isadmin": true}` 2. 利用WeasyPrint漏洞: - 创建HTML文件: ```html ``` - 通过PDF生成器访问该HTML #### Fix阶段: - 加强JWT验证 - 限制PDF生成内容 - 禁止加载HTML文件 - 更换PDF库 ## 七、正则表达式参考 ### PHP正则 ```php // 校验数字 数字:^[0-9]*$ n位数字:^\d{n}$ 至少n位数字:^\d{n,}$ m-n位数字:^\d{m,n}$ // 校验字符 汉字:^[\u4e00-\u9fa5]{0,}$ 英文和数字:^[A-Za-z0-9]+$ 长度为3-20的所有字符:^.{3,20}$ 由26个英文字母组成的字符串:^[A-Za-z]+$ // 特殊需求 Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.? InternetURL:[a-zA-z]+://[^\s]* 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ 身份证号:15或18位身份证:^\d{15}|\d{18}$ ``` ### Python正则 ```python import re # 检查字符串是否包含字母"a" txt = "Hello, world!" match = re.search("a", txt) print(match) # 输出:None,因为"a"没有在字符串中 ``` ### Java正则 ```java \\:将下一字符标记为特殊字符、文本、反向引用或八进制转义符 ^:匹配输入字符串开始的位置 $:匹配输入字符串结尾的位置 [abc]:表示可能是a,可能是b,也可能是c [^abc]:表示不是a,b,c中的任意一个 [a-zA-Z]:表示是英文字母 [0-9]:表示是数字 ``` ## 八、总结 AWDP比赛要求选手同时具备攻击和防御能力,关键在于: 1. **快速识别漏洞**:通过代码审计、黑盒测试等方法快速定位漏洞 2. **有效防御**:根据漏洞类型选择合适的防御方案,WAF是最通用的手段 3. **实战经验积累**:平时积累各种漏洞的利用和防御方法 4. **工具准备**:准备好各种语言的WAF模板和正则表达式库 5. **策略选择**:优先Fix,同时思考Break方案 通过系统学习和实践这些技术,可以显著提升在AWDP比赛中的表现。\]