Syskron Security CTF Writeup

この大会は2019/10/9 7:00(JST)~2019/10/14 7:00(JST)に開催されました。
今回もチームで参戦。結果は4131点の満点で584チーム中29位でした。
自分で解けた問題をWriteupとして書いておきます。

History lesson in malware (Trivia 10)

ここで言っているマルウェアはStuxnetのこと。https://techtarget.itmedia.co.jp/tt/news/1705/09/news03.htmlなどに載っている。

{cve-2010-2568}

Making OPC UA secure (Trivia 10)

OPC UAUA Securityのご紹介の資料からSecurityモードがわかる。

https://www.jpcert.or.jp/ics/2011/20110210-fujii-sama.pdf
{none-sign-signandencrypt}

Using an OPC UA service (Trivia 10)

OPC UAUA Securityのご紹介の資料からサービスがわかる。

https://www.jpcert.or.jp/ics/2011/20110210-fujii-sama.pdf

暗号化するのはSecureチャネルのサービス。

{securechannel}

Insider attack 1 (Secret 1)

このジャンルの問題を解くのを了承するだけ。

{ok}

Industrial sightseeing tour 1 (OSINT 100)

写真に"KOH-I-NOOR HARDTMUTH"と書いてあるのが見える。文房具店らしい。その場所を調べる。

Ceske Budejovice
{CeskeBudejovice}

Access the device! (OSINT 200)

見えている文字(ETH, MRX)で検索すると、MRX3 LTE という製品が見つかる。"MRX3 LTE" default password で検索すると、マニュアルが見つかる。

http://www.insys-icom.cz/bausteine.net/f/10803/QIG_en_INSYS_MRX_LTE_190125.pdf?fd=0

これにデフォルトのユーザ名、パスワードが書いてある。

{insys-icom}

An e-mail and a link (Fun 100)

問題に記載のYouTubeのページのコメントにやたら"no reason"と書いてある。

{noreason}

The pasted leak (Forensics 700)

https://pastebin.com/6U36KmG0にアクセスすると、以下の内容がpasteされている。

traciduke:3a7a5871998e43741d09571f779f18d9
uwilliams:f67793a860600a2e73f7cb58ed3c9f25
diazrhonda:21fd63743deefd2380305ba5dc3932a0
chendana:472eccb32d0d9419903587934b2fc68a
melaniecarr:516373b9c689d6dd28c62e245c58c035
thompsonsheri:7ad71e2f104c6e670888bbf59ada4d15
dennis40:466df7524db6a9270135162d7df1bbb3
nfrye:b1201c10488c081de2a423390514a8d3
torresmaria:d39a6996e054f90a507a812e8cb88067
nfrederick:4316dee282545d285ff2d4e0fa777698

https://hashkiller.co.uk/Cracker/MD5でクラックする。

3a7a5871998e43741d09571f779f18d9 [No Match]
f67793a860600a2e73f7cb58ed3c9f25 [No Match]
21fd63743deefd2380305ba5dc3932a0 [No Match]
472eccb32d0d9419903587934b2fc68a [No Match]
516373b9c689d6dd28c62e245c58c035 [No Match]
7ad71e2f104c6e670888bbf59ada4d15 [No Match]
466df7524db6a9270135162d7df1bbb3 MD5 pa$$2019
b1201c10488c081de2a423390514a8d3 [No Match]
d39a6996e054f90a507a812e8cb88067 [No Match]
4316dee282545d285ff2d4e0fa777698 [No Match]

KeePassで添付のkdbxを開き、Master Passwordとしてpa$$2019を指定する。パスワードが通り、内容を見ることができた。
f:id:satou-y:20191020165347p:plain
important-noteを見ると、URLが書いてある。

https://0bin.net/paste/8OroFXcfz9P-+uN9#UJiJ2F7SsuvS3zgxzI00vw8rehyoPT0+CdShZmgN8L6

ここにアクセスすると、フラグが書いてあった。
f:id:satou-y:20191020165422p:plain

{n3v3r_r3cycl3_y0ur_53cr37_p455w0rd5}

Schlamperei (Crypto 200)

