Write up
0x01 base64
题目说明
元宵节灯谜是一种古老的传统民间观灯猜谜的习俗。 因为谜语能启迪智慧又饶有兴趣,灯谜增添节日气氛,是一项很有趣的活动。 你也很喜欢这个游戏,这不,今年元宵节,心里有个黑客梦的你,约上你青梅竹马的好伙伴小鱼, 来到了cyberpeace的攻防世界猜谜大会,也想着一展身手。 你们一起来到了小孩子叽叽喳喳吵吵闹闹的地方,你俩抬头一看,上面的大红灯笼上写着一些奇奇怪怪的 字符串,小鱼正纳闷呢,你神秘一笑,我知道这是什么了。
附件:base64-crypto1.txt
1 | Y3liZXJwZWFjZXtXZWxjb21lX3RvX25ld19Xb3JsZCF9 |
分析
base64 应该是编码,算不上加密。
Python base64 内建模块
| 函数 | 用途 |
|---|---|
| urlsafe_b64encode, urlsafe_b64decode | 专门对url进行base64编解码的,实际上也是调用的前一组函数。 |
| encode, decode | 编码和解码文件,对 StringIO 里的数据做编解码 (StringIO是从内存中读写str) |
| 编码和解码字符串,但是在 Python 3.1 之后这个名字被弃用 | |
| encodebytes, decodebytes | 编码和解码字符串,代替 encodestring, decodestring |
| b64encode, b64decode | 编码和解码字符串,并且有一个替换符号字符的功能 |
我们最常用的是 b64encode, b64decode,当然某些情况用 encodebytes, decodebytes 也没有大碍。
注意: encodebytes, decodebytes, b64encode, b64decode 都是对二进制字符串加解码.
e.g. b'liZXJwZWFjZXtXZWxjb21lX3RvX25ld19Xb3J'
由于base64编码后的字符除了英文字母和数字外还有三个字符
+,/,=,
其中=只是为了补全编码后的字符数为4的整数,而+和/在一些情况下需要被替换的,
b64encode和b64decode正是提供了这样的功能。
+和/需要被替换,把字符+和/分别变成-和_,最常见的就是对 url 进行 base64 编码的时候。
1 | import base64 |
题解
1 | import base64 |
0x02 Caesar
题目说明
你成功的解出了来了灯谜,小鱼一脸的意想不到“没想到你懂得这么多啊!” 你心里面有点小得意,“那可不是,论学习我没你成绩好轮别的我知道的可不比你少,走我们去看看下一个” 你们继续走,看到前面也是热热闹闹的,同样的大红灯笼高高挂起,旁边呢好多人叽叽喳喳说个不停。你一看 大灯笼,上面还是一对字符,你正冥思苦想呢,小鱼神秘一笑,对你说道,我知道这个的答案是什么了
附件:Caeser-crypto2.txt
1 | oknqdbqmoq{kag_tmhq_xqmdzqp_omqemd_qzodkbfuaz} |
题解
https://planetcalc.com/1434/# 直接分析出来

