复盘 羊城杯 S2 2021 Misc 赛博德国人

发布于 2021-09-17  145 次阅读


题目评价

这题比赛的时候,一直卡在解密码机的那步,各种参数试来试去都不对。
如果不是很了解恩格尼密码机的话,根本没有做出来的可能。
一是密码机的变种版本太多,二是各个参数的用途并不清楚,这两个原因直接导致了出不来的时候无法怀疑是版本问题还是参数问题;另一方面,解出来的明文是没有空格的德文,但是德文和英文在书写方式上(基于a-z的书写)并没有区别,如果本身不会德语的话,基本上解出来了也不知道解对了。
所以出题人过来挨打。

WriteUp

打开题目压缩包,注意一下压缩包注释。

file

解压后,wireshark打开,过一眼后感觉ftp的相关数据很多,应该是ftp流量,所以配一个ftp || ftp-data过滤器。

file

直接看出ftp的账号和密码:rootd279186428a75016b17e4df5ea43d080

接着发现只有两个文件传输,都是上传。

file

file

第一个pdf追一下tcp流,改成原始数据,然后另存为GWHT_codebook.pdf

file

第二个txt直接复制明文就完事。

0911 = 1tle = 1tl = 350 = RZS NAJ =
nkfgp roqad boprv yrdhy zwamf qsrhb owqvt jzotr ffcjq snpqh kpwzm fprru gufez xsuws aohyw xbreu pifbz kagxj blbha jzixj zrasn zxkay lpaza ejwou itcip dfdgp rbjnv xuqzq qhtya xwwik wyybx kdgrc slrkj pgjay aidwa jeszp pbqat njojg jrplb kkhot joqpg vwecj soabm aupsr fenug ybxmr jloch kmjgc tznxl tnrqx pbeph fwymn gpoor pjkkb plkwb kxzeq quorp ipuvs utyae qyzgp mqnai iysse gzsht tsrmv crrkr opuxj tqshv ypdrw rvnzt cstlj 

file

打开pdf发现要密码,用刚才的ftp密码d279186428a75016b17e4df5ea43d080成功打开。

file

关键词Tag Walzenlage Ringstellung Steckerverbindungen Kenngruppen搜索发现前两个结果都是wiki,且和恩格尼密码机有关,那大概这个文件就是密码机的密码本了。

file

然后就是找工具进行解密了,我用的是py-enigma库,因为找了半天就它给的例子和题目最像。

User’s guide — Py-Enigma 0.1 documentation

file

跟着py-enigma库的例子,首先,RZS是初始位置,NAJ是密钥,或者可以理解为公钥,用于解密。

file

接着密文中的前五位nkfgp,取三位作为特征码,nkfkfgfgp,在密码本中搜索,发现10号的码表存在fgp这个特征码。(比赛的时候这一步就开始错了,因为密码本右上角写着SEPTEMBER 2021,然后TAG是德文里的意思,所以想当然地以为应该用11号的码表,然后又没仔细看这个库的例子,以为前五位也是需要解密的密文)

file

file

file

然后看一下上面的初始位置和公钥咋用。

file

大致就是设置一下初始位置,然后加密(实际上恩格尼密码机的加密和解码算法是一样的,就像RC4一样)一下公钥得到私钥,接着将初始位置设置成私钥,就可以用来解密密文得到明文了。

所以这个时候就可以编写脚本进行解密了。

from enigma.machine import EnigmaMachine
# 初始化密码机
machine = EnigmaMachine.from_key_sheet(
    rotors='II III I',
    reflector='B',
    ring_settings='5 21 25',
    plugboard_settings='AT BV CF EN GY HO IW LU MZ QX'
)
# 解密
initPosition = 'RZS'
publicKey = 'NAJ'
ciphertext = 'roqad boprv yrdhy zwamf qsrhb owqvt jzotr ffcjq snpqh kpwzm fprru gufez xsuws aohyw xbreu pifbz kagxj blbha jzixj zrasn zxkay lpaza ejwou itcip dfdgp rbjnv xuqzq qhtya xwwik wyybx kdgrc slrkj pgjay aidwa jeszp pbqat njojg jrplb kkhot joqpg vwecj soabm aupsr fenug ybxmr jloch kmjgc tznxl tnrqx pbeph fwymn gpoor pjkkb plkwb kxzeq quorp ipuvs utyae qyzgp mqnai iysse gzsht tsrmv crrkr opuxj tqshv ypdrw rvnzt cstlj'
ciphertext = ciphertext.replace(' ', '')  # py-enigma库不支持带空格的明文和密文
machine.set_display(initPosition)  # 设置初始位置 RZS
privateKey = machine.process_text(publicKey)  # 解密公钥 NAJ,获得私钥 PKS
print(privateKey)
machine.set_display(privateKey)  # 使用私钥 PKS 作为正式解密的初始值
plaintext = machine.process_text(ciphertext)
import textwrap
print(textwrap.fill(plaintext.lower(), 5).replace('\n', ' '))  # 解密后和格式处理

