Jarvis’s encryption system
Let’s play with Jarvis’s new encryption box.
nc pwn.jarvisoj.com 9880
题目来源:基于BCTF2016 hyperRSA改编。
Hint1: 此题需要一定量的爆破。
题目给了一个py,一个png
crypto500.py
1 | #!/usr/bin/env python |
flag.png
在线ocr提取一下图片中的数字:
1 | Welcome to Jarvis's encryption system.Let's init the cipher first: |
只知道一个不能分解的N,一段知道明文的密文,一段不知道明文的密文。理论上爆破出e,然后去服务端输入e就能得到d,于是我这样爆破
1 | N = 808637320166213096433765975908829772554859069394497436792703828416763985949910999652518305818627321094257781267795371106923808192073932662313603219525599014635435542122940843344921727149256852355110338886574805360544004118210641173633231100848831019159519744863314748281129830905559513810272933968408858616937223539622595750248885831720830102914499513408356858587797522763592193335162884129664298938995394243273615798207065590802899685489088903478734288977143851327400816886878238915788561611104380001569848016035186213716602462262685777960742683591155978590371074585063550419528377002596163321548052257322263024813745933243795081592986850478573362522245788630785664119935566422559659277401321793012274415007906726880710258434953224297253000176721652344571059040066987969691706315602374506498087282531643212970147526356421919309049062439117990930204486012562031589114880474346559407445496718773030816258262150397230280669274725009415653773469037623986165899557423095323109994543129373149980880777219450714265152054529287453826506032747047856303879606356141420416161004589629524370677871918513405209191951229311529443558187652701599377904802383252318582028816524498306240682160249309341335405511246150908708558397938689907425750101507 |
如上,我爆破了1000000,还是没有爆破出来,说明e有点大,这样爆破可能明年就能做出这题了。换个思路,这里看到提示
题目来源:基于BCTF2016 hyperRSA改编。
然后谷歌BCTF 2016 hyper RSA
找到这个
https://grocid.net/2016/03/24/bctf-hyper-rsa-partial-write-up/
我理解的意思是先根据n,e,d解出p和q,然后再穷举攻击,所以还是得爆破,本来以为这样爆破的速度快一点,结果发现速度更慢了,excuse me,感觉这题改编的和原题一点都不像,不能按上面链接的那个思路,还是老老实实爆破e吧,这里记录一下得到p,q的过程,和本题无关
根据n,e,d解出p和q我们用rsatool来做
安装rsatool
1 | git clone https://github.com/ius/rsatool.git |
安装gmpy时可能会报错,不过在(上)中我们已经安装了gmpy2了,而且看到rsatool.py的开头,
1 | try: |
所以不需要再去安gmpy了
先去自己搞个e,d,如
1 | ➜ ~ nc pwn.jarvisoj.com 9880 |
新建一个get_pq.py
1 | from rsatool import factor_modulus |
执行得到p和q
1 | ➜ rsatool git:(master) ✗ python get_pq.py |
扯完废话,改进一下脚本然后放到服务器上跑,这次跑0到100000000,根据log的输出预算时间为21小时,先去看后面的题
1 | import logging |
吃了个晚饭,看了集黑客军团
,登陆服务器一看,爆破出来了
1 | root:~# head rsa.log |
可以看到,爆破用时2h 10min 41s,出题人一点都不友好
然后可以通过上面得到的p
和q
来得到e=7845741
时的d
1 | import libnum |
我们可以去服务端验证一下
1 | ➜ ~ nc pwn.jarvisoj.com 9880 |
最后一步,用d解密
1 | c2 = 738822002752800877524466308025949155169562722946933006009883884249589602039677687891359871510923927357766748131398443497541198900771818831638644263405425815579383553019562159083788644122365536627592737115316351290153544908592280731090451811311680698586032725090719266003369555867584457372823678746133588560994163232730766388456903527206840527304843529539480355012405496730615078972755415860013097394363116913629756292725693596880188792245847698225435105827398989245800248197290718407831242734331874121327502564673597694670795036098967372950089253263743880807024448724715652660602771818683520844873803372738417012436219777372987997036211306992938395670636075660990930360358970016244484405618827909229400111542660072678812089441010001235353317911131109787281238112284352067511452432985149442969693926797740772628154057474332702139775407456229918917403138849681496015981718513476254353617586634306067889050783266988506871489696817574207289110594169371818597141857443042841485880477066344316648550850088971005108756497748568090122624591451915965314486079436499049418137147522360690326710468200339550170216543240318289067712843687012174036874897324652429812609807952220427326987655148639613323665786093803557065570465270944069296977739085 |
影之密码
请分析下列密文进行解密 8842101220480224404014224202480122 得到flag,flag为8位大写字母
题目来源:CFF2016
1 | import string |
好多盐
某遗留系统采用固定格式+6-10位数字类型密码,今天他们发生了数据泄露事件,已知固定格式为{FLAG:},做为一名黑客,你要开始干活了。字符串长度为10位
题目来源:CFF2016
1 | import hashlib |
flag:1234567890
superexpress
题目来源:TWCTF2016
下载解压,两个文件
encrypted
1 | 805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9 |
problem.py
1 | import sys |
设key
为'a1a2a3b1b2b3'
,zip后得到一个列表包含三个元组[(a1,b1),(a2,b2),(a3,b3)]
,按照脚本里的加密方式,可以推出
1 | a3*(a2*(a1*c+b1)+b2)+b3 = a3*a2*a1*c+b1*a2*a3+b2*a3+b3 |
于是
1 | import string |
vigenere
题目来源:TWCTF2016
解压,有三个文件
1 | ➜ Downloads cd vigenere_300 |
解密脚本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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176# Python 3 Source Code
from base64 import b64encode, b64decode
import os
from math import sqrt
candi_count = 0
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/'
encrypted = "a7TFeCShtf94+t5quSA5ZBn4+3tqLTl0EvoMsNxeeCm50Xoet+1fvy821r6Fe4fpeAw1ZB+as3Tphe8xZXQ/s3tbJy8BDzX4vN5svYqIZ96rt35dKuz0DfCPf4nfKe300fM9utiauTe5tgs5utLpLTh0FzYx0O1sJYKgJvul0OfiuTl00BCks+aaJZm8Kwb4u+LtLCqbZ96lv3bieCahtegx+7nzqyO6YCb4b9LovCELZ9Pe0L5rLSaBDzXaftxseAw1JzCF0MGjeCacKb69u9TlgCudZT6Os3ojhcWxD914vNHfeCuaJvH4s4aarBKlGdsT8G4UKZhfJB+y0LbjqCOnZT6baF1WiZeNtfsNtuoo+c=="
def shift(char, key, rev=False):
if char not in chars:
return char
if rev:
return chars[(chars.index(char) - chars.index(key)) % len(chars)]
else:
print((chars.index(char) + chars.index(key)) % len(chars))
return chars[(chars.index(char) + chars.index(key)) % len(chars)]
def encrypt(message, key):
encrypted = b64encode(message.encode('ascii')).decode('ascii')
return ''.join([shift(encrypted[i], key[i % len(key)]) for i in range(len(encrypted))])
def original_decrypt(encrypted, key):
encrypted = ''.join([shift(encrypted[i], key[i % len(key)], True)
for i in range(len(encrypted))])
return b64decode(encrypted.encode('ascii')).decode('ascii')
# not using encode or decode ascii
def decrypt(encrypted, key):
encrypted = ''.join([shift(encrypted[i], key[i % len(key)], True)
for i in range(len(encrypted))])
return b64decode(encrypted)
def generate_random_key(length=5):
return ''.join(map(lambda a: chars[a % len(chars)], os.urandom(length)))
def Kasiski_exam(encrypted):
strlist = []
count = 0
indexlist = []
for i in range(len(encrypted)):
for j in range(i, len(encrypted)):
if j - i < 3:
continue
start = i
search_str = encrypted[i:j]
while True:
detect = encrypted[start:].find(search_str)
if detect == -1:
break
else:
count += 1
if count == 2:
strlist.append(search_str)
indexlist.append(detect + j - i)
start += detect + (j - i)
if count == 0:
break
count = 0
print(indexlist)
print(strlist)
anslist = my_factor(indexlist)
return anslist
def my_factor(numlist):
factor_list = []
for x in range(2, int(sqrt(numlist[0])) + 1):
if numlist[0] % x == 0 and x >= 5 and x <= 14:
factor_list.append(x)
for i in range(1, len(numlist)):
anslist = list(factor_list)
num = numlist[i]
for x in factor_list:
if num % x != 0:
anslist.remove(x)
return anslist
def is_ascii(string):
if string:
for char in string:
if char > 126:
return False
if char < 32 and not char == 10:
return False
return True
def split_str_and_isascii(plain, num, block):
start = 3 * block
for i in range(start, len(plain), 9):
if not is_ascii(plain[i:i + num]):
return False
return True
# if key_len == 12
def brute_key(encrypted, key_len):
global candi_count
candi_key_list = [[], [], []]
for block in range(int(key_len / 4)):
for a in chars:
for b in chars:
if not split_str_and_isascii(decrypt(encrypted, a + b + "aa"), 1, block):
continue
for c in chars:
if not split_str_and_isascii(decrypt(encrypted, a + b + c + "a"), 2, block):
continue
for d in chars:
if split_str_and_isascii(decrypt(encrypted, a + b + c + d), 3, block):
candi_key_list[block].append(a + b + c + d)
candi_count += 1
return candi_key_list
# if key_len == 6
def brute_key_6(encrypted, key_len):
global candi_count
candi_key_list = []
for block in range(int(key_len / 4)):
for a in chars:
for b in chars:
if not split_str_and_isascii(decrypt(encrypted, a + b + "aa"), 1, block):
continue
for c in chars:
if not split_str_and_isascii(decrypt(encrypted, a + b + c + "a"), 2, block):
continue
for d in chars:
if split_str_and_isascii(decrypt(encrypted, a + b + c + d), 3, block):
candi_key_list.append(a + b + c + d)
candi_count += 1
return candi_key_list
def main():
# ==== kasiski examination ====
factor_list = Kasiski_exam(encrypted) # [6,12]
key_len = factor_list[1]
# ==== brute force attack to base64 ====
print("Start brute force...")
candi_key1, candi_key2, candi_key3 = brute_key(encrypted, key_len)
print(candi_key1)
print(candi_key2)
print(candi_key3)
# ==== key candidate ====
keylist = []
for key1 in candi_key1:
for key2 in candi_key2:
for key3 in candi_key3:
keylist.append(key1 + key2 + key3)
print(candi_count)
print(keylist)
# if "TWCTF{" in decrypted, It is highly possible that the key is correct.
for key in keylist:
dec = decrypt(encrypted, key)
check = b"TWCTF{"
if check in dec:
print("--------- key candidate : decrypted ---------------")
print(key, ":", dec)
print()
if __name__ == '__main__':
main()
具体操作过程参见:http://73spica.tech/blog/tw_mma_ctf_2016_vigenere-cipher/
运行结果
TWCTF{C14ss1caL CiPhEr iS v3ry fun}
DSA
DSA是基于整数有限域离散对数难题的,其安全性与RSA相比差不多。DSA的一个重要特点是两个素数公开,这样,当使用别人的p和q时,即使不知道私钥x,你也能确认它们是否是随机产生的,还是作了手脚。
可以使用openssl方便地进行dsa签名和验证。
签名与验证:
openssl dgst -sha1 -sign dsa_private.pem -out sign.bin message.txt
openssl sha1 -verify dsa_public.pem -signature sign.bin message.txt
本题的攻击方法曾被用于PS3的破解,答案格式:CTF{x}(x为私钥,请提交十进制格式)
目录结构为
1 | ➜ dsa tree |
先用题目给的验证命令看一下
1 | ➜ dsa openssl sha1 -verify dsa_public.pem -signature packet1/sign1.bin packet1/message1 |
读一下公钥
1 | ➜ dsa openssl dsa -in dsa_public.pem -text -noout -pubin |
读一下签名的值
1 | ➜ dsa openssl asn1parse -inform der -in packet1/sign1.bin |
注意到3和4的2:d=1 hl=2 l= 20 prim: INTEGER :
值一样,这里这个值代表签名结果中的r,下面一行为s。由于r一样,说明采用的随机数k一样,采用共享k攻击
这里读取dsa的公钥采用,需要从github上clone下来的,pip的这个函数阉割了,在虚拟机中读取
1 | from Crypto.PublicKey import DSA |
然后攻击脚本
1 | from hashlib import sha1 |
CTF{520793588153805320783422521615148687785086070744}
Complicated Crypto
五层密码,好复杂
来源:sunnyelf
第一层:crc32爆破
crc32_util.py
1 | # -*- coding: utf-8 -*- |
爆破脚本,注意6位的crc32碰撞有很多结果,手动缩小一下字典,这里先试了string.letters + string.digits,发现没有特别像密码的,再加个_到字典里就得到了
1 | from crc32_util import * |
解出一个CRC32 Collision.7z,没有密码,直接解压得到
1 | ➜ CRC32 Collision tree |
第二层:维吉尼亚
看一下每个文件的内容
1 | ➜ CRC32 Collision cat ciphertext.txt |
还有几千行key
先在线解一下看看:https://guballa.de/vigenere-solver
结果不是太理想,看最后那里有个f开头某个单词,但是我们不知道是什么,不过可以看出个大概了,用关键字password爆破一下,最后密码换成小写加上空格
1 | from pycipher import Vigenere |
然后解压得到
1 | ➜ Find password tree |
第三层:sha1爆破
txt的内容
1 | 恭喜! |
直接爆破
1 | import hashlib |
解压出来
1 | ➜ Easy SHA1 tree |
第四层:md5碰撞
txt内容为
1 | Hello World ;-) |
之前看过这个,就是有个输出Hello World ;-)
的程序的md5碰撞,直接百度得到这篇文章
blog.csdn.net/xiaofei0859/article/details/54924123
换到win10环境下载下来,运行得到结果
Goodbye World :-(
第五层:RSA
历经艰辛,终于到最后一关了,这次终于没压缩包了
1 | ➜ Vulnerable RSA tree |
读取公钥
1 | from Crypto.PublicKey import RSA |
e特别大,那么一般d就特别小。在d比较小时,有两种攻击方法:
d < N^(1/4)/3
时,可以使用wiener’s attack
来获得RSA的私钥。d ≤ N^0.292
时, 我们可以利用Boneh and Durfee attack
,在一定程度上该要攻击比wiener attack要强一些。
使用wiener’s attack
:
安装
1 | git clone https://github.com/pablocelayes/rsa-wiener-attack.git |
新建一个py文件
1 | from RSAwienerHacker import hack_RSA |
使用Boneh and Durfee attack
:
1 | git clone https://github.com/mimoo/RSA-and-LLL-attacks.git |
然后修改boneh_durfee.sage
中的example()
中的N
和e
,然后运行sage
1 | ➜ RSA-and-LLL-attacks git:(master) ✗ sage boneh_durfee.sage |
解密flag
1 | from Crypto.PublicKey import RSA |
1 | ➜ crypto python Complicated_Crypto.py |