ohroy / blog

A super blog lite -- just one page. use vue with github api !
https://blog.6h.work
Do What The F*ck You Want To Public License
147 stars 15 forks source link

sf双十一解密答案及详解及分析思路 #8

Open ohroy opened 8 years ago

ohroy commented 8 years ago

前言

今天上班百无聊赖的在群里发现一个有趣的链接光棍节程序员闯关秀,点开之后浑身颤抖如获至宝啊。 我最喜欢这种挑战了。 花了一个小时的时间,终于全部解密。下面奉上思路和分析以及代码。由于sf良好的前端氛围,这里全部用JavaScript作为工具语言。 喜欢python的朋友可以参见我另一篇用python作为示例语言的解密一个有意思的解密 话不多说,时间宝贵,我们立马开始解密之旅吧:)

第一关

光棍节程序员闯关秀第1关
1a 作为一个web入门的学员,我们自然而然的直接右键查看源文件:


<html>
<head><title>光棍节程序员闯关秀第1关(总共10关)</title></head>
<body style="background: #172024; color: #54BA3E; font: 100%/1.5 Menlo, Consolas, Courier, monospace; text-align: center; padding: 10% 0 0 0">
<h2>光棍节程序员闯关秀第1关(总共10关)</h2>
<u>提示: 从所有信息中找到进入下一关的方法</u>
<p><a style="color: #172024" href="?k=e70030d49158de95087eae6469f5319e">进入下一关</a></p> 
</body>
</html>

而实际上,我们不查看源文件也是可以的,直接在页面上ctrl+a,就可以让进入下一关的链接变蓝,直接点击即可。

第二关

光棍节程序员闯关秀第2关
这一关同上,我们也是先直接右键查看源文件,幸运的是,它写在了注释里。

<html>
<head><title>光棍节程序员闯关秀第2关(总共10关)</title></head>
<body style="background: #172024; color: #54BA3E; font: 100%/1.5 Menlo, Consolas, Courier, monospace; text-align: center; padding: 10% 0 0 0">
<h2>光棍节程序员闯关秀第2关(总共10关)</h2>
<!-- 不错嘛,密码在此:4c29dbaf326fe76232390dac0917e921 -->
<!-- 强插广告: 欢迎访问 http://segmentfault.com 或者 http://sf.gg -->
<p>密码在哪呢?</p>
<form><input autocomplete="off" placeholder="输入密码" name="k" /></form>
<p><a style="color: #172024" href="javascript:alert('你太天真了');">进入下一关</a></p> 
</body>
</html>

我们复制出密码,然后填在上面网址的k=后面,k参数应该是代表key,也就是密码的意思。在以后的几关里我们都是用这种方法来完成跳转。

第三关

光棍节程序员闯关秀第3关

页面上说,这关就没有那么简单了。显而易见的,我们还是查看源代码,然而他们这次没有给我们任何提示,一无所获。 这一关才开始登堂入室了,我们开动大脑想一想,这个密码会藏在哪里呢? 开动脑洞分析吧:

  1. cookies
  2. Storage
  3. css
  4. http头
  5. console
  6. ...

我们一项一项,最终在http头里找到了疑似密码的字段:

Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Fri, 11 Nov 2016 05:23:01 GMT
The-Key-Is:a87ff679a2f3e71d9181a67b7542122c
Transfer-Encoding:chunked
X-Hit:sf-web1

3 图中部分即时我们要的key。 输入网址中,我们即可来到第四关。

第四关

光棍节程序员闯关秀第4关

这一关让我们观察我们密码的规律。
实际上不用他说我们已经发现,这些密码都是32位的,非常像一个md5有木有? 那么规律到底是什么呢?我记得上小学的时候就经常有这种找规律的题,我们先推断一下,规律可能是:

  1. 当前的md5是上一个md5的结果
  2. md5是某个有规律的数的结果

我们分别测试最后我们发现

md5("4");//a87ff679a2f3e71d9181a67b7542122c

那显然易见的是,下一关5是

md5("5");//e4da3b7fbbce2345d7772b0674a318d5

其实如果各种尝试都无法猜到规律的话,还有一个撞运气的做法,就是我们去md5解密的网站上试一下,a87ff679a2f3e71d9181a67b7542122c 的结果为4。也可以得到相同的结论。
但是这种方法只是一种碰运气无奈之举,因为md5是一种校验算法,已经破坏了数据的原始结构,再不可能还原成原来的结果。
所谓"解密"就是穷举法,自己用md5分别加密常见的字符串再将结果以key=value的字典方式保存到数据里,然后等用的时候再从这里面查找, 看是否有已经碰撞出结果。具有很大的偶然性。

第五关

光棍节程序员闯关秀第5关
这一关开始,就变得比较难了起来。 首先看到一个二维码,我相信大多数人都和我一样,先扫为快。但是手机扫描二维码非常耽误我们时间,而且不好分析。 我们百度搜索在线二维码解析,然后传上去这个图片,结果竟然是:...

