mina-nokawari’s blog

流行に便乗したかった後悔はしていない

PlaidCTF 2017 Writeup (zipper)

就職活動も一段落し、1人で張り切って参加してきたが得点は51 pt(345 位/1143 チーム中)となんとも言えない感じ
とりあえずzipperという50 pt問題だけ解いたので、Writeupを書きます

問題自体は壊れたzipからFlagを取り出せ! というシンプルなもの

世の中にはDiskInternals ZIP Repairやら『zip -F』コマンドやらがあって
簡単に壊れたzipを修復できるワザがあるらしいが、これらを試しても中のFlagは取り出せず

ツールを使っても解決できなそうなので、バイナリエディタからデータ列を引っこ抜いてやろうという方針に変更
zipファイルをバイナリエディタで見ると、以下のような感じに見える(250byteくらいしかないし初めからバイナリエディタで見ればよかった)

f:id:mina-nokawari:20170424135712p:plain

バイナリがそんなに長いわけではないので、ZIPのファイルフォーマットを見て頭から解析していく
途中までは問題なく解析できるのだが、ローカルファイルヘッダのfile name length(2329)の部分で「ファイル全体のサイズがこれより小さいからこのあたりから壊れてるのかなぁ」と思い始める

とりあえず、わかっている部分だけで解析できたことを整理すると、

f:id:mina-nokawari:20170424140150p:plain

  • ファイル圧縮形式はデフレート形式(0008 画像の緑部分)
  • 圧縮後のデータサイズは0x46バイト(画像の赤い部分)
  • 情報ヘッダ(02014B50)よりも前半部分にファイルの実データが格納されている

(つまり、データサイズを加味すると画像の青い部分の0x46バイト分のエリアが最も実データである可能性が高い?)

となります。なので、あとはコードを書くだけ。
デフレート形式で圧縮されたファイルを元に戻す方法について、JavaScriptで実装されていた方がいたので、コードはそちらの方のものを参考にさせていただきました

バイナリエディタを駆使して壊れたzipからファイルを救出する, Qiita(2015/12/8)
qiita.com


var fs = require('fs');
var zlib = require('zlib');

var start;
var end;

function unzipFile(name, start, end) {
  var rs = fs.createReadStream('Zipper.zip', {start: start, end: end});
  var inflateStream = zlib.createInflateRaw();
  var ws = fs.createWriteStream(name);
  rs.pipe(inflateStream).pipe(ws);
}

start = 0x42; 
end = 0x87;
unzipFile('DecFile', start, end);

コードの説明はほぼ不要だと思いますが、開始バイトを0x42, 終了バイトを0x87としてそのエリアを切り取って、InflateRawを使って圧縮データを元に戻すだけです。
このコードを実行するとtxtファイルが出力されて、無事にFlagが取れましたとさ

f:id:mina-nokawari:20170424142727p:plain

flag: PCTF{f0rens1cs_yay}