0x03 Morse
题目说明
题目描述:小鱼得意的瞟了你一眼,神神气气的拿走了答对谜语的奖励,你心里暗暗较劲 想着下一个谜题一定要比小鱼更快的解出来。不知不觉你们走到了下一个谜题的地方,这个地方有些奇怪。 上面没什么提示信息,只是刻着一些0和1,感觉有着一些奇怪的规律,你觉得有些熟悉,但是就是想不起来 这些01代表着什么意思。一旁的小鱼看你眉头紧锁的样子,扑哧一笑,对你讲“不好意思我又猜到答案了。”(flag格式为cyberpeace{xxxxxxxxxx},均为小写)
附件:Morse-crypto3.txt
1 | 11 111 010 000 0 1010 111 100 0 00 000 000 111 00 10 1 0 010 0 000 1 00 10 110 |
摩斯密码使用 .、- 来表示不同含义,这里只用 0和1,所以应该是用01代替.、-。
那么到底是用1代表.还是用0代表.呢?
都试一下不就好了……
结果:
1 | 用·表示0,-表示0 |
个人思考:
Morse 编码能用两个符号就能表示26个字母和十个数字,并且不会产生歧义,一定有它的方法。稍微想一下,其实就能想到这个方法,就是用哈夫曼编码。
用.表示左分支,用-表示右分支。
根结点没有数据,第二层左孩子和右孩子分别是.和-.
有:$25-2<26+10<26-2$ .
所以对应的树的深度是 5,亲测正确(手画)。
0x04 Railfence
题目描述
被小鱼一连将了两军,你心里更加不服气了。两个人一起继续往前走, 一路上杂耍卖艺的很多,但是你俩毫无兴趣,直直的就冲着下一个谜题的地方去了。 到了一看,这个谜面看起来就已经有点像答案了样子了,旁边还画着一张画,是一副农家小院的 图画,上面画着一个农妇在栅栏里面喂5只小鸡,你嘿嘿一笑对着小鱼说这次可是我先找到答案了。
附件:Railfence-crypto4.txt
1 | ccehgyaefnpeoobe{lcirg}epriec_ora_g |
分析
常规思路
len = 35
尝试用栅栏密码解密
在线工具:https://www.qqxiuzi.cn/bianma/zhalanmima.php
但是尝试无果??!!!
手动分析,利用前几个字母已知(cyberpeace{.+})的条件分析了半天,无果。
重新审视
这个不是普通的栅栏密码!
终于在某个博客上看到了线索:
栅栏密码的变种WWW型
栅栏密码的变种WWW型
传统型
1 | 假如有一个字符串:123456789 |
WWW型
1 | 同样一个字符串:123456789 |
题解
可以用脚本(见E:\·桌面\Cryptology\古典密码学\移位密码)
也可以用工具:http://www.atoolbox.net/Tool.php?Id=777 (默认 WWW 型加解密)
0x07 不仅仅是Morse
题目描述
“这个题目和我们刚刚做的那个好像啊但是为什么按照刚刚的方法做出来答案却不对呢” ,你奇怪的问了问小鱼,“可能是因为还有一些奇怪的加密方式在里面吧,我们在仔细观察观察”。两个人 安安静静的坐下来开始思考,很耐心的把自己可以想到的加密方式一种种的过了一遍,十多分钟后两个人 异口同声的说“我想到了!”
附件:Railfence-crypto4.txt
1 | --/.-/-.--/..--.-/-..././..--.-/..../.-/...-/./..--.-/.-/-./---/-/...././.-./..--.-/-.././-.-./---/-.././..../..../..../..../.-/.-/.-/.-/.-/-.../.-/.-/-.../-.../-.../.-/.-/-.../-.../.-/.-/.-/.-/.-/.-/.-/.-/-.../.-/.-/-.../.-/-.../.-/.-/.-/.-/.-/.-/.-/-.../-.../.-/-.../.-/.-/.-/-.../-.../.-/.-/.-/-.../-.../.-/.-/-.../.-/.-/.-/.-/-.../.-/-.../.-/.-/-.../.-/.-/.-/-.../-.../.-/-.../.-/.-/.-/-.../.-/.-/.-/-.../.-/.-/-.../.-/-.../-.../.-/.-/-.../-.../-.../.-/-.../.-/.-/.-/-.../.-/-.../.-/-.../-.../.-/.-/.-/-.../-.../.-/-.../.-/.-/.-/-.../.-/.-/-.../.-/.-/-.../.-/.-/.-/.-/-.../-.../.-/-.../-.../.-/.-/-.../-.../.-/.-/-.../.-/.-/-.../.-/.-/.-/-.../.-/.-/-.../.-/.-/-.../.-/.-/-.../.-/-.../.-/.-/-.../-.../.-/-.../.-/.-/.-/.-/-.../-.../.-/-.../.-/.-/-.../-.../.- |
分析+题解
是摩斯密码的形式,先对摩斯密码解码。
工具:http://www.zhongguosou.com/zonghe/moErSiCodeConverter.aspx
(这个网站对摩斯代码的分隔方式支持单斜杠和空格,比较好)
返回结果:
1 | MAYBEHAVEANOTHERDECODEHHHHAAAAABAABBBAABBAAAAAAAABAABABAAAAAAABBABAAABBAAABBAABAAAABABAABAAABBABAAABAAABAABABBAABBBABAAABABABBAAABBABAAABAABAABAAAABBABBAABBAABAABAAABAABAABAABABAABBABAAAABBABAABBA |
Maybe have another decode …
它的意思应该不是让你从头开始换另一个,而是对摩斯密码解析出来的再用一个的方式解密。
此刻应该关注:
1 | AAAAABAABBBAABBAAAAAAAABAABABAAAAAAABBABAAABBAAABBAABAAAABABAABAAABBABAAABAAABAABABBAABBBABAAABABABBAAABBABAAABAABAABAAAABBABBAABBAABAABAAABAABAABAABABAABBABAAAABBABAABBA |
很有特征的是,它只有两个字符组成,这是培根密码的典型特征。(手抓饼,A套餐,B套餐)
工具:http://rumkin.com/tools/cipher/baconian.php
1 | cyberpeace{ATTACKANDDEFENCEWORLDISINTERESTING} |
0x08 混合编码
题目描述
经过了前面那么多题目的历练,耐心细致在解题当中是 必不可少的品质,刚巧你们都有,你和小鱼越来越入迷。那么走向了下一个题目,这个题目好长 好长,你知道你们只要细心细致,答案总会被你们做出来的,你们开始慢慢的尝试,慢慢的猜想 ,功夫不负有心人,在你们耐心的一步步的解答下,答案跃然纸上,你俩默契一笑,相视击掌 走向了下面的挑战。
附件:Railfence-crypto4.txt
1 | JiM3NjsmIzEyMjsmIzY5OyYjMTIwOyYjNzk7JiM4MzsmIzU2OyYjMTIwOyYjNzc7JiM2ODsmIzY5OyYjMTE4OyYjNzc7JiM4NDsmIzY1OyYjNTI7JiM3NjsmIzEyMjsmIzEwNzsmIzUzOyYjNzY7JiMxMjI7JiM2OTsmIzEyMDsmIzc3OyYjODM7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiMxMDc7JiMxMTg7JiM3NzsmIzg0OyYjNjU7JiMxMjA7JiM3NjsmIzEyMjsmIzY5OyYjMTIwOyYjNzg7JiMxMDU7JiM1NjsmIzEyMDsmIzc3OyYjODQ7JiM2OTsmIzExODsmIzc5OyYjODQ7JiM5OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzUwOyYjNzY7JiMxMjI7JiM2OTsmIzEyMDsmIzc4OyYjMTA1OyYjNTY7JiM1MzsmIzc4OyYjMTIxOyYjNTY7JiM1MzsmIzc5OyYjODM7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiM5OTsmIzExODsmIzc5OyYjODQ7JiM5OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzExOTsmIzc2OyYjMTIyOyYjNjk7JiMxMTk7JiM3NzsmIzY3OyYjNTY7JiMxMjA7JiM3NzsmIzY4OyYjNjU7JiMxMTg7JiM3NzsmIzg0OyYjNjU7JiMxMjA7JiM3NjsmIzEyMjsmIzY5OyYjMTE5OyYjNzc7JiMxMDU7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiM2OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzExOTsmIzc2OyYjMTIyOyYjMTA3OyYjNTM7JiM3NjsmIzEyMjsmIzY5OyYjMTE5OyYjNzc7JiM4MzsmIzU2OyYjMTIwOyYjNzc7JiM4NDsmIzEwNzsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzEyMDsmIzc2OyYjMTIyOyYjNjk7JiMxMjA7JiM3ODsmIzY3OyYjNTY7JiMxMjA7JiM3NzsmIzY4OyYjMTAzOyYjMTE4OyYjNzc7JiM4NDsmIzY1OyYjMTE5Ow== |
分析
base64
以==结尾,应该是base家族。
1 | import base64 |
得到
1 | b'LzExOS8xMDEvMTA4Lzk5LzExMS8xMDkvMTAxLzExNi8xMTEvOTcvMTE2LzExNi85Ny85OS8xMDcvOTcvMTEwLzEwMC8xMDAvMTAxLzEwMi8xMDEvMTEwLzk5LzEwMS8xMTkvMTExLzExNC8xMDgvMTAw' |
这一串组成主要是’A-Z, a-z, 0-9’,还是 base64.
还是base64
1 | import base64 |
得到:
1 | b'/119/101/108/99/111/109/101/116/111/97/116/116/97/99/107/97/110/100/100/101/102/101/110/99/101/119/111/114/108/100' |
应该是 ASCII 码
ASCII 码
工具:http://www.ab126.com/goju/1711.html