http://sf.gg/你被耍了什么都没有

我了个去,我还不信邪,分别测试了

md5("http://sf.gg/你被耍了什么都没有")
md5("你被耍了什么都没有")

然后又按照第三关的步骤检查了一遍还是一无所获。 看来玄机确实就在这个图片本身上,我们下载这个图片,然后右键,详情,看看密码会不会在这些字段里。 尴尬的是里面竟然什么都没有... 等等,什么都没有?说明这个头片显然是以一种非正常方式生成的。我们用十六进制的方式打开它。
此类工具有很多,此处我使用的是WinHex,发现里面有个字符串:

KEY:bdbf46a337ac08e6b4677c2826519542

它是ANSII编码的,所以能被直观的看到。也就是说,其实我们用系统自带的记事本可以看到这个字符串的。
根本不需要WinHex之类的工具。然而我这里主要要表达的是一种分析思路。如果它这里是用的unicode编码,或者含有中文时用的utf8编码, 用记事本就不一定有效了。 还是建议大家掌握更多的诀窍和思路,结果并不重要。通关也不是目的,而是在这个过程中学到了什么东西。

第六关

光棍节程序员闯关秀第6关

f4de502e58723e6252e8856d4dc8fc3b, 只能告诉你这么多

我们同样的用第三关的步骤检查一遍,结果并没有得到有用的信息。 看来玄机就在这个字符串上了。。我们还是老样子,去解密下这个md5,然而这次就没有那么幸运了。没有能找到对应的明文。
实在没办法了,我们只能利用强大的搜索引擎了。。。。 坑爹的是我们竟然找到了这个,第一条:

6

看来是他防水给我们通过了,好吧,虽然我到现在都没弄明白这个key是怎么算出来的。。。

第七关

光棍节程序员闯关秀第7关

有问题就Google是个好习惯! 再试试 ba9b101dd284c566b78042d278e422bd

好吧,看来上题本意就是让我们谷歌啊。好吧,我们就按照他说的,继续谷歌ba9b101dd284c566b78042d278e422bd。 然而时间过的很快夜幕就要降临,我们还是没有找到有用的信息。 好吧,我们注意下,再试试后面有个空格,有没有可能不是让我们试后面的关键词的呢,那后面的关键词又是什么?

第八关

光棍节程序员闯关秀第8关

上一关让我们学习到了听话并不是一个好习惯。举一反三桀骜不驯多么重要,当年孙悟空要是没明白那三下,说不定我们现在还是唐朝。。 这一关他说

有时候事情就是这么简单 钥匙就在手里, 门却不知所踪

我放佛看到了他嘴角嘲弄的笑。嗯,忍了。 我们同样查看源文件,

<html>
<head><title>光棍节程序员闯关秀第8关(总共10关)</title></head>
<body style="background: #172024; color: #54BA3E; font: 100%/1.5 Menlo, Consolas, Courier, monospace; text-align: center; padding: 10% 0 0 0">
<h2>光棍节程序员闯关秀第8关(总共10关)</h2>
<p>有时候事情就是这么简单</p>
<p>钥匙就在手里, 门却不知所踪</p>
<form method="GET">
<input type="text" name="k" value="f57c633b47691a70744a04128e491c32" />
</form> 
</body>
</html>

一看我们就笑了。

狗子不要搞事 那么大的GET,你当我傻啊。我们把这个GET改成POST,回车一下,轻松过关。

第九关

光棍节程序员闯关秀第9关 嚯,这一关厉害了。目测大多数人要栽了。这一关也是最难的一关,也是我要写本文的目的。
对于一个普通的web前端来说可能是有些难了,因为这里面牵涉到很多其它的知识。幸而我不是一个前端。
6 然而我已经看穿了一切!
首先 0 1 0 1 这种的明显是组二进制,下面有缺失的部分,我们先不管,我们先看看前面几个,打开计算器,把这些二进制转几个到十进制看看。 发现它们全是位于ascii表的可见字符范围。

32-126(共95个)是字符(32是空格),其中48-57为0到9十个阿拉伯数字。 65-90为26个大写英文字母,97-122号为26个小写英文字母,其余为一些标点符号、运算符号等。

大学c语言入门课程,大家要记牢,以后用的地方很多。 我们试着译出前面不缺失的这部分,结果为

q6GDLaJ4yq9A7xFAnxyvsc/AT

我去,这什么鬼,这么长的部分明显不是key,那是什么呢?而且乱七八糟的像个密文。假使它是个密文,它又是什么加密的? 我们注意到中间有个/,我们所知的BASE64码中间可以出现\,并且根据base64的原理,它最后加密结果一定能被4整除。 我们看了下,共有8112组2进制数据,是个能被4整除的数。同样的,据其原理,如果原字符串的长度不足,可能要在最后补一到两个等号。 有了以上这些线索,我们直接去最后一个二进制看看是不是等号。
然而最后一个却是00____01,很明显是需要我们补齐中间四位的。看来这就是算____所代表数的契机啊。看来我们的路子走对了!!
下面我们在console上执行:

let code ="=".charCodeAt();
console.log(code);//61
console.log(code.toString(2));
VM335:2 61
VM335:3 111101

由此可知____代表的应该是1111。哈哈,到时符合主题:双十一。
由此,我们已经嗅到了胜利的气息,想必马上可以到最后一关了。
耶 我们把上面的二进制列表复制下来,用替换,来编辑成一个数组:

let bin=["01001000","00110100","01110011","01001001","01000001","01000011","01001010","01001011",
"01101110","01101100","01000001","01000001","01000001","00101011","00110001","01011010",
"01000010","00110001","01010010","01010100","01010011","00110111","01100011","00101011",
"01001001","01010001","01000111","01101011","01101001","01000110","01010001","01010110",
"01000101","01001001","01101101","01000001","01100111","01001000","01010010","01000011",
"01000100","01010110","01001011","01101011","01000110","00110110","01101011","01000010",
"01010001","01110010","01010111","01000101","01000101","01000011","01000011","00110000",
"01010001","01000010","01001011","01110001","01101111","01101111","01001011","01001011",
"01101001","01101111","01100111","01010101","01010001","01010101","01000010","01000001",
"01100111","01010101","01100111","01010000","01010011","01000111","00111000","01010111",
"01010101","01001111","01101100","01100101","01110101","01100111","01101001","01001011",
"01011000","01000010","01010110","01110000","01001001","01101000","01100001","01110001",
"01110001","01001101","01100111","00110111","01000001","01010110","01010100","01110101",
"00101111","01100110","00110011","01001100","01100101","00101011","01110110","00111001",
"00101111","00110001","01110000","01110110","01110110","01100010","01110100","01011010",
"01100011","00101011","01011001","00110111","01100101","00110011","00101011","01111010",
"01011010","00101011","00111001","00111001","00110101","01110011","01111001","01011010",
"01001100","01000111","01010010","01101011","01011010","01011001","00110101","01100111",
"01001101","01100010","00110111","01110101","01001101","01110000","00110101","00101011",
"00110111","01110011","01000011","00101111","01010011","01100101","01010100","01101011",
"00110101","01001010","01010001","01010110","01000110","01100101","01000111","00110000",
"01011000","01101011","01010110","01011010","01100001","01100001","00110010","01011000",
"01010001","00110110","01111010","01100110","01110010","00110100","01101011","00111000",
"01000001","01100111","01000111","01011000","01010010","00111000","01100111","01110000",
"01110001","01101001","01100111","01110001","01001011","01101111","01000111","00110011",
"01100011","01000100","01101100","00110101","01000010","01010001","01010111","01000101",
"01000011","01100111","01000011","01011000","00101011","00110011","01100011","01000110",
"01110100","01000110",.........,"01000001","00111101"];

接下来,我们将它转为十进制,然后再转为字符,再拼接到一个字符串。

let str="";
for(i=0;i<bin.length;i++){
    let strCode=parseInt(bin[i],2);
    str+=String.fromCharCode(strCode)
}
console.log(str);

输出的结果是:

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

接下来我们百度一个在线base64解密工具。
等等,解密出来是一串乱码?

why

对嘛,这么长的一个base64显然里面不是只有一个key啊。可能需要经过我们再次加工,那么既然它不是一个字符串,很有可能是个文件。
于是我们选择

9

解密为16进制显示,然后我们注意下结果里面的1f 8b 08,所有二进制文件的头部的几个字节都是文件头,一般作为识别文件用。
我们百度文件头大全,然后发现1f 8b 08代表它是一个gz文件。
那接下来我们单纯使用控制台是不行了,我们用node环境来把这个buff输出到一个文件中去。

var b = new Buffer(str, 'base64');
var fs= require('fs');
fs.writeFileSync("test.gz", b);

然后用一个解压缩工具解压后,即可得到一个图片: 10 为啥这个女的有点眼熟呢?好啦不管了,反正它上面的就是密码,我们敲下来完成这一关。

第十关

恭喜, 你已经通过了所有关卡

然而第十关竟然是个广告。。好吧!

结语

写这篇文章用了我好几个小时的时间,键盘都敲碎了,大哥们还不支持下?嘿嘿嘿。

工具

第九关脚本

flyingzl commented 8 years ago

第九关来一个python脚本:

import base64

def transform( words ):
    return ''.join([chr(int(word,2 )) for word in words])

def main():
    with open('code.txt', 'r') as f:
        data = f.read().replace('_', '1')

    lines = data.splitlines()
    result = ''.join([ transform(line.split(' '))  for line in lines ])

    with open('love.tar.gz','wb') as f:
        bytes = base64.b64decode(result)
        f.write(bytes)

if __name__ == '__main__':
    main()
Pcookie commented 7 years ago

https://harrynull.tech/cipher 感觉是仿的这个