1. 程式人生 > >南郵CTF平臺-Vigenere題解(窮舉法)

南郵CTF平臺-Vigenere題解(窮舉法)

題目提示使用重合因子,但是我嘗試失敗了

以下為解題思路:

假設加密者使用的金鑰為vigenerekey,長度為keylen,密文為字串arr

那麼字串subarr0=arr[0]+arr[keylen]+arr[keylen*2]……是被vigenerekey[0]解密的內容

依次類推,subarr1=arr[1]+arr[1+keylen]+arr[1+keylen*2]……是被vigenerekey[1]解密的內容

subarr2=arr[2]+arr[2+keylen]+arr[2+keylen*2]……是被vigenerekey[2]解密的內容

……

按照這種分割方法,可以把密文arr分割成keylen份,每份都被金鑰vigenerekey的同一位解密

比如:

金鑰為WXYZ,密文為abcdefghijklmnop

那麼解密過程為

abcdefghijklmnop
WXYZWXYZWXYZWXYZ
subarr0=aeim

subarr1=bfjn

以此類推

對每個vigenerekey[index],窮舉vigenerekey[index]的值(範圍是0x00~0xFF),並與對應的subarr裡的內容解碼,找到能將該subarr所有內容解為可見字元的所有可能值

python程式碼如下:

def findindexkey(subarr):#該函式可以找出將密文subarr解密成可見字元的所有可能值
    visiable_chars=[]#可見字元
    for x in range(32,126):
        visiable_chars.append(chr(x))
    #print(vi)
    test_keys=[]#用於測試金鑰
    ans_keys=[]#用於結果的返回
    for x in range(0x00,0xFF):# 列舉金鑰裡所有的值
        test_keys.append(x)
        ans_keys.append(x)
    for i in test_keys:#對於0x00~0xFF裡的每一個數i和subarr裡的每個值s異或
        for s in subarr:
            if chr(s^i) not in visiable_chars:#用i解密s,如果解密後明文不是可見字元,說明i不是金鑰
                ans_keys.remove(i)#去掉ans_keys裡測試失敗的金鑰
                break
    #print(ans_keys)
    return ans_keys

strmi='F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923C\
AB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF\
5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84C\
C931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D96\
3FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47E\
FD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5\
923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA\
36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63C\
ED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A8\
5A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250\
C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794'
arr=[]#密文,每個元素為字元的ascii碼
for x in range(0,len(strmi),2):
    arr.append(int(strmi[x:2+x],16))


for keylen in range(1,14):#列舉金鑰的長度1~14
    for index in range(0,keylen):#對金鑰裡的第index個進行測試
        subarr=arr[index::keylen]#每隔keylen長度提取密文的內容,提取出來的內容都被密文的第index個加密
        ans_keys=findindexkey(subarr)#找出金鑰中第index個的可能的值
        print('keylen=',keylen,'index=',index,'keys=',ans_keys)
        if ans_keys:#如果金鑰第index個有可能存在,嘗試用金鑰的index個去解密文
            ch=[]
            for x in ans_keys:
                ch.append(chr(x^subarr[0]))
            print(ch)

此處輸出的結果為

keylen= 1 index= 0 keys= []
keylen= 2 index= 0 keys= []
keylen= 2 index= 1 keys= []
keylen= 3 index= 0 keys= []
keylen= 3 index= 1 keys= []
keylen= 3 index= 2 keys= []
...省略中間的輸出...
keylen= 7 index= 0 keys= [168, 169, 174, 175, 178, 179, 184, 185, 186, 187, 190, 191]
['Q', 'P', 'W', 'V', 'K', 'J', 'A', '@', 'C', 'B', 'G', 'F']
keylen= 7 index= 1 keys= [10, 11, 26, 27, 28, 29, 30, 31, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95]
['g', 'f', 'w', 'v', 'q', 'p', 's', 'r', '/', '.', ')', '(', '+', '*', '%', '$', "'", '&', '!', ' ', '=', '<', '?', '>', '9', '8', ';', ':', '5', '4', '7', '6', '1', '0', '3', '2']
keylen= 7 index= 2 keys= [132, 133, 144, 145, 146, 147, 148, 149, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223]
['l', 'm', 'x', 'y', 'z', '{', '|', '}', '(', ')', '*', '+', ',', '-', '.', '/', ' ', '!', '"', '#', '$', '%', '8', '9', ':', ';', '<', '=', '>', '?', '0', '1', '2', '3', '4', '5', '6', '7']
keylen= 7 index= 3 keys= [166, 167, 176, 177, 178, 179, 180, 181, 182, 183]
['d', 'e', 'r', 's', 'p', 'q', 'v', 'w', 't', 'u']
keylen= 7 index= 4 keys= [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 80, 81, 82, 83, 86, 87]
['%', '$', '#', '"', '!', ' ', '/', '.', '-', ',', ')', '(', '7', '6', '5', '4', '3', '2', '1', '0', '?', '>', '=', '<', ';', ':', '9', '8', 'w', 'v', 'u', 't', 'q', 'p']
keylen= 7 index= 5 keys= [128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 200, 201, 204, 205, 206, 207, 216, 217]
['"', '#', ' ', '!', '&', "'", '$', '%', '*', '+', '(', ')', '.', '/', ',', '-', '2', '3', '6', '7', '4', '5', ':', ';', '8', '9', '>', '?', '<', '=', 'j', 'k', 'n', 'o', 'l', 'm', 'z', '{']
keylen= 7 index= 6 keys= [58, 59, 60, 61, 62, 63]
['c', 'b', 'e', 'd', 'g', 'f']
keylen= 8 index= 0 keys= []
...省略中間的輸出...
keylen= 13 index= 12 keys= []

