WhaleCTF 密碼學_Writeup

期末事情有點多,新年放一波完整wp



Death_Chain

夏多密碼,對照翻譯即可,參考鏈接如下
https://www.33iq.com/group/topic/242254/
PS:所有的變換都要以原圖中的爲準,而不是基於上一次變換

先有什麼

對着鍵盤畫圈圈

檢查符號

摩斯密碼,寫個替換腳本再翻譯即可

# -*- coding: utf-8 -*-
# __author__: Pad0y

import re
s = "o00。o。o0oo。0o0o。000。00。o。0。000。ooo0。o。0o。oo0。ooo。0o0o。0。oo0o"
a = ["。", "o","0"]
b = [" ", ".","-"]
dic = dict(zip(a,b))
pattern = re.compile('(' + '|'.join(a) + ')')
s = pattern.sub(lambda a:dic[a.group()], s)
print(s)

德軍密碼

費娜姆密碼,密文每7個一組,與密鑰進行異或處理,詳情看這裏,腳本如下

# coding=utf-8
# __author__: Pad0y

import re


def b2ten(string):
    split = re.findall(r'.{7}', string)
    ten = []
    for i in split:
        ten.append(int(i, base=2))
    return ten

bin_t= '0000011000000000101010110111001011000101100000111001100100111100111001'
private_key= 'helloworld'
b2ascii = map(ord, private_key)
a_ten = b2ten(bin_t)
c = []
for i in range(len(a_ten)):
    c.append(a_ten[i] ^ b2ascii[i])
print ''.join(map(chr, c))

密鑰生成

找個工具算下

規則很公平

tips是公平的遊戲規則,想到了Playfair密碼,也是一種古典密碼,基於字符替換的密碼。參考如下

Playfair算法是基於一個5*5的字母矩陣,
題目中CGOCPMOFEBMLUNISEOZY是密文,
關鍵詞矩陣題目已經構造好了,比較省事。

  • 矩陣構造規則
按從左到右、從上到下順序
填入關鍵詞的字母(去除重複字母)後
將字母表其作餘字母填入
  • 加密規則
Playfair加密算法是先將明文按兩個字母一組進行分組,然後在矩陣中找對應的密文。

取密文的規則如下:
- 若明文出現相同字母在一組,則在重複的明文字母中插入
  一個填充字母(eg:z)進行分隔後重新分組(eg: balloon被重新分組爲ba lz lo on)
  
- 若分組到最後一組時只有一個字母,則補充字母z
  若明文字母在矩陣中同行,則循環取其右邊下一個字母爲密文
  (矩陣最右邊的下一個是最左邊的第一個)(eg: ar被加密爲RM)
  
- 若明文字母在矩陣中同列,則循環取其下邊下一個字母爲密文
  (矩陣最下邊的下一個是最上邊的第一個)(eg: mu被加密爲CM)
  
- 若明文字母在矩陣中不同行不同列,則取其同行且與同組另一字
  母同列的字母爲密文(eg: hs被加密爲BP,ea被加密爲IM或JM)

爲了方便用numpy構造下5*5的關鍵詞矩陣(其實是懶的畫圖)

# coding=utf-8
# __author__: Pad0y

import numpy as np
s = 'CULTREABDFGHIKMNOPQSVWXYZ'
print(np.array(list(s)).reshape(5, 5))

"""
手動解密對照如下,python兩行代碼的事情(pycipher)

[['C' 'U' 'L' 'T' 'R']
 ['E' 'A' 'B' 'D' 'F']
 ['G' 'H' 'I' 'K' 'M']
 ['N' 'O' 'P' 'Q' 'S']
 ['V' 'W' 'X' 'Y' 'Z']]
 """
# 從矩陣可以看出密鑰是CULTRE
# CG OC PM OF EB ML UN IS EO ZY

柵欄加密

根據題目描述是分成了3根柵欄,將密文每3個字符一組,將第一列與第二列換一下位置(用notepad/sublime的列編輯模式很容易做到),得到如下

duJ
mZl
V2Y
uVW
dkx
XXs
N2e
D1V
V59
EXs
Z2d
7ZW
SlN
Vbr
9me
DNS
alF
GX9
F1

然後將所得到的字符串從上到下從左至右組合在一起再base64解碼即可
numpy很容易就可以實現轉換過程,解密腳本如下

# -*- coding: utf-8 -*-
# __author__: Pad0y

import numpy as np
import base64

s = 'udJZml2VYVuWkdxXXs2Ne1DV5V9XEs2ZdZ7WlSNbVrm9eNDSlaFXG91F*'  # 字符串末尾補充*,構造19*3的矩陣
arr = np.array(list(s)).reshape(19, 3).T  # 矩陣轉置
arr[[0, 1], :] = arr[[1, 0], :]  # 交換第一行和第二行的數據
f = arr.flat  # 數組扁平化
tmp = ''
for item in f:
    tmp += item
