justCTF 2020 - PDF is broken, and so is this file

3 min読了の目安(約2900字TECH技術記事

問題概要

This PDF contains the flag, but you’ll probably need to fix it first to figure out how it’s embedded. Fortunately, the file contains everything you need to render it. Follow the clues to find the flag, and hopefully learn something about the PDF format in the process.

https://ams3.digitaloceanspaces.com/justctf/eccb3bff-69aa-4232-8087-a5e8eea0f581/challenge.pdf

PDFファイルが与えられます。見ると真っ白です。中にFLAGが隠されているので、取り出してください、という問題。

解説

PDF上にあるRubyコード

PDFファイルの構造に初手から手を出すのは正攻法なのですが、取り敢えずはエディタで開いてみます。

見ると明らかにRubyっぽいコードが見えます。このプログラム、Rubyで動かせる🤔🤔🤔?ということで動かしてみます。

ruby challenge.pdf

そうすると、HTTPサーバーが立ち上がります。中のソースコード読んでいくと、/flag.zipでそれっぽいzipファイルのダウンロードが出来るので、wget localhost:8000/flag.zipで雑に拾ってみます。開くと、false_flag.mdmutoolという2ファイルが。

...外れですね。こんな簡単な問題な訳無いのですが、「PDFってRuby動かせるんだよね!すごくない!?ところで、これはあくまでネタで、答えは違うよ」という出題者からの煽りを感じます。

一応、mutoolにかけてみるといいよという文言がfalse_flag.mdにあったので、指定通りにコマンドを実行すると、「PDFの構造ちゃんと調べろよ?」的な煽り画像が。いい加減ちゃんと中身を見ましょう。

もしかしたらヒントあるかもですが、読んでません

PDFの大体の構造とおかしな点

PDFのおおまかな構造は以下の4つです。

  • header: 上の画像にもある%PDF-1.xのバージョン情報です。これだけです。
  • body: PDFとして描画するために必要なものが「オブジェクト」として複数個並べられています。各オブジェクトは固有のIDを持っており、これは後のxrefから参照されます。実際、描画の際はこのオブジェクトが木構造を為します。
  • xref: body上にあるオブジェクト数が記載されていたり、body上の各オブジェクトがこのファイルの先頭から何バイト目にあるかが示されています。オブジェクトIDは1から連番になっており、xrefのn行目がオブジェクトID: nについての情報です。

ざっくりとした説明で、厳密には異なる点が多いです

  • trailer: ファイルの一番最後にある項目で、文書情報のあるオブジェクトの位置や、描画に用いる木構造のルートとなるオブジェクトの位置が記載されています。最後は%%EOFで終わっています。

この内容から、PDFファイルを読む際にはtrailerの情報を取り出せばいいことが分かります。が、trailerの最初にあるtrailerという内容がファイル内に2つあります。また、この少し上から内容が重複しており、以下のようにこのファイルが加工されていることが示唆されます。

  1. 普通にPDFファイルを作る
  2. ファイルの末尾から適当な範囲をコピーし、末尾に張り付ける

一応、ファイル末尾にある%%EOFは1つのようでした。

ということで、同じ範囲のうち上側を削除します。ただ、そうするとPDFファイルが読み込めなくなると同時にもう1つおかしな点が見つかります。

bodyには複数のオブジェクトがバラバラに格納されているのですが、そう考えるとobjectの中にobjectが入ることは絶対に無いはずです。ただ、今の状態ではオブジェクトの始まりを示すobjから終わりを示すendobjに整合性が取れなくなってしまっています。で、このタイミングで気になるのがその整合性が取れない中にあるstreamという存在。PDFにおいてバイナリが埋め込まれている箇所なのですが、この直前にある/Filter /FlateDecodeの内容ですが、flatedecodeで検索するとdeflateというzipファイルの圧縮にもよく用いられるような圧縮手法とのことです。

...streamの前後抜いたらそのファイルとして扱えません?ということで、オブジェクト内部に存在するオブジェクト(PDFにおいておかしな存在)の中にあるstreamを抜き出してファイルの詳細について調べます。zipファイルで、綺麗に回答は出来ませんでしたが、中にあるファイルから先述のflag.zipであることが分かりました。ここは削除してよいでしょう。

これでPDFが表示できるようになりました。が、白紙。

PDFに描画されないオブジェクトについて調べる

ということは、以下の2種類がフラグとして考えられます。

  1. PDFファイルにある何かしらのオブジェクトがフラグを持っている。
  2. 複数のオブジェクトが重なり合うことでフラグを生成する。こちらの方が難しい。

2は難しいので、1から調べましょう。気になるのはオブジェクトの最初にはobject_id (よく分からないけど大体0) objという行があります。このobject_idはxrefで参照できるように連番になっているのですが、object_id4919のものがあります。これは意図的かつ恐らくそのままでは描画されないことでしょう。

例のオブジェクトもstreamであることから、抜き出してファイルについて調べてみましょう。4919 0 obj4919 1 objの2種類があるのですが、前者はテキストファイル、後者は画像ファイルで、後者にはフラグが記載されていました。

余談

この問題ではフラグが1枚の画像になっているので、最悪全てのstreamデータに対して「抜き出して、解析する」という作業をやれば見つかりそうです。PDFの強みを生かすのであれば、オブジェクト毎に(恐らく)座標を自由に設定できそうなので、複数オブジェクトで生成するとより難しくなるのかなと思いました。