xss challenge--正则,一生之敌!

1 背景

在推特上看到一个 XSS chanllege,凭借我shi一样的代码审计能力,当然是没有做出来,时间都花在分析正则表达式上了,唉。看了答案之后,其实也并不很难,好多点我也注意到了,比方说参数targetUrl 被提取了两次,但我把 [?|&] 理解错了,所以一直找不到利用方式(还得多练emmm)

2 描述

打开网页后,映入眼帘的是一个重定向,参数名为 targetUrl。不说这是个 XSS 题,我还以为要 SSRF 呢。

3 分析

首先依旧是看源码(如下),总共有两个函数 getQuerystring()queryPass(),主函数为后者。有两种方式调用 queryPass() 函数,一种是 setTimeout,打开页面五秒后自动调用;一种是点击按钮 “Go go go” 后调用。而 queryPass() 只干了一件事,将 location.href 设置为 getQuerystring() 的返回值。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
setTimeout(function () {
queryPass();
}, 5000);

function getQuerystring(key, default_) {
var regex =
/^((([A-Za-z]{4,5}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
var url = getURLParameter("targetUrl");
var urlParts = url
.replace("http://", "")
.replace("https://", "")
.split(/[/?#]/);
var domain = urlParts[0];
function getURLParameter(name) {
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
location.search
) || [null, ""])[1].replace(/\+/g, "%20")
) || null
);
}
if (
domain.indexOf("fransrosen.com") == -1 &&
domain.indexOf("fransrosen.se") == -1
) {
url = "https://www.fransrosen.com";
window.location.href = url;
} else if (
!(
url &&
(url.indexOf("fransrosen.com") > -1 ||
url.indexOf("fransrosen.se") > -1) &&
regex.test(url)
)
) {
url = "https://www.fransrosen.com";
window.location.href = url;
} else {
if (default_ == null) default_ = "";
key = key.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + key + "=([^\0]*)");
var qs = regex.exec(window.location.href);
retVal = qs == null ? default_ : qs[1] == "" ? "" : qs[1];
return retVal;
}
}

document.getElementById("url-continue").addEventListener("click", function (e) {
e.preventDefault();
queryPass();
});

function queryPass() {
if (getQuerystring("targetUrl")) {
location.href = unescape(getQuerystring("targetUrl"));
}
}

getQuerystring() 函数开始就是一个正则 /^((([A-Za-z]{4,5}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/,不过用不用得到分析一下,权当练手了。

结构化展示如下,用来匹配 URL 的。

1
2
3
4
5
6
7
8
9
10
11
12
13
^(
(
([A-Za-z]{4,5}:(?:\/\/)?)
(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+
|
(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+
)
(
(?:\/[\+~%\/.\w-_]*)?
\???:[-\+=&;%@.\w_]*)
#?(?:[\w]*)
)?
)$
  • ([A-Za-z]{4,5}:(?:\/\/)?) 匹配 URL 的协议部分,如:https://, ftp:,(?:...) 表示非捕获组,匹配的内容不会被保存,不影响分组的编号
  • (?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+ 匹配用户信息前缀(HTTP身份验证)+主机名,如: expected-host:fakepassword@evil-host(想到橘师傅那篇 SSRF 的文章了…)
  • (?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+ 匹配无协议头(scheme) 的 url
  • (?:\/[\+~%\/.\w-_]*)? 匹配路径,以 / 打头,如: /my+path, /users/~john/documents, /encoded%20value, /path/to/file.html
  • \??(?:[-\+=&;%@.\w_]*) 匹配查询参数,以 ? 打头
  • #?(?:[\w]*) 匹配片段标识符,hash #

这有个工具 Regexper 可以帮助分析正则表达式。怎么样?是不是一目了然?!看到这张图的时候,我感觉分析了这个正则好长好长时间的我真纯纯🤡 emmm

既然是 XSS,又要传 URL,显然应该用 javascript: 伪协议执行 js 代码。上图红框框住的这条路径允许使用 以内的字符,故可以传入 javascript:,但只有这些个字符如何执行任意 js 代码呢?后面还有个 @ 符号,没法用 // 把它注释掉。

如何只使用 -;:&=+$,\w 中的字符执行 js?

搞不懂 (个_个)

再次查看源码,发现参数 targetUrl 被提取了两次:一次是在 getURLParameter 获取参数的时候,用以验证输入有效性;一次是在 else 条件分支使用参数的时候,用来执行具体操作。应该可以算 TOCTOU (time of check, time of use)。

由此形成思路,构造的 payload 需要绕过验证顺利进入 else 分支,进入条件为:1. url 不为空;2. domain 部分包含 fransrosen.com 或 fransrosen.se;3. regex.test(url) 为 true。之后,在 else 分支中重新提取参数信息,返回值赋给 location.href,从而执行任意 js 代码。

注意两次提取参数的方式也有所不同,第一次在getURLParameter() 函数中,使用的正则是 "[?|&]targetUrl=([^&;]+?)(&|#|;|$) 其中 [?|&] 中的 | 并不是“或”的意思,而是原本的 “|” 号,匹配的是三种字符 ?|& 及其组合。

第二次在 else 分支中,使用的是 [\\?&]targetUrl=([^\0]*)。不难发现,对于 ?|targetUrl=...,第一次能匹配到参数的值,但第二个正则匹配不到,因为 targetUrl 前面是 |,而不是 ? 或 &。

所以,如果我们提供两个 targetUrl 参数,?|targetUrl&targetUrl,这两个正则会分别匹配到不同的参数值,正则一由于未使用修饰符 g,仅返回第一个匹配项,故只能匹配到 ?|targetUrl,用来绕过验证;正则二匹配到 &targetUrl 的值,用来将恶意 js 赋值给 location.href

构造 payload: ?|targetUrl=www.fransrosen.com&targetUrl=javascript:alert(0)


PS: 赶紧学习正则表达式,再不学就自己会了。。。。。

4 参考

  1. XSS-challenge
  2. http://regexper.com