可以看出,keylen=7時有結果

不妨縮小一下範圍,假設明文只有字母、數字、空格、逗號和句號,再進行窮舉

該部分的python程式碼如下:

import string
def findindexkey2(subarr):#再造一個函式篩選金鑰
    test_chars=string.ascii_letters+string.digits+','+'.'+' '#將檢查的字元改為英文+數字+逗號+句號+空格
    #print(test_chars)
    test_keys=[]#用於測試金鑰
    ans_keys=[]#用於結果的返回
    for x in range(0x00,0xFF):# 列舉金鑰裡所有的值
        test_keys.append(x)
        ans_keys.append(x)
    for i in test_keys:#對於0x00~0xFF裡的每一個數i和substr裡的每個值s異或
        for s in subarr:
            if chr(s^i) not in test_chars:#用i解密s,如果解密後不是英文、數字、逗號、句號、空格,說明i不是金鑰
                ans_keys.remove(i)#去掉ans_keys裡測試失敗的金鑰
                break
    #print(ans_keys)
    return ans_keys

vigenerekeys=[]#維基尼爾密碼的金鑰
for index in range(0,7):#已經知道金鑰長度是7
    subarr=arr[index::7]
    vigenerekeys.append(findindexkey2(subarr))
print(vigenerekeys)

該部分的輸出結果:

[[186], [31], [145], [178], [83], [205], [62]]

很幸運,結果已經是唯一的了,這就是金鑰

我們再使用這個金鑰去解密密文,Python的程式碼如下:

ming=''
for i in range(0,len(arr)):
    ming=ming+chr(arr[i]^vigenerekeys[i%7][0])
print(ming)

解密的結果為:

Cryptography is the practice and study of techniques for, among other things, secure communication in the presence of attackers. Cryptography has been used for hundreds, if not thousands, of years, but traditional cryptosystems were designed and evaluated in a fairly ad hoc manner. For example, the Vigenere encryption scheme was thought to be secure for decades after it was invented, but we now know, and this exercise demonstrates, that it can be broken very easily.

文章已經解密出來了

按照題目的要求,flag為最後6個單詞,不含標點,因此flag:nctf{it can be broken very easily}

比較遺憾的是,我解決這題的是偶並沒有用到重合因子,而是使用窮舉。我想題目設計的本意是讓解題者使用字母出現頻率之類的統計學原理進行解題吧

以下是整段程式原始碼:

def findindexkey(subarr):#該函式可以找出將密文subarr解密成可見字元的所有可能值
    visiable_chars=[]#可見字元
    for x in range(32,126):
        visiable_chars.append(chr(x))
    #print(vi)
    test_keys=[]#用於測試金鑰
    ans_keys=[]#用於結果的返回
    for x in range(0x00,0xFF):# 列舉金鑰裡所有的值
        test_keys.append(x)
        ans_keys.append(x)
    for i in test_keys:#對於0x00~0xFF裡的每一個數i和subarr裡的每個值s異或
        for s in subarr:
            if chr(s^i) not in visiable_chars:#用i解密s,如果解密後明文不是可見字元,說明i不是金鑰
                ans_keys.remove(i)#去掉ans_keys裡測試失敗的金鑰
                break
    #print(ans_keys)
    return ans_keys

strmi='F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923C\
AB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF\
5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84C\
C931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D96\
3FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47E\
FD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5\
923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA\
36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63C\
ED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A8\
5A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250\
C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794'
arr=[]#密文,每個元素為字元的ascii碼
for x in range(0,len(strmi),2):
    arr.append(int(strmi[x:2+x],16))


for keylen in range(1,14):#列舉金鑰的長度1~14
    for index in range(0,keylen):#對金鑰裡的第index個進行測試
        subarr=arr[index::keylen]#每隔keylen長度提取密文的內容,提取出來的內容都被密文的第index個加密
        ans_keys=findindexkey(subarr)#找出金鑰中第index個的可能的值
        print('keylen=',keylen,'index=',index,'keys=',ans_keys)
        if ans_keys:#如果金鑰第index個有可能存在,嘗試用金鑰的index個去解密文
            ch=[]
            for x in ans_keys:
                ch.append(chr(x^subarr[0]))
            print(ch)
#執行到這裡,觀察輸出可以發現,金鑰長度為7時有解
print('###############')
import string
def findindexkey2(subarr):#再造一個函式篩選金鑰
    test_chars=string.ascii_letters+string.digits+','+'.'+' '#將檢查的字元改為英文+數字+逗號+句號+空格
    #print(test_chars)
    test_keys=[]#用於測試金鑰
    ans_keys=[]#用於結果的返回
    for x in range(0x00,0xFF):# 列舉金鑰裡所有的值
        test_keys.append(x)
        ans_keys.append(x)
    for i in test_keys:#對於0x00~0xFF裡的每一個數i和substr裡的每個值s異或
        for s in subarr:
            if chr(s^i) not in test_chars:#用i解密s,如果解密後不是英文、數字、逗號、句號、空格,說明i不是金鑰
                ans_keys.remove(i)#去掉ans_keys裡測試失敗的金鑰
                break
    #print(ans_keys)
    return ans_keys

vigenerekeys=[]#維基尼爾密碼的金鑰
for index in range(0,7):#已經知道金鑰長度是7
    subarr=arr[index::7]
    vigenerekeys.append(findindexkey2(subarr))
print(vigenerekeys)#輸出的是[[186], [31], [145], [178], [83], [205], [62]].


print("#########")
ming=''
for i in range(0,len(arr)):
    ming=ming+chr(arr[i]^vigenerekeys[i%7][0])
print(ming)