HackThisSite - JavaScript Mission 3

Challenge

Math time!. The page hands you the checker source in a textarea; the password is a length, not a secret string.

第三关 Math time!:页面直接把校验源码放在一个 textarea 里,密码是一段算出来的长度,不是一个固定字符串。

Solution

用带登录态的会话取关卡页:

1
2
$ curl -s -b 'HackThisSite=<mission-cookie>' \
'https://www.hackthissite.org/missions/javascript/3/'

页面里的 textarea 给出这段源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var foo = 5 + 6 * 7
var bar = foo % 8
var moo = bar * 2
var rar = moo / 3
function check(x)
{
if (x.length == moo)
{
alert("win!");
window.location += "?lvl_password="+x;
}
else
{
alert("Fail D:");
}
}

逐步计算

  • foo = 5 + 6 * 7。乘法优先:6 * 7 = 425 + 42 = 47
  • bar = foo % 847 % 8 = 747 = 5 * 8 + 7)。
  • moo = bar * 27 * 2 = 14
  • rar = moo / 314 / 3 = 4.666…。这一行算完就没被用过,是干扰项:校验里只引用了 moo

用 Node 运行一遍确认:

1
2
$ node -e 'var foo = 5 + 6 * 7, bar = foo % 8, moo = bar * 2, rar = moo / 3; console.log(foo, bar, moo, rar)'
47 7 14 4.666666666666667

校验条件是 x.length == moo,也就是 x.length == 14

为什么这段 JS 能被绕过

  • 关卡没有固定的密码字符串,校验看的是输入的长度moo 完全由源码里的常量算出,任何读到源码的人都能推出 14 这个数字。
  • 这是客户端计算型校验的典型弱点:算法和常量都在浏览器里,逆不逆向都只是照着算一遍。构造任意一个 14 字符的字符串就满足 x.length == moo,比如 aaaaaaaaaaaaaa
  • 放行动作仍是成功分支里的 window.location += "?lvl_password=" + x,把输入串拼进 URL 回跳;也就是说即使不知道正确密码,只要构造一个长度对得上的串就行。
1
2
$ node -e 'function check(x){ return x.length == 14; } console.log(check("aaaaaaaaaaaaaa"))'
true

Submit

提交一个 14 字符的串:

1
2
3
$ curl -s -b 'HackThisSite=<mission-cookie>' \
-e 'https://www.hackthissite.org/missions/javascript/3/' \
'https://www.hackthissite.org/missions/javascript/3/?lvl_password=aaaaaaaaaaaaaa'

服务端靠 Referer 判断请求来自关卡页:不带 Referer 时不计完成,带上关卡页地址后即计入完成。提交后 profile 的 Javascript 列表出现本关完成标记,账号积分 6501 → 6669(七关合计)。

任意 14 字符的字符串