DUAL-IPA

Break My Heart

ZIP Attacks with Reduced Known Plaintext

5月最後の土曜日。とても良い陽気です。
花のきれいな季節ですね。君もコクリコわれもコクリコ
テネシー出身のマイリ―・サイラスが歌うFlowersは依然としてチャート上位をキープしていますね。
日本人に歌いやすい英語のように思います。

さて、今日は、ksnctf Q.39 Unknown Plaintext Attackについて考えます。

所与

(1). split_flag.php
(2). flag.zip

(1)をエディタで開くと、PHPのソースのように見えますが、コードの後ろに改行が続き、最後に文字化けした判読不明な文字列があります。

PHPのコードは以下のような流れです。

1.ファイルflag.txtを読み込み変数$flagに格納する。

2.6回ループを繰り返す。

 1-1.ファイルサイズ分のランダム文字列を生成する。

 1-2.ランダム文字列をファイルflag%d.txtに出力する。(%dはループ回数)

 1-3.変数$flagにランダム文字列をXORする。

3.ループを抜けた後、変数$flagをflag7.txtに出力する。


つまり、flag.txtを読み込み、ファイルサイズが同じflag1.txt~flag7.txtの計7本のファイルを出力しています。

文字化け部分を見ると、PKから始まり、途中で2回PKが出現します。a.txtという文字も2回出てきます。
zipファイルの先頭はPKで始まります。そこで、split_flag.phpバイナリエディタで開き、PKより前の部分を削除して、拡張子をzipにしたファイルに保存します。
zipファイルを解凍すると、a.txtというファイルが出てきました。
a.txtの中身は次のとおりです。

Hint:
Michael Stay, "ZIP Attacks with Reduced Known Plaintext." Fast Software Encryption, LNCS 2355, 125--134. Springer-Verlag, 2002.

ヒントが書かれているようです。googleで翻訳すると、

Michael Stay、「既知の平文を削減した ZIP 攻撃」。高速ソフトウェア暗号化、LNCS 2355、125 ~ 134。シュプリンガー・フェルラーク、2002 年。

となりました。Michael Stayという人の論文を参照しろということのようです。
googleで検索すると、論文のPDFはすぐ見つかりました。10ページしかないですが、全て英語です。ちょっと置いておきます。


次に(2)を見てみます。Explzhで開くとflag1.txt~flag7.txtの7つのファイルがあります。
つまり、(1)のphpで作成したファイルのようです。しかし、パスワードがかかっており解凍することができません。
この手順(phpで7つのファイルを作成し、パスワード付ZIPにする)は、設問に記載されている操作ログと一致しています。


既知の平文を削減した ZIP 攻撃

設問のタイトルUnknown Plaintext Attackと、ZIP Attacks with Reduced Known Plaintext.は、似ているようで意味が異なります。前者は「全く知らない」と言っているのに対し、後者は「ちょっと知っている」という感じです。いずれにせよ、パスワードクラックの手法を表しているように思われます。
Unknown Plaintext Attackでウェブ検索すると、known(既知平文攻撃)に関するものばかりが見つかりますが、unknownに関するものは見つけられません。
ZIP Attacks with Reduced Known Plaintext.の方は、参考文献として記載している英語のサイトは見つかりますが、攻撃の内容を記載したものが見つかりません。

ksnctf ZIP Attacks with Reduced Known Plaintext.で検索すると、Twitterの記事が見つかりました。

買ってくれ~🙏 読みどころとしては最後のMichael Stay, ZIP Attacks with Reduced Known Plaintextの解説。Linuxのzipコマンドで暗号化したZIPならば、ZIPに5個以上のファイルが含まれていれば、既知平文不要で解読できるという話。 sanya.sweetduet.info/understandzip/

ZIPに5個以上のファイルが含まれていればunknown(既知平文不要)でいける!
今回は7個ファイルがあるので余裕ということでしょうか!?
しかし、対価が必要!? URLをクリックすると、sample.pdfがあり、Stayの攻撃法が記載されていますが、素数ページのみ抜粋しての掲載であるため、全容がわかりません。ただし、stay.cppというサンプルコードが付いており、こちらは歯抜けがないようです。このサンプルコードがエクスプロイトコードのようです。


stay.cppを見ると、rabbit.zipというファイルを読み込んで、5箇所のポイントから12バイトを取り出しています。rabbit.zipはサンプルコードに掲載されています。このzipファイルは5個のファイルをパスワード付ZIPにしたもののようです。ファイル数はポイント数の5と符合しています。
rabbit.zipをバイナリエディタで開いてみます。ASCIIで5つのファイル名が含まれていることがわかります。ファイル名の後ろにはいずれも、09 00 03 FF CC 2A 5A 0A CD 2A 5A 75 78 0B 00 01 04 00 00 00 00 04 00 00 00 00 という26バイトが続いています。その次のバイト位置が、サンプルソースに書かれた5箇所のポイントと一致します。

そこで、flag.zipに対してバイナリエディタで同じようにポイントを拾い出してみます。
26バイトの中身はrabbit.zipとは一致しませんが、同じように7箇所のアドレスを拾って、stay.cppのpos[5]を書き換えます。さらに、要素数が5の配列を要素数7に全て書き換えます。エクスプロイトコードができました。
Linuxコンパイルして動かしてみましょう。
・・・何も出てきません。

sample.pdfによると、実行時間はCore i7-4790 3.6GHz で1 時間弱だそうです。全探索の37%くらいのところで解が見つかったとあります。ということは最悪2~3時間ぐらいかかるということでしょうか。

フラグ取得
・・・5時間経ちました。18040.92秒かかりました。(たぶん、全探索の62%くらい)ちなみに、実行環境はAMD Ryzen 7 Extreme Edition 1.80 GHzで、VM上のLinuxです。
さて、実行結果にはパスワードが表示されるわけではありませんでした。
ただし、key0、key1、key2それぞれ1ワードの16進数が得られます。
zipdecryptを使えば、key0、key1、key2を引数とすることで、パスワード付ZIPファイルを解凍できます。

flag1.txt~flag7.txtを解凍できました。
flag1.txt~flag6.txtまでがPHPで生成したランダムな文字列で、flag7.txtがそれらをXORしたものです。
排他的論理和の性質から、再度XORすれば、元の文字列に復元が可能です。

php > $flag1 = file_get_contents("flag1.txt");
php > $flag2 = file_get_contents("flag2.txt");
php > $flag3 = file_get_contents("flag3.txt");
php > $flag4 = file_get_contents("flag4.txt");
php > $flag5 = file_get_contents("flag5.txt");
php > $flag6 = file_get_contents("flag6.txt");
php > $flag7 = file_get_contents("flag7.txt");
php > $flag = $flag1 ^ $flag2 ^ $flag3 ^ $flag4 ^ $flag5 ^ $flag6 ^ $flag7;
php > file_put_contents("flag.txt" , $flag);

flag.txtに復元できました!