详见CTF_Crypto-all-in-one/tools.py at main · DuanYuFi/CTF_Crypto-all-in-one (github.com)中solve_classical函数,其可以自动化分析类似维吉尼亚密码加密算法的密钥,从而解出明文。成功率不保证100%,但是非常高。其相对于网上现有工具的优点是可以对密文包含不可见字符的算法进行解密,并且易于扩展。
使用方法如下:
函数参数介绍:
text
,即bytes
类型的古典加密后密文
decode_function
,即正常情况下该古典算法的解密函数,需要其满足形式为:def decode_function(cipher, key)
,返回值为通过key
解密的明文,类型都是bytes
。
min_length
和max_length
: 密钥长度区间
key_char
: 密钥可能包含的字符,bytearray
类型
使用案例:
例如上周的某个比赛,其加密采用的是类似维吉尼亚密码,不过其字符集不是小写字母而是全体单字节,加法采用异或加,因此网上常见的脚本不方便进行破解。然而使用这里的脚本,再进行一些微调,就能够完成该攻击。
exp如下
# -*- coding: utf-8 -*-
import string
IC = 0.065
charset = string.ascii_letters
charset = charset.encode()
naturalLanguageP = [0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056,0.02758, 0.00978, 0.02360, 0.00150, 0.01974, 0.00074]
cc2 = b'*\x11^XD]\x17QPQ\x14T\x1b\x17\x0c\x0cG\x0bCQL\r\tW\x18BV\x16\x16\rQACFYK^\\\x10J\x11SQJ\x16^\x13\x03]N\x17QV\x00FMPCJ\x16\x0f\x1c\x18^\nWS\x19ZYD\x19SPQWBU\t\x07B\x11RT\x16Dl|YDUOB\x0cV\x12\x17YS\x19VYN\x19XA\x14N\x03DE\x15Y\x0bD@]\x16\x03]\x18BQW\x16EOWCB^VGTS\x19BT]UB^\x0bBPBU_Y\x10J\x19WXUOB\x11P]\x16\x11WWV\x18~\x15\x11TZ]BY\x00\x14T\x10\x17Q\x18\x17\tLT\x16PXB\x11PWCFYK^\\\x17N^@X]B\\\x0b\rFBXV\x18\x10\x0ePK\x16VC\x10EH[\x0fVDP_YP\\\x11A[\x19\x0cXE\x01^\x17YDJ\x1dFXVR\x19B\rEV]CTX]\x1c2~W\x11A\\\\BZ\x00\x03_\x15_YT\x01Fp\x18ET_\x0e\x00\x18S\rU\x16p\x12K^WV\x15UU\x0e\x17\x04\x0e^\x0cR\x1e\x18-\x08\x19L^\\\x16\x0f\x00Y\\\x14Y_UW\x18CQT\x15UP\x10\x17\x0c\x11\x11\x04^\\T\r\x08^\x18APB\nELZ\x06\x11F\\@^BTT\x15[_BG\x17\r\\\x0bDU\x16n2Q]\x16M_\x0f\x00\x18F\x0bPB\x19_A\x17S^@FW\x07NE\x16P\tRC\x18\r\x15\x19TYWQB\x04VVCE^\\\x12OV@\x11ZR\x19\x0bCE\x0e^\x0cP\x1eqD\x05XUS\x19Y\x17\x11\x18]\r\x11BQW\x18TQPG]V\x16\x17\n\x04\x11\x16_U\x18\x02\x0fKKB\x19Q\x0e\x00Y_C^P\x19^QPQE\x19\x14X\x0cSE\x12D\x10DE]\x00FTA\x16OY\x1b\x04_WCE^K]MPQ\x11A\\\\B@\x0c\x0eU\x07E^]\x17\x15\\K\x16VPB\x12W@\x0fUE\x19^]VOX[S\x19\x0fNE\x16C\x03T[\x18\x0b\x08\x19UWWOB\x04\x18A\x17PD\x19SVS\x19AYUW\x07CK'
def count_p(text, charset):
count = {}
for each in text:
if each not in count:
count[each] = 1
else:
count[each] += 1
return count
def get_ic(text):
count = count_p(text)
length = len(text)
ic = 0
for each in count:
ic = ic + (count[each] - 1) * count[each]
return ic / (length * (length - 1))
def split_text(text, length):
strings = []
len_text = len(text)
for i in range(length):
this = []
for j in range(i, len_text, length):
this.append(text[j])
strings.append(bytes(this))
return strings
def break_key(text, length, decode_function, key_char, charset = (string.ascii_lowercase).encode()):
strings = split_text(text, length)
key = []
for i in range(length):
maxx = 0
this_char = ''
for each_char in key_char:
plain_text = decode_function(strings[i], each_char)
count = count_p(plain_text, charset)
total = len(plain_text)
this_p = []
for each in charset:
if each not in count:
this_p.append(0)
else:
this_p.append(count[each] / total)
tmp = 0
for j in range(26):
tmp += this_p[j] * naturalLanguageP[j]
if tmp > maxx:
(maxx, this_char) = (tmp, each_char)
key.append(this_char)
return bytes(key)
def f(a, b):
ret = []
for each in a:
ret.append(each ^ b)
return bytes(ret)
key = break_key(cc2, 32, f, (string.ascii_letters + string.digits).encode())
print(key)
import itertools
key = itertools.cycle(key)
print(bytes([next(key) ^ each for each in cc2]).decode())
然后根据明文的语法微调一下key就能解出