はじめに
あまり時間が取れなかったのですが, 久々に1000点もいれることができたのでWriteup(とflagに至るまでの試行錯誤など)を綴っていきます.
Onion Layer Encoding(Misc, 100pt)
Encoding is not encryption, but what if I just encode the flag with base16,32,64? If I encode my precious flag for 150 times, surely no one will be able to decode it, right?
リード文の通りです. flagをBase16, Base32, Base64でひたすらエンコーディングしまくったファイルが渡されます. ファイルはGitHubにあげています.
以下のサイト
CTF Crypto - A painter and a black cat1
を見ると,
・Base16:使用文字は 0-9A-F
・Base32:使用文字は A-Z2-7
・Base64:使用文字は A-Za-z0-9+/
です. まず最初は小文字が使われているのでBase64でエンコードされていると推測します.
import base64
with open('onionlayerencoding', mode='r') as f:
s = f.read()
f.close()
s = base64.b64decode(s)
print(s)
# 344234453537343834313...
今度は数字ばかりなのでBase16っぽいですね. 1行追加してみます.
import base64
with open('onionlayerencoding', mode='r') as f:
s = f.read()
f.close()
s = base64.b64decode(s)
# Add
s = base64.b16decode(s)
print(s)
# 4B4E5748415443534E4E5...
今度は大文字ばかりなのでBase32っぽいです.
import base64
with open('onionlayerencoding', mode='r') as f:
s = f.read()
f.close()
s = base64.b64decode(s)
s = base64.b16decode(s)
# Add
s = base64.b32decode(s)
print(s)
…なんと # Error: Non-base32 digit found
が返ってきました.
さて, Base32には2種類あり, 使用する文字が 0-9A-V
のBase32hexというものも存在します2.
これはbase64モジュールには含まれていないので, 別途インストールする必要があります(実際は必要ありません. やりたい人はどうぞ).
$ pip install base32hex --user
さて, 再開しましょう.
import base64
import base32hex
with open('onionlayerencoding', mode='r') as f:
s = f.read()
f.close()
s = base64.b64decode(s)
s = base64.b16decode(s)
# Add
s = base32hex.b32decode(s)
print(s)
…終わらん. ここでかなり詰まりました.
Base32hex自体はアルゴリズムが簡単(Decodeについては8文字ごとに区切って5文字ずつに圧縮している)なので, この s
にだけ適用できるように自作のBase32hexのデコード関数まで作って高速化を図りました(なんとか終わりましたが結局徒労に終わります).
さて, そもそも最初の8文字 4B4E5748
をBase32hexでデコードしてみると
print(base32hex.b32decode('4B4E5748'))
# '"\xc8\xe2\x9c\x88'
そもそもAsciiで英数字とすらなりません. 結論として, この 4B4E5748
から始まる文字列はBase32でもBase32hexでもないエンコーディング方式, ということになります.
(ここでの私の失敗は, Base64$\rightarrow$Base16ときたので次はBase32に違いない, と決め込んだことです.)
import base64
with open('onionlayerencoding', mode='r') as f:
s = f.read()
f.close()
s = base64.b64decode(s)
s = base64.b16decode(s)
# Add
s = base64.b16decode(s)
# s = base64.b64decode(s) でもエラーは吐きませんが, 返り値が英数字ではなくなります.
print(s)
# KNWHATCS...
以上を踏まえて, 以下のような流れのコーディングをしていくことになります.
- ファイルを読み込む.
- Base16でデコードしてみる(Errorがでるかもしれないので例外処理をしておく).
- デコードできない場合はBase32でデコードする(同じく例外処理する).
- デコードできない場合はBase64でデコードする(同じく例外処理する).
- 2~4を150回繰り返す.
となります. 上でも述べたような, Base16と64など複数のモードでデコードできてしまう場合はBase16から優先するようにしました(もちろん, これでうまくいく保証はなく, 詰まったらまたその都度考えるつもりでした).
import base64
with open('onionlayerencoding.txt', mode='r') as f:
s = f.read()
f.close()
for i in range(150):
try:
s = base64.b16decode(s)
except:
try:
s = base64.b32decode(s)
except:
try:
s = base64.b64decode(s)
except:
print('Decode Error...')
print(i)
32回目から Decode Error
を吐き始めました. 見てみましょう.
import base64
with open('onionlayerencoding.txt', mode='r') as f:
s = f.read()
f.close()
# 150ではなく32にしただけ
for i in range(32):
try:
s = base64.b16decode(s)
except:
try:
s = base64.b32decode(s)
except:
try:
s = base64.b64decode(s)
except:
print('Decode Error...')
# print(i)
print(s)
b'RITSEC{0n1On_L4y3R}'
結局, Base32hexは使われておりませんでしたが, 知識が増えたのでよしとしましょう.
AlPhAbEtIcAl Challenge(Misc, 280pt)
Hint: A potential avenue of attack might be to take a look at how often things occur!
59:87:57:51:85:80{:40:50:56:08:82:58:81:08:18:85:57:87:48:85:88:40:50:56:59:15:56:11:18:85:59:51:}
ヒントの"how often things occur"から頻度分析, つまり単一換字式暗号を疑います. これについては(経験則から)思いつくしかないでしょう…
flagの形式は RITSEC{…}
なので,
・59 → R
・87 → I
・57 → T
・51 → S
・85 → E
・80 → C
ですね. 一旦置き換えてみます.
R:I:T:S:E:C{:40:50:56:08:82:58:81:08:18:E:T:I:48:E:88:40:50:56:R:15:56:11:18:E:R:S:}
…うーん、全然置き換わってないので何もわかりません.
一旦上の対応表は忘れて適当にアルファベットを当てはめます. コロンも抜いておきます.
ここでは, 2桁の数字が出てきた順に59をAに, 87をBに, 57をCに, …とABCを順番に割り振っただけです.
ABCDEF{GHIJKLMJNECBOEPGHIAQIRNEAD}
これを頻度分析してくれるこのサイトに投げます.
(注釈)一度何かしらのアルファベットに変換しないとうまく解析できません.
そして, 先頭のABCDEFはそれぞれRITSECに対応することはわかっているので
Cluesのところに「ABCDEF=RITSEC」を入力してsolveを押すと, それらしいのを返してくれます. 今回はこれが答えでした.
RITSEC{YOUALPHABETIZEDYOURNUMBERS}
pre-legend(Crypto, 100pt)
9EEADi^^8:E9F3]4@>^4=2J32==^D@>6E9:?8\FD67F=\C:ED64
Wrap the flag in RITSEC{ }
いやこれノーヒントで出されても何もわからないですが….
困ったら以下のサイト
Cyber Chef
に投げます. そして, 左のOperationsからMagicを選び, 真ん中のRecipeにドラッグ&ドロップします. しかし, 何も有益な情報が得られません…
困ったらググりましょう. 先頭の何文字か, 今回なら 9EEADi^^8:E9
で検索をかけます.
こういう時になぜかヒットするんですよね…. ザーッと見ていくと, どうやら文字化け?している感じがします.
さらに検索結果を見ていくと, ありました. 「Free HTML tools - Online ROT, ROT5, ROT13, ROT18, ROT47 ...」なるタイトルの記事. Cryptoの問題なのでROT確定でしょう.
さて, 再び登場です.
CTF Crypto - A painter and a black cat
を見てみると, ROTの代表として13と47があります. 13ではないことは明らかでしょう. 上のリンクからさらにWikipediaのページに飛んでも似たような文字が登場するので, ここで勝利を確信します.
では, またもCyber Chefに投げます. 左のOperationのすぐ真下, Searchの箇所に"ROT"を打ち込むとROT47が出てくるので, 先ほどと同じようにRecipeにドラッグ&ドロップします. するとURLが表示されます.
え, 2段構えだったか. ここに載っているソースコードをまた読み解くのか…と落胆しました. というか15分くらいマジで解析してました.
ですが, 試しにこのURLそのものを
RITSEC{https://github.com/clayball/something-useful-ritsec}
として入力してみると通りました. 焦った, 多分どこかで使うんでしょうね.
まだ3問くらいWriteup書くので, 続きは後ほど.