也可以用 Python
1 | import base64 |
1 | cyberpeace{welcometoattackanddefenceworld} |
意外情况——环境相关? - Unicode
1 | import base64 |
这段代码在 Markdown 里面运行能直接得到:
1 | 'LzExOS8xMDEvMTA4Lzk5LzExMS8xMDkvMTAxLzExNi8xMTEvOTcvMTE2LzExNi85Ny85OS8xMDcvOTcvMTEwLzEwMC8xMDAvMTAxLzEwMi8xMDEvMTEwLzk5LzEwMS8xMTkvMTExLzExNC8xMDgvMTAw' |
但是在我的 Python 中运行的结果却是:
1 | 'LzExOS8xMDEvMTA4Lzk5LzExMS8xMDkvMTAxLzExNi8xMTEvOTcvMTE2LzExNi85Ny85OS8xMDcvOTcvMTEwLzEwMC8xMDAvMTAxLzEwMi8xMDEvMTEwLzk5LzEwMS8xMTkvMTExLzExNC8xMDgvMTAw' |
看了其他人的 write up,有些只有两次 base64,有些和我的情况一样。
那怎么处理?
显然这是 Unicode 编码
工具:https://tool.chinaz.com/Tools/unicode.aspx

之后的步骤同上。
可以用 Python
Unicode 的 &# 形式后面跟的数字和 ASCII 码 在数字区和字母区应该是重合的。
所以:
1 | import base64 |
Python 补充
编码不在 Crypto 的 Wiki 中,所以这道题做得比较艰难;Python的重要的一些函数也有些忘了,这里做补充。
map函数
描述
是种映射(map)函数,可以对列表每个元素进行操作(映射/函数)
语法
1 | map(function, iterable, ...) |
iterable: 一个或多个序列
Python 2.x 返回列表。
Python 3.x 返回迭代器。
用法
1 | def square(x): |
将列表中的字符串转为数字
1 | numbers = ['1','124', '6982', '320'] |
bytes、str函数
1 | # bytes object |
因为 b64encode、b64decode 只能处理二进制字符串,返回值也是二进制,所以这个函数很有用。
完整题解
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
import base64
# Base64
# b'JiM3NjsmIzEyMjsmIzY5OyYjMTIwOyYjNzk7JiM4MzsmIzU2OyYjMTIwOyYjNzc7JiM2ODsmIzY5OyYjMTE4OyYjNzc7JiM4NDsmIzY1OyYjNTI7JiM3NjsmIzEyMjsmIzEwNzsmIzUzOyYjNzY7JiMxMjI7JiM2OTsmIzEyMDsmIzc3OyYjODM7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiMxMDc7JiMxMTg7JiM3NzsmIzg0OyYjNjU7JiMxMjA7JiM3NjsmIzEyMjsmIzY5OyYjMTIwOyYjNzg7JiMxMDU7JiM1NjsmIzEyMDsmIzc3OyYjODQ7JiM2OTsmIzExODsmIzc5OyYjODQ7JiM5OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzUwOyYjNzY7JiMxMjI7JiM2OTsmIzEyMDsmIzc4OyYjMTA1OyYjNTY7JiM1MzsmIzc4OyYjMTIxOyYjNTY7JiM1MzsmIzc5OyYjODM7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiM5OTsmIzExODsmIzc5OyYjODQ7JiM5OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzExOTsmIzc2OyYjMTIyOyYjNjk7JiMxMTk7JiM3NzsmIzY3OyYjNTY7JiMxMjA7JiM3NzsmIzY4OyYjNjU7JiMxMTg7JiM3NzsmIzg0OyYjNjU7JiMxMjA7JiM3NjsmIzEyMjsmIzY5OyYjMTE5OyYjNzc7JiMxMDU7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiM2OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzExOTsmIzc2OyYjMTIyOyYjMTA3OyYjNTM7JiM3NjsmIzEyMjsmIzY5OyYjMTE5OyYjNzc7JiM4MzsmIzU2OyYjMTIwOyYjNzc7JiM4NDsmIzEwNzsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzEyMDsmIzc2OyYjMTIyOyYjNjk7JiMxMjA7JiM3ODsmIzY3OyYjNTY7JiMxMjA7JiM3NzsmIzY4OyYjMTAzOyYjMTE4OyYjNzc7JiM4NDsmIzY1OyYjMTE5Ow=='
cipher = b'JiM3NjsmIzEyMjsmIzY5OyYjMTIwOyYjNzk7JiM4MzsmIzU2OyYjMTIwOyYjNzc7JiM2ODsmIzY5OyYjMTE4OyYjNzc7JiM4NDsmIzY1OyYjNTI7JiM3NjsmIzEyMjsmIzEwNzsmIzUzOyYjNzY7JiMxMjI7JiM2OTsmIzEyMDsmIzc3OyYjODM7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiMxMDc7JiMxMTg7JiM3NzsmIzg0OyYjNjU7JiMxMjA7JiM3NjsmIzEyMjsmIzY5OyYjMTIwOyYjNzg7JiMxMDU7JiM1NjsmIzEyMDsmIzc3OyYjODQ7JiM2OTsmIzExODsmIzc5OyYjODQ7JiM5OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzUwOyYjNzY7JiMxMjI7JiM2OTsmIzEyMDsmIzc4OyYjMTA1OyYjNTY7JiM1MzsmIzc4OyYjMTIxOyYjNTY7JiM1MzsmIzc5OyYjODM7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiM5OTsmIzExODsmIzc5OyYjODQ7JiM5OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzExOTsmIzc2OyYjMTIyOyYjNjk7JiMxMTk7JiM3NzsmIzY3OyYjNTY7JiMxMjA7JiM3NzsmIzY4OyYjNjU7JiMxMTg7JiM3NzsmIzg0OyYjNjU7JiMxMjA7JiM3NjsmIzEyMjsmIzY5OyYjMTE5OyYjNzc7JiMxMDU7JiM1NjsmIzEyMDsmIzc3OyYjNjg7JiM2OTsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzExOTsmIzc2OyYjMTIyOyYjMTA3OyYjNTM7JiM3NjsmIzEyMjsmIzY5OyYjMTE5OyYjNzc7JiM4MzsmIzU2OyYjMTIwOyYjNzc7JiM4NDsmIzEwNzsmIzExODsmIzc3OyYjODQ7JiM2OTsmIzEyMDsmIzc2OyYjMTIyOyYjNjk7JiMxMjA7JiM3ODsmIzY3OyYjNTY7JiMxMjA7JiM3NzsmIzY4OyYjMTAzOyYjMTE4OyYjNzc7JiM4NDsmIzY1OyYjMTE5Ow=='
cipher_Unicode = base64.decodebytes(cipher)
# Unicode
# 'LzExOS8xMDEvMTA4Lzk5LzExMS8xMDkvMTAxLzExNi8xMTEvOTcvMTE2LzExNi85Ny85OS8xMDcvOTcvMTEwLzEwMC8xMDAvMTAxLzEwMi8xMDEvMTEwLzk5LzEwMS8xMTkvMTExLzExNC8xMDgvMTAw'
cipher_Unicode = str(cipher_Unicode, encoding = 'UTF-8') #bytes 对象转成 string 对象
cipher_Unicode = cipher_Unicode.replace("&#", "")
cipher_Unicode = cipher_Unicode.replace(";", ",")
cipher_Unicode = cipher_Unicode.strip(",") # 末尾有一个';'
lst = [] #结果存储中介列表
cipher_list = list(map(int, cipher_Unicode.split(','))) #数字存储中中介列表
for num in cipher_list:
lst.append(chr(num))
cipher_base_sec = "".join(lst)
# Base 64
# b'LzExOS8xMDEvMTA4Lzk5LzExMS8xMDkvMTAxLzExNi8xMTEvOTcvMTE2LzExNi85Ny85OS8xMDcvOTcvMTEwLzEwMC8xMDAvMTAxLzEwMi8xMDEvMTEwLzk5LzEwMS8xMTkvMTExLzExNC8xMDgvMTAw'
cipher_base_sec = bytes(cipher_base_sec, encoding = 'UTF-8')
cipher_ascii = base64.b64decode(cipher_base_sec)
# b'/119/101/108/99/111/109/101/116/111/97/116/116/97/99/107/97/110/100/100/101/102/101/110/99/101/119/111/114/108/100'
cipher_ascii = str(cipher_ascii, encoding = "UTF-8")
cipher_ascii = cipher_ascii.replace('/', ',')
cipher_ascii = cipher_ascii.strip(',') # 去掉开头的 '/'
lst = [] #结果存储中介列表
cipher_list = list(map(int, cipher_ascii.split(','))) #数字存储中中介列表
for num in cipher_list:
lst.append(chr(num))
plaintext = "".join(lst)
# 测试与输出
f = open("混合编码-cipher.txt", "w")
f.write(plaintext)
f.close()
1 | import base64 |
0x09 转轮机加密
题目描述
你俩继续往前走,来到了前面的下一个关卡,这个铺面墙上写了好多奇奇怪怪的 英文字母,排列的的整整齐齐,店面前面还有一个大大的类似于土耳其旋转烤肉的架子,上面一圈圈的 也刻着很多英文字母,你是一个小历史迷,对于二战时候的历史刚好特别熟悉,一拍大腿:“嗨呀!我知道 是什么东西了!”。提示:托马斯·杰斐逊
附件:转轮机加密-crypto.txt
1 | 1: < ZWAXJGDLUBVIQHKYPNTCRMOSFE < |
分析
转轮机加密
参看:传统密码学(三)——转轮密码机
是个多表替换密码。
基本操作是“旋转”,其余的和本题是没有关系。
加密表和密钥、密文的特点
加密表每一行都有 26 个不同的字母,密钥的长度、密文长度、表格的行数都是 13,密文中字母是有重复字母的。
猜想一:
由此推断,密文是滚轮机的加密表某一列的字母连起来。
然而经过尝试并非如此,每一行对应密文中的字母对应的序号与下一行的字母对应的序号之间的差值不能根据密钥推出!
调整调整!!!是否有其他解法?
猜想二:
跳出之前的思维,之前的思考角度是试图逆向想出它的加密方案,
但是如果同时有密文和密钥,其实只要知道解密方案就好了,
虽然加密和解密是逆向的过程,但是方法不同。所以可以双向思考、双向破解:
- 根据密文密钥的特点,猜测加密方法,对密码进行攻击。
- 根据密文密钥,推测解密方法,使用解密方法对密文进行解密。
既然猜想一似乎没有出路了,接下来尝试 2 对应的思路。
构造解密和加密的映射,加密 - 解密,密文 - 明文,密钥 - 密钥;
现在的明文:NFQKSEVOQOFNP
现在的密钥:2,3,7,5,13,12,9,1,8,10,4,11,6
密钥的每个数字对应一行,那么明文的字母如果对应一列(比如以这个字母为首),就能唯一确定一个表格。然后加密表的某一列应该会有密文(映射原来的明文)。
题解
方法一:“手动”转
1 | 转前: |
方法二:写 Python 脚本(别人的 writeup)
1 | import re |
0x10 easychallenge
题目描述
你们走到了一个冷冷清清的谜题前面,小鱼看着题目给的信息束手无策,丈二和尚摸不着头脑 ,你嘿嘿一笑,拿出来了你随身带着的笔记本电脑,噼里啪啦的敲起来了键盘,清晰的函数逻辑和流程出现在 了电脑屏幕上,你敲敲键盘,更改了几处地方,运行以后答案变出现在了电脑屏幕上。
附件:easychallenge.pyc
分析
.pyc
Python 的实质
其实 Python 和 Java、C# 一样,也是一门基于虚拟机的语言.
1 | javac hell.java |
Python也是一门先编译后解释的语言。
将.py形式的程序编译成中间式文件(byte-compiled)的.pyc文件,这么做的目的就是为了加快下次执行文件的速度
并不是所有的.py文件在与运行的时候都会产生.pyc文件,只有在import相应的.py文件的时候,才会生成相应的.pyc文件
对于py文件,可以执行下面命令来生成pyc文件。
python -m foo.py
Python的运行过程:
在硬盘上看到的pyc文件,其实PyCodeObject才是Python编译器真正编译成的结果。
当python程序运行时,编译的结果是保存在位于内存中的PyCodeObject中,
当Python程序运行结束时,Python解释器则将PyCodeObject写回到pyc文件中。
当python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。
所以,我们可以说pyc文件其实是PyCodeObject的一种持久化保存方式。
反编译
方法一:使用 uncompyle6 库
pip install uncompyle6