得到明文viers ieben fuenf siebe nvier achtf uenfv iersi ebenb ertad reise chsse chszw eidre ineun dreis echsd reisi ebend reizw eidre inull dreif uenfs echss echss echsf uenfd reisi ebend reifu enfdr eisie bendr eineu ndrei fuenf sechs sechs dreie insdr einul ldrei vierd reiac htdre ifuen fdrei siebe ndrei einss echsd reise chsse chsdr eivie rsech sdrei sechs sechs sechs zweid reisi ebend reinu lldre idrei siebe ndora

但是讲真,如果不会德语的话,这时候真的会觉得自己解对了吗?

然后根据德语中的0-9a-f的单词进行替换,即可得到十六进制文本475748547b36623936373230356665373537393566313034383537316366346366623730337d,解一下得到flag。

plaintext = plaintext.lower().replace('null', '0')\
    .replace('eins', '1')\
    .replace('zwei', '2')\
    .replace('drei', '3')\
    .replace('vier', '4')\
    .replace('fuenf', '5')\
    .replace('sechs', '6')\
    .replace('sieben', '7')\
    .replace('acht', '8')\
    .replace('neun', '9')\
    .replace('anton', 'a')\
    .replace('berta', 'b')\
    .replace('caesar', 'c')\
    .replace('dora', 'd')\
    .replace('emil', 'e')\
    .replace('friedrich', 'f')
print(plaintext)
print(bytes.fromhex(plaintext).decode())

GWHT{6b967205fe75795f1048571cf4cfb703}

题外话

在研究官方wp的时候,顺带写了个加密的脚本,可以加深理解。

from enigma.machine import EnigmaMachine
import textwrap
# 初始化密码机
machine = EnigmaMachine.from_key_sheet(
    rotors='II III I',
    reflector='B',
    ring_settings='5 21 25',
    plugboard_settings='AT BV CF EN GY HO IW LU MZ QX'
)
# 加密
initPosition = 'RZS'
privateKey = 'PKS'
plaintext = 'vier sieben fuenf sieben vier acht fuenf vier sieben berta drei sechs sechs zwei drei neun drei sechs drei sieben drei zwei drei null drei fuenf sechs sechs sechs fuenf drei sieben drei fuenf drei sieben drei neun drei fuenf sechs sechs drei eins drei null drei vier drei acht drei fuenf drei sieben drei eins sechs drei sechs sechs drei vier sechs drei sechs sechs sechs zwei drei sieben drei null drei drei sieben dora'
plaintext = plaintext.replace(' ', '')  # py-enigma库不支持带空格的明文和密文
machine.set_display(initPosition)  # 设置初始位置 RZS
publicKey = machine.process_text(privateKey)  # 加密私钥 PKS,获得公钥 NAJ
print(publicKey)
machine.set_display(privateKey)  # 使用私钥 PKS 作为正式加密的初始值
ciphertext = machine.process_text(plaintext)
print(textwrap.fill(ciphertext.lower(), 5).replace('\n', ' '))  # 加密后和格式处理
# 解密
initPosition = 'RZS'
publicKey = 'NAJ'
ciphertext = 'roqad boprv yrdhy zwamf qsrhb owqvt jzotr ffcjq snpqh kpwzm fprru gufez xsuws aohyw xbreu pifbz kagxj blbha jzixj zrasn zxkay lpaza ejwou itcip dfdgp rbjnv xuqzq qhtya xwwik wyybx kdgrc slrkj pgjay aidwa jeszp pbqat njojg jrplb kkhot joqpg vwecj soabm aupsr fenug ybxmr jloch kmjgc tznxl tnrqx pbeph fwymn gpoor pjkkb plkwb kxzeq quorp ipuvs utyae qyzgp mqnai iysse gzsht tsrmv crrkr opuxj tqshv ypdrw rvnzt cstlj'
ciphertext = ciphertext.replace(' ', '')  # py-enigma库不支持带空格的明文和密文
machine.set_display(initPosition)  # 设置初始位置 RZS
privateKey = machine.process_text(publicKey)  # 解密公钥 NAJ,获得私钥 PKS
print(privateKey)
machine.set_display(privateKey)  # 使用私钥 PKS 作为正式解密的初始值
plaintext = machine.process_text(ciphertext)
print(textwrap.fill(plaintext.lower(), 5).replace('\n', ' '))  # 解密后和格式处理
# 本题后续步骤
plaintext = plaintext.lower().replace('null', '0')\
    .replace('eins', '1')\
    .replace('zwei', '2')\
    .replace('drei', '3')\
    .replace('vier', '4')\
    .replace('fuenf', '5')\
    .replace('sechs', '6')\
    .replace('sieben', '7')\
    .replace('acht', '8')\
    .replace('neun', '9')\
    .replace('anton', 'a')\
    .replace('berta', 'b')\
    .replace('caesar', 'c')\
    .replace('dora', 'd')\
    .replace('emil', 'e')\
    .replace('friedrich', 'f')
print(plaintext)
print(bytes.fromhex(plaintext).decode())

The End