先吐槽一下这次的界面好了。前端确实是随手写的,加了一段 prism.js
做代码高亮;本来想做优雅链接来着,结果 Openshift 上面 Nginx 老是调不好。刚才又调了一下,终于可以了,于是把所有题目的链接都改成了优雅链接(https://hongbao.rexskz.info/{code}
)。
然后在 index.php
的一开始写了如下几句:
if (!preg_match('/^[0-9A-Za-z_]+$/', $_GET['code'])) die('Access denied');
sleep(1);
require_once "{$_GET['code']}.php";
第一句为了防止任意文件包含,第二句是为了防止暴力破解口令,第三句加载对应关卡的变量,然后下面就是将这些变量填充到固定的模板里。下面就看一下具体的题目吧!
Level 1
给个入口好了:https://hongbao.rexskz.info/first
const img = <img src="2.png" />;
const code = hongbao_decode(img);
const curUrl = location.href;
const newUrl = curUrl.replace('first', code);
if (/\d{8}/.test(code)) {
console.log('Wrong!');
}
console.log(`The code is: ${code}`);
console.log(`Next level: ${newUrl}`);
一段自认为还比较标准的 JSX 代码,其中 hongbao_decode
函数显然不是什么系统函数,大概能知道这是让你自己想的。
那么 2.png
是什么东西呢?打开网址 https://hongbao.rexskz.info/2.png
看一下:
看起来是一个二维码被打乱了顺序。纠正之后扫出来是 Li4uLi4gLS4uLi4gLi0tLS0gLS0tLi4gLi4uLi4gLS0tLi4gLi4uLi0gLi4tLS0=
,显然是个 Base64,解密之后是 ..... -.... .---- ---.. ..... ---.. ....- ..---
,显然是个摩斯电码,解密之后是 56185842
,发现刚好符合 /\d{8}/
,因此推测这就是答案,可以通过访问 newUrl
来验证。
Level 2
上来就是一段 PHP,其中 $input
可以发现是一段混淆压缩之后的 JavaScript,尝试着对其解密。注意,因为这是写在 PHP 里的,所以有些字符是转义字符,不能直接复制粘贴运行得出答案。简便的方法是将这一段写成 PHP 然后执行。解密结果是 123467@13457@36@136@136@123467@2346@13457
。然后看到下面的代码是先用 @
打散成数组,然后做一个类似于 LED 的加密,例如 1
对应的是最上面那根横线。这样解密之后得到 92177942
。
Level 3
题目首先要求输入一个满足要求的字符串。限制条件分别为:
- 必须由 1-4 组成,长度为 8;
- 第零位必须比第一位要小;
- 1-4 必须都要出现,并且数字 i 第一次与最后一次出现的位置刚好隔了 i 个字符,也可以推出来 1-4 分别出现了两次。
通过简单的编程枚举,可以确定唯一满足答案的字符串是 23421314
(灵感来源于我初三的时候看到的一篇文章 一个数字问题:23421314,这篇文章里面的代码现在看起来似乎太复杂了一些。发空间链接只是为了秀一下发布日期大家不要在意,这个数字网上也能很容易地搜到相关的文章)。diff
变量是一段 Base64,不断解密之后得到 23220328
,于是答案为 23421314 + 23220328 = 46641642
。
Level 4
题目中什么提示也没有,因此考虑抓包,发现一个请求头:
The-File-You-Must-See: https://hongbao.rexskz.info/_
下载下来之后 file
一下发现是个 .tar.gz
的压缩包,里面有一个 .git
文件夹,还有个 last.sh
,里面写了最后一关的地址是 'https://hongbao.rexskz.info/final_' + code
。发现这个 Git 仓库有好多 commit,于是不断退回到每一个 commit 查看里面的 README.md
(其实只有这一个文件),最后拼起来就是 code
的值 09154542
。
Level 5
首先提示说必须满足 isset($_GET['number']) && preg_match('/^\d{1,10}$/', $_GET['number'])
,下面还贴心地并不给出了满足条件的一个链接 https://hongbao.rexskz.info/final_09154542?number=0
。
然后大家就看到了全蛋男神的语录,并且发现下面的链接中 number
改成了 1,点进去发现是另外一条语录……(完整的语录一共有 100 条,详见文章末尾。)
但是究竟有多少条语录呢?如果闲的无聊可以一条条看,会发现这是个 100 的循环,但是可以每次在后面加个 0,会发现 10000
、100000000
是两个分割点,因此考虑答案可能是这之间的某个数。
# number == 10000 时
$_GET['number'] === 10000
Seems no more fun.
Why not try https://hongbao.rexskz.info/final_09154542?number=10001 ? Maybe the reward is waiting for you!
# number == 100000000 时
$_GET['number'] === 100000000
Number is toooooooooooooooooooooooooooooooooooooo large!
For god's sake, trust me, there's no reward here at all!
Why not try https://hongbao.rexskz.info/final_09154542?number=100000001 ? Alright I'm just kidding.
写一段程序不断二分访问,最终得到那个特殊的 number
是 24361
(灵感来源于爱因斯坦的那个“两打和十九的平方”的梗)。
# number == 100000000 时
Congratulations! But the answer is not the code, you need more answer.
Why not try https://hongbao.rexskz.info/final_09154542?number=24362 ? But this is exactly the answer, it's no use to try again...
Another answer
在下面的倒计时里,然而这个倒计时是到二月底的……查看源代码可以发现:
var timestamp = 1488297599; // 2017-02-28 23:59:59
var seconds;
var a = setInterval(function () {
seconds = parseInt(new Date().valueOf() / 1000);
var diff = timestamp - seconds;
if (diff > 0) {
document.getElementById("countdown").innerHTML = 'Another answer will show in ' + diff + ' seconds...';
} else {
document.getElementById("countdown").innerHTML = 'Another answer is <a href="https://hongbao.rexskz.info/____">THIS</a> !';
clearInterval(a);
}
}, 500);
这都什么破名字……下载下来后发现是个 32 位的 ELF 文件。用 IDA 反编译一下发现其实非常简单:
int getData()
{
int result; // eax@1
int v1; // [sp+8h] [bp-10h]@1
int v2; // [sp+Ch] [bp-Ch]@1
v2 = *MK_FP(__GS__, 20);
sub_4A0("Do you know what year is it: ");
v1 = 0;
sub_4D0("%d", &v1);
result = delta[v1 % 10] + 1323624;
if ( *MK_FP(__GS__, 20) != v2 )
_stack_chk_fail_local(*MK_FP(__GS__, 20) ^ v2);
return result;
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
getData();
sub_4A0("The encrypted data `dat = %d`.\n");
sub_4B8("The code in php is `'0' . (number + dat)`, remember the number?");
sub_4B8("It's not too difficult to get RMB 50!");
return 0;
}
为什么我的 IDA 字体改了呢,因为这一版 Windows 里面的 Fixedsys 字体是乱码……顺便我已经懒到代码格式都不想改了……
main
函数很简单,下面那三个一看就是 printf
,然后输出的数字应该就是 getData
的返回值,于是重点看到这个函数。其中 v2
在逻辑上来看没啥意义,唯一有意义的是 delta[v1 % 10] + 1323624
,v1
又是 scanf
得出来的,因此应该是 2017
,返回值为 delta[7] + 1323624
。双击 delta
查看里面的数据,既然是 int 那么每一位应该占四个字节。想方设法转换成如下的形式:
.data:00002020 ; int delta[]
.data:00002020 delta dd 435
.data:00002024 dd 654
.data:00002028 dd 67
.data:0000202C dd 54643
.data:00002030 dd 5425
.data:00002034 dd 46
.data:00002038 dd 564
.data:0000203C dd 6457
.data:00002040 dd 657
.data:00002044 dd 56
所以返回值是 6457 + 1323624 = 1330081
,然后看最后的输出,结果应该是 '0' + (24361 + 1330081) = '01354442'
。只可惜最后没人拿到这个五十元的红包。