方法二:在线反编译
解密源代码
1 | import base64 |
发现末尾有’===’,所以应该是 base32, 而非 base64
encode3 -> base64.b32decode(cipher)
encode2, encode1 异或的解密只需要再对对应的数字异或一次就好了。
1 | import base64 |
存在的问题 1
ord()expected string of length 1,but int found
参考博客,得到方法:将其变为字符串再进行ord等操作
尝试
直接转化:bytes(cipher12, encoding = ‘utf-8’)
得到稀奇古怪的式子;以为解密方法不对。
1 | c = chardet.detect(cipher21) |
但是解密处理还是稀奇古怪的……
哦哦哦!因为经过加解密,不正常才是正常的…………
所以只能用最最原始的方法:直接复制为 str 类型。如下:
1 | import base64 |
存在的问题 2
Python运算符优先级

惊讶地发现,位运算比四则运行优先级要低,所以
chr ( ord(char) ^ 36 - 36 ) -> chr ( (ord(char) ^ 36) - 36 )
Flag
cyberpeace{interestinghhhhh}
0x11 幂数加密
题目描述
你和小鱼终于走到了最后的一个谜题所在的地方,上面写着一段话“亲爱的朋友, 很开心你对网络安全有这么大的兴趣,希望你一直坚持下去,不要放弃 ,学到一些知识, 走进广阔的安全大世界”,你和小鱼接过谜题,开始了耐心细致的解答。flag为cyberpeace{你解答出的八位大写字母}
附件:幂数加密-crypto
1 | 8842101220480224404014224202480122 |
分析与题解
幂数加密的意思用 $2^0, 2^1, 2^2, 2^3 $ 四个数字进行加密。
显然……这不是云影密码吗?
??????????
???难度 2.0 ???
??????????
1 | 88421 122 48 2244 4 142242 248 122 |
cyberpeace{WELLDONE}
0x05 easy_RSA
题目描述
解答出来了上一个题目的你现在可是春风得意,你们走向了下一个题目所处的地方 你一看这个题目傻眼了,这明明是一个数学题啊!!!可是你的数学并不好。扭头看向小鱼,小鱼哈哈一笑 ,让你在学校里面不好好听讲现在傻眼了吧~来我来!三下五除二,小鱼便把这个题目轻轻松松的搞定了。flag格式为cyberpeace{小写的你解出的答案}
附件:easy_RSA-crypto.txt
1 | 在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17 |
分析
gmpy2
https://gmpy2.readthedocs.io/en/latest/intro.html
gmpy2是一个C编码的Python扩展模块,它支持多精度算术。gmpy2是原始gmpy模块的后继者。
1 | invert(...): |
e * d== k * phi(n) mod n 即求 e 模 phi(n) 的乘法逆元
题解
1 | import gmpy2 |
cyberpeace{125631357777427553}
补充 gmpy2
0x06 Normal_RSA
题目描述
你和小鱼走啊走走啊走,走到下一个题目一看你又一愣,怎么还是一个数学题啊 小鱼又一笑,hhhh数学在密码学里面很重要的!现在知道吃亏了吧!你哼一声不服气,我知道数学 很重要了!但是工具也很重要的,你看我拿工具把他解出来!你打开电脑折腾了一会还真的把答案 做了出来,小鱼有些吃惊,向你投过来一个赞叹的目光
附件:
pubkey.pem
flag.enc
分析与解
目前只有保存公钥和密文的文件,故意味着公钥一定是可以攻击的。
从文件中提取信息
1 | OpenSSL>rsa -in pubkey.pem -text -publin |
更好的方式是把模数(大整数 n )提取出来:
1 | openssl>rsa -pubin -text -modulus -in warmup -in pubkey.pem |
从而获得模数:
Modulus = C2636AE5C3D8E43FFB97AB09028F1AAC6C0BF6CD3D70EBCA281BFFE97FBE30DD
信息基本处理
显然是十六进制,需要转化成十进制。
偷懒用网站工具:https://www.sojson.com/hexconvert/16to10.html
然后就是报应……
得到十进制:
87924348264132512606026244266860026602226062424884468222066668822204828484286
因式分解之后会发现居然不是两个素数的乘积……
显然是因为数字发生错误了,所以是在转换的时候,网站在处理大整数的时候发生偏差。
进制转化:
1.其他进制转十进制
函数:int(number_str, base_number)
第一个参数是内容是数值类型的字符串,第二个参数是字符串内容数值的基数。
返回十进制数值型变量
1
2
3
4
5
6 int('0xf',16)
15
int('10100111110',2)
1342
int('17',8)
152.转十六进制
hex(decimal_numeral) 函数
参数是十进制数值
如果是其他进制的字符串,需要先用 int() 函数转化成 十进制数值,再用 hex()
返回对应类型的字符串(十六进制)
1
2
3
4 hex(1033)
'0x409'
hex(int('101010',2))
hex(int('17',8))3.转二进制
bin(decimal_numeral) 函数
参数是十进制数值,和十六进制类似。
如果是其他进制的字符串,需要先用 int() 函数转化成 十进制数值,再用 bin()
返回对应类型的字符串(二进制)
1
2
3
4
5
6 bin(10)
'0b1010'
bin(int('ff',16))
'0b11111111'
bin(int('17',8))
'0b1111'4.转八进制
oct(decimal_numeral) 函数
其余的类型。
1 | n = int('C2636AE5C3D8E43FFB97AB09028F1AAC6C0BF6CD3D70EBCA281BFFE97FBE30DD', 16) |
大整数因数分解:
生成私钥文件
1 | p = 275127860351348928173285174381581152299 |
rsatool.py 放进当前目录下,
1 | python rsatool.py -out private.pem -e 65537 -p 275127860351348928173285174381581152299 -q 319576316814478949870590164193048041239 |
发现 Python2.7 虽然能够运行命令,但是会报错。猜测这个是用 Python3 写的,一些处理上不兼容,所以换了 Python3 :
此刻目录下生成了,private.pem
用私钥解密
1 | wcb534@Ubuntu:~/$openssl |
PCTF{256b_i5_m3dium}
OpenSSl
详见:OpenSSL
rsatool
官方的 README.md 写的很简洁,也只给了两个例子:
1 | python rsatool.py -f PEM -o key.pem -n 13826123222358393307 -d 9793706120266356337 |
基本格式:
1 | python rsatool.py [-p P] [-q Q] [-n N] [-e E] [-d D] [-h] [-o] [-f] [-v] |
目前只用来生成 pem 文件……
rsatool 还有更多的功能。(还有RSA-CRT)
更多见:https://github.com/adeptex/rsatool
0x12 easy_ECC
题目分析
这个题目只是实现 ECC 的加密,没有太大的难度。
CTF - Wiki 上的 2013 年 SECCON CTF quals 中的 Cryptanalysis 需要对 ECC 进行爆破,比较有趣。
先实现椭圆曲线上的加法运算:
1 | # encoding=utf-8 |
由于 M 是素数,所以由费马小定理可知 $(x_2 - x_1)^{M-1} = 1 \pmod {M}$,
所以 $x_2 - x_1$ 的乘法逆元是 $(x_2 - x_1)^{M-2}$,同理,$y_2 - y_1$ 的乘法逆元是 $(y_2 - y_1)^{M-2}$.
接下来进行乘法运算(加密)
1 | G = (6478678675,5636379357093) |