setup_customerID_9721.zipはパスワードがかかっているが、"9721"で解凍できた。
展開されたファイル群の中にsessionkey_2fishecb.txt.gpgがあるので、復号してみる。
まず71062c43B022BE72_public-key.txtは秘密鍵なので、インポートする。

$ gpg --import 71062c43B022BE72_public-key.txt
gpg: 鍵B022BE72: 公開鍵"MYF4N74571CM4CH1N3C0rP (MFMC)"をインポートしました
gpg: 鍵B022BE72: 秘密鍵をインポートしました
gpg: 鍵B022BE72:"MYF4N74571CM4CH1N3C0rP (MFMC)"変更なし
gpg: 処理数の合計: 2
gpg:               インポート: 1  (RSA: 1)
gpg:              変更なし: 1
gpg:       秘密鍵の読み込み: 1
gpg:   秘密鍵のインポート: 1
$ gpg --output sessionkey_2fishecb.txt --decrypt sessionkey_2fishecb.txt.gpg

次のユーザの秘密鍵のロックを解除するには
パスフレーズがいります:"MYF4N74571CM4CH1N3C0rP (MFMC)"
2048ビットRSA鍵, ID B022BE72作成日付は2019-09-15

gpg: 無効なパスフレーズです。再入力してください ...

パスフレーズが必要のようだ。
README.txtを見ると以下の文がある。

NOTE: The old default encryption password 'VMC' has been replaced since 09/2018. Please use the new one.

VMCというパスワードが変わったとのこと。'MFMC'になったと考えられる。

$ gpg --output sessionkey_2fishecb.txt --decrypt sessionkey_2fishecb.txt.gpg

次のユーザの秘密鍵のロックを解除するには
パスフレーズがいります:"MYF4N74571CM4CH1N3C0rP (MFMC)"
2048ビットRSA鍵, ID B022BE72作成日付は2019-09-15

gpg: 2048-ビットRSA鍵, ID B022BE72, 日付2019-09-15に暗号化されました
      "MYF4N74571CM4CH1N3C0rP (MFMC)"

パスフレーズに"MFMC"を指定すると、復号できた。

$ cat sessionkey_2fishecb.txt
A0 C9 18 74 33 F2 2C 00 83 2B 1E 99 22 10 1A 6A

ファイル名からTwofish暗号(ECBモード)の鍵と考えられる。message.txtの内容をこの鍵で復号する。

from twofish import Twofish

with open('message.txt', 'r') as f:
    ct = f.read().replace(' ', '').decode('hex')

with open('sessionkey_2fishecb.txt', 'r') as f:
    key = f.read().replace(' ', '').decode('hex')

T = Twofish(key)

pt = ''
for i in range(0, len(ct), 16):
    pt += T.decrypt(ct[i:i+16])
print pt

復号結果は以下の通り。

If you reveal your secrets to the wind, you should not blame the wind for revealing them to the trees.
{silence_is_golden}
{silence_is_golden}

Enhanced PLC Encryption Standard (Crypto 600)

とりあえずどんな暗号処理をしているか日本語に直訳。

1.すべてのPLCが共有シークレットパスワードを取得します。
 これは非常に長いため、誰もそれをブルートフォースできません。
2.2つのデバイスが通信したい場合、1つ(A)が他のデバイス(B)に固有のチャレンジを送信します。
3.Bはチャレンジを取得し、チャレンジのパスワードの各文字をハッシュします:
 response = hash(char + challenge)
4.セキュリティのため、ここではSHA-256を使用します(安全でないMD5またはSHA-1は使用しません!)。 
 また、各文字を個別にハッシュするため、攻撃者が応答を記録した場合に完全なパスワードが
 漏洩することはありません。
5.BはAに多くの応答を送り返します。Aはパスワードの長さとパスワード自体を知っています。 
 したがって、応答がない場合、または応答が多すぎる場合、Aは接続を終了できます。
6.Aはハッシュ(char +チャレンジ)も実行し、すべての応答を比較します。 
 不一致がある場合、Aは接続を終了します。
7.すべての応答が一致すると、AとBは共有秘密パスワードをキーとして使用して通信を開始します。 
 ここでは、PLCが軍事グレードのAESをサポートしていないため、CBCモードで3DESを使用しています。

