前言
前不久,我打了一些规格比较高的比赛,发现对于web的awdp,能做fix的同学还是比较多的,能break的却人少之又少,这个很正常,毕竟fix方法很多,但是break很可能只有一种办法。
尽管如此,还是有很多同学对于fix毫无办法,结果awdp只能爆零离场,十分落寞。下面我整理了web fix的一些学习文档,本人比赛经历有限,仅供参考。
如果你想要pdf形式文档,以便于比赛使用,可以后台回复webfix
获取文档
1. Web方向漏洞修复概述
在CTF AWD/AWDP比赛中,Web方向的fix patch是非常关键的环节。AWDP模式(Attack,Defense,WebandPwn)分为Break与Fix环节,参赛队员需要在发现漏洞后及时修复自己服务器上的漏洞,防止被攻击方获取flag。
1.1 AWDP赛制特点
AWDP是一种综合考核参赛团队攻击、防御技术能力、即时策略的攻防兼备比赛模式。每个参赛队互为攻击方和防守方,充分体现比赛的实战性、实时性和对抗性。
-
• 每个战队拥有相同的起始分数及相同配置的虚拟靶机 -
• 参赛队员需对平台中的GameBox发起攻击,向平台提交正确的flag -
• 平台以轮次制的方式向参赛战队的靶机发起攻击,检查漏洞是否修补成功 -
• 若修补成功则认为参赛战队具备该漏洞的防御能力
1.2 Fix修复基本原则
-
1. 最小修改原则:只修改必要的代码,避免引入新的问题 -
2. 功能保持原则:修复漏洞的同时保证原有功能正常运行 -
3. 快速响应原则:在比赛中,修复速度往往比修复质量更重要 -
4. 防御深度原则:采用多层防御策略,不仅修复漏洞,还要增加防御措施
2. 常见Web漏洞类型及修复方法
2.1 SQL注入漏洞修复
SQL注入是最常见的Web漏洞之一,攻击者通过在输入中插入SQL语句来操纵数据库。
2.1.1 PHP中的SQL注入修复
// 不安全的代码$query = "SELECT * FROM users WHERE username = '" . $_GET['username'] . "'";// 修复方法1:使用参数化查询$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");$stmt->execute([$_GET['username']]);// 修复方法2:使用过滤函数functionwafsqli($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);}if (!wafsqli($_GET['username'])) {die('SQL injection detected!');}
2.1.2 其他语言中的SQL注入修复
Python (Flask/SQLAlchemy):
# 不安全的代码query = "SELECT * FROM users WHERE username = '" + username + "'"# 修复方法:使用ORM或参数化查询user = User.query.filter_by(username=username).first()# 或cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
Node.js:
// 不安全的代码const query = `SELECT * FROM users WHERE username = '${username}'`;// 修复方法:使用参数化查询const query = 'SELECT * FROM users WHERE username = ?';connection.query(query, [username], function(error, results, fields) {// 处理结果});
2.2 XSS跨站脚本漏洞修复
XSS允许攻击者在网页中注入客户端脚本,从而获取用户信息或执行恶意操作。
2.2.1 PHP中的XSS修复
// 不安全的代码echo"Welcome, " . $_GET['name'];// 修复方法1:使用htmlspecialcharsecho"Welcome, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');// 修复方法2:使用过滤函数functionwafxss($str) {return !preg_match("/'|http|"|`|cookie|<|>|script/i", $str);}if (!wafxss($_GET['name'])) {die('XSS detected!');}
2.2.2 其他语言中的XSS修复
Python (Flask):
# 不安全的代码@app.route('/welcome')defwelcome():returnf"Welcome, {request.args.get('name')}"# 修复方法:使用模板引擎自动转义@app.route('/welcome')defwelcome():return render_template('welcome.html', name=request.args.get('name'))# 在模板中,Jinja2会自动转义变量
Node.js (Express):
// 不安全的代码app.get('/welcome', (req, res) => { res.send(`Welcome, ${req.query.name}`);});// 修复方法:使用转义函数constescapeHtml = (unsafe) => {return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'");};app.get('/welcome', (req, res) => { res.send(`Welcome, ${escapeHtml(req.query.name)}`);});
2.3 文件包含漏洞修复
文件包含漏洞允许攻击者包含恶意文件,从而执行未授权的代码。
2.3.1 PHP中的文件包含漏洞修复
// 不安全的代码include($_GET['page'] . '.php');// 修复方法1:白名单验证$allowed_pages = ['home', 'about', 'contact'];$page = $_GET['page'];if (in_array($page, $allowed_pages)) {include($page . '.php');} else {include('home.php');}// 修复方法2:过滤危险字符$page = str_replace(['../', '..\', ':', ';', '&', '|'], '', $_GET['page']);include($page . '.php');
2.4 命令注入漏洞修复
命令注入允许攻击者执行系统命令,可能导致服务器被完全控制。
2.4.1 PHP中的命令注入修复
// 不安全的代码system("ping " . $_GET['ip']);// 修复方法1:使用escapeshellargsystem("ping " . escapeshellarg($_GET['ip']));// 修复方法2:使用过滤函数functionwafrce($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);}if (!wafrce($_GET['ip'])) {die('Command injection detected!');}
2.4.2 其他语言中的命令注入修复
Python:
# 不安全的代码import osos.system("ping " + ip)# 修复方法:使用参数列表而非字符串import subprocesssubprocess.run(["ping", ip], check=True)
Node.js:
// 不安全的代码const { exec } = require('child_process');exec('ping ' + ip);// 修复方法:使用execFile并传递参数数组const { execFile } = require('child_process');execFile('ping', [ip], (error, stdout, stderr) => {// 处理结果});
2.5 CSRF跨站请求伪造漏洞修复
CSRF允许攻击者诱导用户执行非预期的操作,通常是在用户已认证的情况下。
2.5.1 PHP中的CSRF修复
// 生成CSRF令牌session_start();if (empty($_SESSION['csrf_token'])) {$_SESSION['csrf_token'] = bin2hex(random_bytes(32));}// 在表单中包含令牌echo'<form method="post">';echo'<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';echo'<!-- 其他表单字段 -->';echo'</form>';// 验证令牌if ($_SERVER['REQUEST_METHOD'] === 'POST') {if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {die('CSRF token validation failed'); }// 处理表单提交}
2.6 文件上传漏洞修复
文件上传漏洞允许攻击者上传恶意文件,如WebShell,从而获取服务器控制权。
2.6.1 PHP中的文件上传漏洞修复
// 不安全的代码move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);// 修复方法1:验证文件类型和扩展名$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];$file_type = $_FILES['file']['type'];$file_extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));if (!in_array($file_type, $allowed_types) || !in_array($file_extension, $allowed_extensions)) {die('Invalid file type');}// 修复方法2:使用随机文件名$new_filename = md5(uniqid(rand(), true)) . '.' . $file_extension;move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $new_filename);// 修复方法3:检查文件内容$file_content = file_get_contents($_FILES['file']['tmp_name']);if (preg_match('/<?php|<script|<%|eval(|system(|exec(|passthru(|shell_exec(/', $file_content)) {die('Potentially malicious file detected');}
3. Web应用通用防御措施(慎用)
3.1 WAF (Web应用防火墙)
在AWD/AWDP比赛中,部署WAF是一种常见且有效的防御措施。
3.1.1 PHP WAF示例
<?phperror_reporting(0);define('LOG_FILENAME', 'waf.log');functionwaf() {if (!function_exists('getallheaders')) {functiongetallheaders() {foreach ($_SERVERas$name => $value) {if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; }return$headers; } }$get = $_GET;$post = $_POST;$cookie = $_COOKIE;$header = getallheaders();$files = $_FILES;$ip = $_SERVER["REMOTE_ADDR"];$method = $_SERVER['REQUEST_METHOD'];$filepath = $_SERVER["SCRIPT_NAME"];//rewirte shell which uploaded by others, you can do moreforeach ($_FILESas$key => $value) {$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);file_put_contents($_FILES[$key]['tmp_name'], "virink"); }unset($header['Accept']); //fix a bug$input = array("Get" => $get,"Post" => $post,"Cookie" => $cookie,"File" => $files,"Header" => $header );//attack detectforeach ($inputas$k => $v) {foreach ($vas$kk => $vv) {if (is_array($vv)) {foreach ($vvas$kkk => $vvv) {if (is_array($vvv)) {foreach ($vvvas$kkkk => $vvvv) {$input[$k][$kk][$kkk][$kkkk] = filter_attack_keyword($vvvv); } } else {$input[$k][$kk][$kkk] = filter_attack_keyword($vvv); } } } else {$input[$k][$kk] = filter_attack_keyword($vv); } } }//filter attack keywordfunctionfilter_attack_keyword($keyword) {if (is_array($keyword)) {return$keyword; }$keyword = addslashes($keyword);$blacklist = array(// SQL注入'select', 'union', 'and', 'or', 'from', 'where', 'join', 'having', 'group', 'insert', 'update', 'delete', 'drop', 'alter', 'table',// 命令注入'eval', 'exec', 'system', 'shell_exec', 'passthru', 'proc_open', 'popen', '`', 'assert',// 文件包含'include', 'require', 'include_once', 'require_once', 'file_get_contents', 'file_put_contents', 'fopen', 'readfile',// XSS'script', 'alert', 'onerror', 'onclick', 'onload', 'onfocus', 'onblur', 'onchange', 'onsubmit' );foreach ($blacklistas$word) {if (stripos($keyword, $word) !== false) {write_attack_log("Detected attack keyword: " . $word . " in " . $keyword);// 可以选择直接终止程序// die("Attack detected!");returnstr_ireplace($word, '***', $keyword); } }return$keyword; }//write attack logfunctionwrite_attack_log($log) {$log = "[" . date("Y-m-d H:i:s") . "] " . $log . "n";file_put_contents(LOG_FILENAME, $log, FILE_APPEND); }return$input;}// 使用WAF$input = waf();$_GET = $input['Get'];$_POST = $input['Post'];$_COOKIE = $input['Cookie'];$_FILES = $input['File'];// 继续正常的程序逻辑?>
3.2 文件监控脚本
在AWD/AWDP比赛中,监控文件变化是防御的重要手段,可以及时发现WebShell等恶意文件。
3.2.1 Linux文件监控脚本示例
#!/usr/bin/env python# -*- coding: utf-8 -*-import osimport timeimport hashlibimport logging# 配置日志logging.basicConfig( level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s', filename='file_monitor.log', filemode='a')# 需要监控的目录MONITOR_DIR = '/var/www/html'# 文件哈希值缓存file_hash_cache = {}# 忽略的文件扩展名IGNORE_EXTENSIONS = ['.log', '.txt', '.bak']# 忽略的目录IGNORE_DIRS = ['.git', 'logs', 'cache']defget_file_hash(filepath):"""计算文件的MD5哈希值"""try:withopen(filepath, 'rb') as f:return hashlib.md5(f.read()).hexdigest()except Exception as e: logging.error(f"Error calculating hash for {filepath}: {e}")returnNonedefscan_directory():"""扫描目录并检查文件变化"""for root, dirs, files in os.walk(MONITOR_DIR):# 跳过忽略的目录 dirs[:] = [d for d in dirs if d notin IGNORE_DIRS]for filename in files:# 跳过忽略的文件类型ifany(filename.endswith(ext) for ext in IGNORE_EXTENSIONS):continue filepath = os.path.join(root, filename) current_hash = get_file_hash(filepath)if current_hash isNone:continueif filepath in file_hash_cache:# 文件已存在,检查是否被修改if file_hash_cache[filepath] != current_hash: logging.warning(f"File modified: {filepath}")# 可以在这里添加恢复文件的逻辑# restore_file(filepath)else:# 新文件 logging.info(f"New file detected: {filepath}")# 更新缓存 file_hash_cache[filepath] = current_hashdefinitialize_cache():"""初始化文件哈希缓存""" logging.info("Initializing file hash cache...") scan_directory() logging.info(f"Initialized {len(file_hash_cache)} files in cache")defrestore_file(filepath):"""从备份恢复文件(示例)""" backup_path = filepath + '.bak'if os.path.exists(backup_path):try:withopen(backup_path, 'rb') as src, open(filepath, 'wb') as dst: dst.write(src.read()) logging.info(f"Restored file from backup: {filepath}")except Exception as e: logging.error(f"Error restoring file {filepath}: {e}")defmain():"""主函数""" logging.info("File monitoring started") initialize_cache()try:whileTrue: scan_directory() time.sleep(5) # 每5秒扫描一次except KeyboardInterrupt: logging.info("File monitoring stopped")if __name__ == "__main__": main()
3.3 检测外来IP脚本
在AWD/AWDP比赛中,监控网络连接可以帮助发现攻击行为。
#!/bin/bash# 日志文件LOG_FILE="/var/log/ip_monitor.log"# 允许的IP列表(比赛组织方和队友的IP)ALLOWED_IPS=("10.0.0.1""10.0.0.2""10.0.0.3")# 检查日志文件是否存在,不存在则创建if [ ! -f "$LOG_FILE" ]; thentouch"$LOG_FILE"echo"$(date): IP监控日志创建" >> "$LOG_FILE"fi# 获取当前连接的IPCURRENT_IPS=$(netstat -tn | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort | uniq)# 检查每个IPfor IP in$CURRENT_IPS; do# 检查IP是否在允许列表中if [[ ! " ${ALLOWED_IPS[@]} " =~ " ${IP} " ]]; thenecho"$(date): 检测到未授权IP连接: $IP" >> "$LOG_FILE"# 可选:阻止该IP(谨慎使用,可能会阻止比赛平台)# iptables -A INPUT -s $IP -j DROP# echo "$(date): 已阻止IP: $IP" >> "$LOG_FILE"# 可选:获取该IP的连接详情 CONNECTIONS=$(netstat -tn | grep $IP)echo"$(date): IP $IP 的连接详情:" >> "$LOG_FILE"echo"$CONNECTIONS" >> "$LOG_FILE"fidoneecho"$(date): IP检查完成" >> "$LOG_FILE"
4. 不同语言的防御代码片段
4.1 PHP防御代码片段
// RCE防御functionwafrce($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注入防御functionwafsqli($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防御functionwafxss($str) {return !preg_match("/'|http|"|`|cookie|<|>|script/i", $str);}// URL参数检查if (preg_match('/system|tail|flag|exec|base64/i', $_SERVER['REQUEST_URI'])) {die('no!');}
4.2 Python防御代码片段
# 黑名单过滤filter_list = ["apple", "banana", "cherry"]strings = "ana"# 匹配包含"ana"的字符串for i in filter_list:if i in strings:print("Hacker!")# Flask应用中的防御from flask import Flask, request, render_template_string, abortapp = Flask(__name__)@app.before_requestdefcheck_security():# 检查请求参数for key, value in request.args.items():if check_dangerous_input(value): abort(403) # 拒绝请求# 检查POST数据for key, value in request.form.items():if check_dangerous_input(value): abort(403) # 拒绝请求defcheck_dangerous_input(value): dangerous_patterns = ['eval(', 'exec(', 'import', 'os.', 'sys.', 'subprocess','SELECT', 'UNION', 'INSERT', 'UPDATE', 'DELETE', 'DROP','<script', 'javascript:', 'onerror=', 'onclick=' ]ifisinstance(value, str):for pattern in dangerous_patterns:if pattern.lower() in value.lower():returnTruereturnFalse
4.3 Node.js防御代码片段
const keywords = ["apple", "banana", "cherry"];for (const i of keywords) {if (code.includes(i)) {console.log("Hacker!") }}// Express应用中的防御中间件const express = require('express');const app = express();// 安全中间件app.use((req, res, next) => {// 检查查询参数for (const key in req.query) {if (checkDangerousInput(req.query[key])) {return res.status(403).send('Forbidden'); } }// 检查请求体for (const key in req.body) {if (checkDangerousInput(req.body[key])) {return res.status(403).send('Forbidden'); } }next();});functioncheckDangerousInput(value) {const dangerousPatterns = ['eval(', 'exec(', 'require(', 'process.', 'child_process','SELECT', 'UNION', 'INSERT', 'UPDATE', 'DELETE', 'DROP','<script', 'javascript:', 'onerror=', 'onclick=' ];if (typeof value === 'string') {for (const pattern of dangerousPatterns) {if (value.toLowerCase().includes(pattern.toLowerCase())) {returntrue; } } }returnfalse;}
4.4 Java防御代码片段
String[] filterList = {"apple", "banana", "cherry"};Stringstr="ana"; // 匹配包含"ana"的字符串for (String s : filterList) {if (s.contains(str)) { System.out.println("Hacker!"); }}// Spring Boot应用中的过滤器import org.springframework.stereotype.Component;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Arrays;import java.util.List;import java.util.Map;@ComponentpublicclassSecurityFilterimplementsFilter {privatestaticfinal List<String> DANGEROUS_PATTERNS = Arrays.asList("eval(", "exec(", "Runtime.getRuntime()", "ProcessBuilder","SELECT", "UNION", "INSERT", "UPDATE", "DELETE", "DROP","<script", "javascript:", "onerror=", "onclick=" );@OverridepublicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequesthttpRequest= (HttpServletRequest) request;HttpServletResponsehttpResponse= (HttpServletResponse) response;// 检查查询参数 Map<String, String[]> parameterMap = httpRequest.getParameterMap();for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {for (String value : entry.getValue()) {if (checkDangerousInput(value)) { httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden");return; } } } chain.doFilter(request, response); }privatebooleancheckDangerousInput(String value) {if (value != null) {StringlowerValue= value.toLowerCase();for (String pattern : DANGEROUS_PATTERNS) {if (lowerValue.contains(pattern.toLowerCase())) {returntrue; } } }returnfalse; }}
5. 克制不死马技术
在AWD/AWDP比赛中,不死马是一种常见的攻击手段,它会不断地生成WebShell,即使被删除也会重新生成。
5.1 PHP不死马示例
<?phpset_time_limit(0);ignore_user_abort(1);unlink(__FILE__);while(1) {file_put_contents('shell.php', '<?php eval($_POST[cmd]); ?>');usleep(100);}?>
5.2 克制不死马的方法
-
1. 修改文件权限:将Web目录设为只读 chmod -R 555 /var/www/html
-
2. 使用inotify监控文件变化: apt-get install inotify-toolsinotifywait -m -r /var/www/html -e create -e modify |whileread path action file; doif [[ "$file" == *.php ]]; thenecho"Detected change in $path$file"rm -f "$path$file"fidone
-
3. 使用PHP禁用危险函数:在php.ini中禁用可能被用于不死马的函数 disable_functions = system,exec,shell_exec,passthru,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fwrite,file_get_contents
-
4. 使用WAF拦截不死马请求:配置WAF规则拦截可能的不死马请求 -
5. 定期清理可疑文件: find /var/www/html -name "*.php" -mmin -5 -execrm -f {} ;
6. 比赛实战技巧
6.1 快速修复流程
-
1. 备份源码:首先备份原始源码,以便在修复出错时恢复 tar -cvf web.tar /var/www/htmlcp web.tar /tmp/backup/
-
2. 代码审计:快速审计代码,找出可能的漏洞点 -
3. 应用修复:根据漏洞类型应用相应的修复方法 -
4. 测试功能:确保修复后的功能正常运行 -
5. 部署防御:部署WAF、文件监控等防御措施
6.2 通防策略
-
1. 部署WAF:使用通用WAF脚本拦截常见攻击 -
2. 修改默认密码:修改所有服务的默认密码 # 修改MySQL密码mysql -u root -pSET PASSWORD FOR 'root'@'localhost' = PASSWORD('NewStrongPassword');# 修改SSH密码passwd
-
3. 限制文件权限:限制Web目录的写入权限 chmod -R 555 /var/www/htmlchmod 777 /var/www/html/uploads # 如果需要上传功能
-
4. 监控系统:部署文件监控和网络监控脚本 -
5. 关闭不必要的服务:减少攻击面 systemctl stop service_namesystemctl disable service_name
7. 总结与最佳实践
7.1 Web方向Fix Patch最佳实践
-
1. 最小修改原则:只修改必要的代码,避免引入新问题 -
2. 多层防御:不仅修复漏洞,还要部署WAF等防御措施 -
3. 自动化工具:使用自动化工具加速修复过程 -
4. 团队协作:明确分工,提高修复效率 -
5. 持续监控:部署监控系统,及时发现新问题
7.2 常见错误与避免方法
-
1. 过度修改:修改过多可能导致功能失效或引入新漏洞 -
2. 忽略测试:修复后未测试功能是否正常 -
3. 单点防御:只依赖单一防御措施 -
4. 忽略日志:未查看日志,错过攻击线索 -
5. 权限过大:Web目录权限过大,允许写入操作
原文始发于微信公众号(Zacarx随笔):CTF AWD/AWDP Web方向 Fix Patch 学习文档
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论