dec = base64.b64decode(tmp[:len(tmp)-1])  # 去掉補充的字符再解碼
print(dec)

小明入侵

思路就是對管理員密碼進行窮舉加密,再和所給的部分md5比較,由於md5的特性若是前10位符合基本就是所得密碼,爆破腳本如下:

# -*- coding: utf-8 -*-
# __author__: Pad0y

import string
import hashlib

s = 'a74be8e20b'
chars = string.ascii_letters + string.digits  # 構造字符集
for i in chars:
    for j in chars:
        for k in chars:
            for n in chars:
                psw = 'key{' + i + j + k + n + '}'
                md5 = hashlib.md5(psw.encode(encoding='utf-8')).hexdigest()
                if s in md5:
                    print(psw, md5)

數學小問題

在這裏插入圖片描述
這道題被坑的不慘,一開始沒注意密文還夾雜着個1,看到圖片很容易想到是仿射密碼,但是仿射密碼不可能存在數字。仿射密碼m在這裏限制爲26,因此a的乘法逆元可能性只有12種,算上b偏移量26,密鑰空間爲12*26=312個,懶得寫算法,直接調用解密網站接口爆破。爲了不增加網站負擔,只放部分源碼,提供密鑰K(5,8)

關於仿射密碼詳見此處

求a逆元函數如下

# -*- coding: utf-8 -*-
# __author__: Pad0y

import math
from Crypto.Util.number import inverse


def rev_a(m):
    a = []  # a 與 26互模集合
    rev_a = []  # 逆元集合
    for i in range(1, m + 1):
        if math.gcd(i, m) == 1:
            a.append(i)
    for i in a:
        rev_a.append(inverse(i, m))
    return rev_a

此處應寫