ハッシュにする際、1文字+チャレンジコードに対して行っているのでブルートフォースで割り出すことができる。これで秘密鍵がわかり、3DES-CBC、IVは\x00*8で暗号化しているので、復号できる。

import hashlib
from Crypto.Cipher import DES3

def unpad(s):
    return s[:-ord(s[-1])]

challenge = 'VkcV29UKCGbfuZyqea7uKbZ9'
h = [
    '5daaa90b563017184bb8dc277f63f02366a59519113b9ac87ba6fd46f93dc1ff',
    '01b4e096bcb756f176beaa2ebbb99ef144dc3fb0bc2d27e5fe63a5601e3abace',
    'db00873b16b99e32c6c67672ea52df6769cf7801ebb3dbf168f5b2e0f2ecc3bf',
    '146797c2afa9e1a2ad2ff8f05de647702949923f9a5dc12b26452b2c520c3340',
    '67dacf84cce58a6bf283d62354ad05052fe42808b59866dfb30137a08b4ff12d',
    'c13dcb97dccf5e3942324409202a103eb9f007866f247ebea48e2a67cbbcd07f',
    'd72a057ba7fddd03cae3d3f7d75f865fb1c2ddbe8ef65afc0ce8fbc0fc4122cb',
    '6eddcbed70839add89ed38c3068ffe6780f7b86f0bc7276e2d7e06f47ea2e05a',
    '146797c2afa9e1a2ad2ff8f05de647702949923f9a5dc12b26452b2c520c3340',
    'd72a057ba7fddd03cae3d3f7d75f865fb1c2ddbe8ef65afc0ce8fbc0fc4122cb',
    'db00873b16b99e32c6c67672ea52df6769cf7801ebb3dbf168f5b2e0f2ecc3bf',
    '4c1b4d5c926c4160b19effa23c93710f3086866a74aca5dad801fd81118d8d68',
    '67dacf84cce58a6bf283d62354ad05052fe42808b59866dfb30137a08b4ff12d',
    '4d4ac18fd35d3707fc3671d372bbe494691b01611632359c7d39b7becbfc1184',
    '4d4ac18fd35d3707fc3671d372bbe494691b01611632359c7d39b7becbfc1184',
    '571aef6ff2d25a7a32c3a9fc3b1c06d874979f082b5e90b0c30a01203885a2b0',
    'fdc984b4a8fec04fcb9faacf99f9dbfd0fbef0a33906c3fa89d9fb0b63947a0e',
    '146797c2afa9e1a2ad2ff8f05de647702949923f9a5dc12b26452b2c520c3340',
    '588917d1f04bbc53aed45c6db061092dde79af4c5fc3f01e96eab2e86b30e581',
    'a122c3b77eaf01341cc0c7da6e45e7ff9ff57f97a4c9542ad7e96a0f28499029',
    'fdc984b4a8fec04fcb9faacf99f9dbfd0fbef0a33906c3fa89d9fb0b63947a0e',
    '8419e8ddded5e57e71db42841f865f9fd751ec3b8e0395ba36818b52a015e47e',
    'af621e444935d03bc563e24982ad25d19c3ca4f52341232c978f7e63c809a27e',
    '572c394ed63437090aec71c806d92a2a10d5e3651eb30a91d1573ba3d37f4ad9'
]

password = ''
for i in range(24):
    for code in range(32, 127):
        text = chr(code) + challenge
        if hashlib.sha256(text).hexdigest() == h[i]:
            password += chr(code)
            break

print 'password =', password

IV = '0' * 8

ct1 = '24066241b2c524457a58196640197307469c18fd71bb6de304501a8d50981e25'
cipher = DES3.new(password, DES3.MODE_CBC, IV)
pt1 = unpad(cipher.decrypt(ct1.decode('hex')))
print pt1

ct2 = '07d89c21b9dd1eb81e26d52398da02a3d000ba82f9198b2b3311cb1cda901418'
cipher = DES3.new(password, DES3.MODE_CBC, IV)
pt2 = unpad(cipher.decrypt(ct2.decode('hex')))
print pt2

実行結果は以下の通り。

password = ultras3cr3tpa$$w0rd2019!
EPES HANDSHAKE SUCCESSFUL
{never-roll-your-own-crypto}
{never-roll-your-own-crypto}