_____*((__//__+___+______-____%____)**((___%(___-_))+________+(___%___+_____+_______%__+______-(______//(_____%___)))))+__*(((________/__)+___%__+_______-(________//____))**(_*(_____+_____)+_______+_________%___))+________*(((_________//__+________%__)+(_______-_))**((___+_______)+_________-(______//__)))+_______*((___+_________-(______//___-_______%__%_))**(_____+_____+_____))+__*(__+_________-(___//___-_________%_____%__))**(_________-____+_______)+(___+_______)**(________%___%__+_____+______)+(_____-__)*((____//____-_____%____%_)+_________)**(_____-(_______//_______+_________%___)+______)+(_____+(_________%_______)*__+_)**_________+_______*(((_________%_______)*__+_______-(________//________))**_______)+(________/__)*(((____-_+_______)*(______+____))**___)+___*((__+_________-_)**_____)+___*(((___+_______-______/___+__-_________%_____%__)*(___-_+________/__+_________%_____))**__)+(_//_)*(((________%___%__+_____+_____)%______)+_______-_)**___+_____*((______/(_____%___))+_______)*((_________%_______)*__+_____+_)+___//___+_________+_________/___

看起來是個數學填空題,口算是不存在的,上腳本

# -*- coding: utf-8 -*-
# __author__: Pad0y

with open('txt', 'r') as f:
    s = f.read()
count = 0
exp = ''
for i in s:
    if i is s[0]:
        count += 1
    else:
        if count != 0:
            exp += str(count)
            count = 0
            exp += i
        else:
            exp += i

if count != 0:
    exp += str(count)
exp = exp.replace('//', '/')  # 文本中除號做轉義需要去掉
print(exp + '\n' * 4, int(eval(exp)))

"""
提交結果不對摺騰了半小時,丟到小葵做各種蜜汁轉換,得到key
只需要轉爲16進制再轉字符即可
"""

後來發現個更騷氣的東西,下劃線個數代表對應數字,不用爆破

import binascii
_ = 1
__ = 2
___ = 3
____ = 4
_____ = 5
______ = 6
_______ = 7
________ = 8
_________ = 9

a = _____*((__//__+___+______-____%____)**((___%(___-_))+________+(___%___+_____+_______%__+______-(______//(_____%___)))))+__*(((________/__)+___%__+_______-(________//____))**(_*(_____+_____)+_______+_________%___))+________*(((_________//__+________%__)+(_______-_))**((___+_______)+_________-(______//__)))+_______*((___+_________-(______//___-_______%__%_))**(_____+_____+_____))+__*(__+_________-(___//___-_________%_____%__))**(_________-____+_______)+(___+_______)**(________%___%__+_____+______)+(_____-__)*((____//____-_____%____%_)+_________)**(_____-(_______//_______+_________%___)+______)+(_____+(_________%_______)*__+_)**_________+_______*(((_________%_______)*__+_______-(________//________))**_______)+(________/__)*(((____-_+_______)*(______+____))**___)+___*((__+_________-_)**_____)+___*(((___+_______-______/___+__-_________%_____%__)*(___-_+________/__+_________%_____))**__)+(_//_)*(((________%___%__+_____+_____)%______)+_______-_)**___+_____*((______/(_____%___))+_______)*((_________%_______)*__+_____+_)+___//___+_________+_________/___
a = hex(a)[2:][:-1]
a = binascii.a2b_hex(a)
print a

RSA分解

解壓得到密文和公鑰,對公鑰解析得到e = 65537(0x10001)
n=0xA41006DEFD378B7395B4E2EB1EC9BF56A61CD9C3B5A0A73528521EEB2FB817A7
在這裏插入圖片描述

用msieve(yafu也行)分解n得到
p = 258631601377848992211685134376492365269
q = 286924040788547268861394901519826758027
在這裏插入圖片描述
腳本如下

在這裏插入圖片描述

RSA分解

  • 解題思路:給出了公鑰對對密文進行遍歷解密即可
N = 920139713 E = 19,分解N,得到

fac: factoring 920139713
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
div: primes less than 10000
fmt: 1000000 iterations
Total factoring time = 0.0421 seconds
***factors found***
P5 = 49891
P5 = 18443
ans = 1

可求出d=96849619 所以私鑰爲(920139713,96849619)

# -*- coding: UTF-8 -*-
# __author__:Pad0y
n = 920139713
d = 96849619
result = []
with open("rsa.txt") as f:
    for i in f:
        result.append(chr(pow(int(i),d,n)))
print(result)

只有密文

思路:計算q=n/p,對比找出小的那個質數,找到n與200組密文的最大公約數即相當於對n做了分解

  1. 把文本里第一個數據去掉,剩下200個密文,計算p
# -*- coding: UTF-8 -*-
# __author__:Pad0y

import re

with open('ciphertext.txt', 'r') as f:
    content = f.readlines()
    e = []
    for line in content:
        res = re.findall('^\d+', line)
        if len(res) > 0:
            e.append(int(res[0]))

n = 135176830582884945708175419898330054260341730432046991449072509302750602166218145078102928897914789996197402658592881347572949256377161172079344803330624352445165759925647345536051853372740246104804540179716136644319380454884518397455488002758429914465640804944658049262500561494830899678619427468784748988379


def divisors(m, n):
    c = 1
    while c != 0:
        c = m % n
        m = n
        n = c
    return m


if __name__ == '__main__':
    for i in range(len(e)):
        print(str(i + 1) + ':' + str(divisors(n, e[i])))

在這裏插入圖片描述

最後只有第96組和n最大公約數不是1,即p=13038371855775914836995578093728166671103633520203033965827703187246607207039273968425501296569317295959057439253867586769212037981452712871242668046329877

  1. 計算q

q=n/p,得到q=10367615840240242845371941453623373821227053765532752994306127876946421006862147600725324340607889088707606730457021312059130583835286311559997627141422127

  1. 比較大小,得到q比較小,進行md5加密
# -*- coding: UTF-8 -*-
# __author__: Pad0y

import hashlib

q = 10367615840240242845371941453623373821227053765532752994306127876946421006862147600725324340607889088707606730457021312059130583835286311559997627141422127
m = hashlib.md5()
m.update(str(q).encode('ascii'))
enc = m.hexdigest()
print('key{' + enc[:8] + '}')

算法問題

運行下發現結果和給的enc文本一樣,果斷把源碼上的flag丟上去(捂臉)


大家來解密

CBC模式的AES加密,AES共有五種加密模式(ECB,CBC,PCBC,CFB,OFB,CTR),其中CBC是公認最安全的模式
KEY=venusCTF-hex IV=123-MD5代表分別把key和偏移量轉化爲後面格式
IV在md5後長度是32位,需要對key填充之相應位數再編碼

# -*- coding: utf-8 -*-
# __author__: Pad0y

import binascii
import hashlib
from Crypto.Cipher import AES

KEY = b'venusCTF'
IV = '123'

iv = hashlib.md5(IV.encode(encoding='utf-8')).hexdigest()
key = (KEY * 4).hex()
c = 'a80d5eb43508e549f83e2e254c0a0f0644be58f453baced4af4777c4cd1b7575'

k = binascii.unhexlify('76656e757343544676656e757343544676656e757343544676656e7573435446')
key_ = binascii.unhexlify(key)
iv_ = binascii.unhexlify(iv)
C = binascii.unhexlify(c)
aes = AES.new(key_, AES.MODE_CBC, iv_)
print(aes.decrypt(C))

RSA專家

得到私鑰和密文,丟到openssl
在這裏插入圖片描述

AK

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章