Trend Micro CTF Asia Pacific & Japan 2015 Quals memo

First published Wed Sep 30 21:48:12 2015 +0900 ; substantive revision Wed Sep 30 22:08:37 2015 +0900

Tags : Security CTF

このエントリーをはてなブックマークに追加

Trend Micro CTF Asia Pacific & Japan 2015 Online Qualifierにチーム「_0x0_」で参加しました。運営の皆さんありがとうございました。

Twitterにも書いたことですが、例えば今回はPoison Ivyの通信を解析する問題がありました。ツールを使えばやるだけ、という点ではいわゆる良問とは必ずしも言えないものの、リアルワールドRATの通信が用意されるCTFというのもあまりなく、運営の強みが意識的に生かされている感じがしてやる気が出ました。

今回はdodododoとの合同チームで、メンバーは @akiym, @nolze, @superbacker, @waidotto, @xrekkusu でした。最終順位は5位でした。5といえば5thライブことラブライブ!μ’s Go→Go! LoveLive! 2015~Dream Sensation!~のBD&DVDが発売になりましたね。6thで連番してくれる方を募集しています。

cryptography 400

問題として奥深く、副産物もできたので紹介します。

忙しい人用のまとめ

問題

document.zip (非公開?)

データの抽出

与えられた document.zip をunzipすると document.xbm が得られる。X BitMap形式のようだ。document.xbm の内容はこんな感じ。

#define ________________width 1024
#define ________________height 540

static unsigned char ________________bits[] =
{
  0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1, 0x00, 0x00, ...
};

unsigned char metadata1[] =
{
  0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, ...
};

unsigned char metadata2[] =
{
  0x30, 0x82, 0x05, 0xEC, 0x30, 0x82, 0x04, 0xD4, 0xA0, 0x03, ...
};

ImageMagickで変換してみる。

世に言う目grepを彷彿とさせる風情がある。構造のあるデータのようなので ________________bits, metadata1, metadata2 のバイト列を、バイナリデータとしてそれぞれ document.bin, metadata1.bin, metadata2.bin として保存する。

fileコマンドを利用するなどして保存したファイルを調べる。判明したファイル形式は以下の通り。

document.bin 暗号化されたMS Officeファイル (OLE2)
metadata1.bin PEM形式のX.509証明書
metadata2.bin DER形式のX.509証明書

OLE2形式のファイルはlibolecfolefileで操作できる。

ファイルの復号

ググっていたらこれも見つかった。

そういえば前にTwitterで見た。

スライドに目grep画像が出てくることや日本的つながりからして、この内容に手がかりがありそうだ。

内容のポイントは最近のMS Officeで使われているAgile Encryptionという暗号化方式の仕組みにある。最近のMS Officeでファイルにパスワード保護を設定すると、雑に言えば以下のように暗号化される。

  1. 入力されたパスワードから中間鍵 (secretKey) を生成する
  2. secretKeyでファイルを暗号化する
  3. 暗号化されたファイルにパスワードのチェック用の値を埋め込む

つまり、原理的にはsecretKeyが分かればパスワードが分からなくてもファイルを復号できることになる。スライドでは、Win32 APIをフックできたりすると乱数生成をガバガバにしてsecretKeyを予測可能にできるよね、という手法が紹介されている。

また、応用というわけではないが、管理者向けの公式機能として、あらかじめ証明書 (Escrow key) を設定しておくことで、そのシステム上で暗号化された任意のMS Officeファイルが復号できるようになっているらしい。暗号化方式の仕様を見ると、この場合具体的には次のステップが追加される。

さっき得られた証明書はこれに関係ありそうだ。とりあえず document.bin に埋め込まれている証明書を取り出して metadata1.bin, metadata2.bin と比較してみると、 metadata2.bin と同じ証明書であることが分かる。もし証明書 metadata2.bin の秘密鍵を得ることができれば、secretKeyを復号してさらに document.bin を復号することができる。

詳細は後述するが、今回はいわゆるGCD攻撃によって秘密鍵を得ることができる。

>>> metadata1_n = 0xd64594936f4fdfb5dabcaef2f54b9040b9a6743e8142084164e32dc35e15a954981f3fc6af88b261213ffb3f52a0fddacea2ae58d8c25f7d65c3004ad061b1bef8b4bca703408d9f86fd2ea66db0e98ad2cb8cdb2d2122608f416072aa1d37ec4f648134472030bfdf2f08c6c5728b63c438155aed8ddb85f3ff9914ada9c239d39830271261e2c1e80c926828b742e4cad929702fe37810fa37b1b71d2a4c44b95bd74981fc6ae49f84823192a174383f1bdfe665a567f8b2989edef23c3a27aced93ac0a32c4a930ebc693a53e78be2ce3247d94c630be0489dbc795cc866ebf7ff9c1904b69d8dafe255043653b575dcbb311d254aab0020425583d073dbb
>>> metadata2_n = 0xafffe033e18b02cd5d58d9295fd0f24a6a3edee7bf8ae4d582501f504bb7f0186b84ae944d12103eba5e553ab70437f70608aad518c5fc21df9072278cf64d94fdbca9e68a2595cc80daedcf727b4f144dab39032d6187ca0065fcec8835cb1149139ab0d0040b586f86d30cf14799882c6d1c811f79e0a6ee07faeb3f3563e7bdba8123e92b495cbeec2efbfe1070ff6bef6ea3cce027f4ea471b397dacb407b92da0e1c63a1f6ad146d66f2fa6d233e0b2d0cf747d2126d911a5c9b8c3f1818a3907cd4f28c1a61027097bd0cc6361e9fd6f66c0a63416aa7485a54c04dcc2a6a8baeef2040ca699b61be2dd63d7221674f81233efb06f866cca57e960a75d
>>> p = gcd(metadata1_n, metadata2_n)
>>> q = metadata2_n / p
>>> print (p, q)
(157746045840640895812964578773586707100705919159625098240496503863073211370186483727847116602878913341676744929198720901288070585370639160366898124554401605620694963419366758153733330098773272970134599633189378720014988750464701361157408042199349121238466211527219183217335113256629803174746270659749965682417L, 140845878615855084480574893585261784293765257941252809857305865006938091440983556929642549028977960113817377125000183721487029429610105300901170077391422785200396807915401031625790480953732685493264277151390292143420529647353248641110992672151937596197239741429670366411222870091056063418605209743233910722349L)
$ python rsatool.py -p 157746045840640895812964578773586707100705919159625098240496503863073211370186483727847116602878913341676744929198720901288070585370639160366898124554401605620694963419366758153733330098773272970134599633189378720014988750464701361157408042199349121238466211527219183217335113256629803174746270659749965682417 -q 140845878615855084480574893585261784293765257941252809857305865006938091440983556929642549028977960113817377125000183721487029429610105300901170077391422785200396807915401031625790480953732685493264277151390292143420529647353248641110992672151937596197239741429670366411222870091056063418605209743233910722349 -o priv.pem

証明書の秘密鍵を priv.pem として、document.bin に埋め込まれている暗号化されたsecretKeyを encryptedKeyValue.bin としてそれぞれ保存し、復号する。

$ openssl rsautl -decrypt -inkey priv.pem -in encryptedKeyValue.bin | xxd -ps -c32   
ae8c36e68b4bb9ea46e5544a5fdb6693875b2fde1507cbc65c8bcf99e25c2562   

これでsecretKeyが手に入った。スライドでは msoffice-crypt.exe という内製ツールを使ってsecretKeyによる復号を実演しているようだが、このツールは公開されていない。とはいえスライドの趣旨として実装可能という話なので、LibreOffice/coreを参考に自分で実装した。

ちなみに、Windows環境が手元にないのでよくチェックしていなかったが、スライドにもある通りWindows環境があればDocRecryptというMS純正のツールに証明書の秘密鍵を与えるだけでも復号できるようだ。

import sys, hashlib, base64, binascii, functools
from struct import *

import olefile
from Crypto.Cipher import AES

SEGMENT_LENGTH = 4096

def hashCalc(i):
    return hashlib.sha512(i).digest()

key = "AE8C36E68B4BB9EA46E5544A5FDB6693875B2FDE1507CBC65C8BCF99E25C2562"
key = binascii.unhexlify(key)
keyDataSalt = base64.b64decode("C/TuhGMeDhrRq6I7/b3SGw==")

ibuf = olefile.OleFileIO('document.bin')
obuf = b''

with ibuf.openstream('EncryptedPackage') as f:
    totalSize = unpack('<I', f.read(4))[0]
    sys.stderr.write("totalSize: {}\n".format(totalSize))
    f.seek(8)
    for i, ibuf in enumerate(iter(functools.partial(f.read, SEGMENT_LENGTH), b'')):
        saltWithBlockKey = keyDataSalt + pack('<I', i)
        iv = hashCalc(saltWithBlockKey)
        iv = iv[:16]
        aes = AES.new(key, AES.MODE_CBC, iv)
        dec = aes.decrypt(ibuf)
        obuf += dec

sys.stdout.write(obuf)

復号されたファイルはPPTX形式だった。開いてTrend Microのロゴ画像をどかすとflagが書いてある。

TMCTF{OFFICE_ESCROW_KEY_SSL_GCD}

余談

ところで、今回はいわゆるGCD攻撃が成立した。CTFに慣れていると、公開鍵を2つ以上与えられた瞬間に互除法を試すという発想が生まれがちだが、一般に成功する確率を考慮すると狂気の沙汰でしかないので、きちんとした理由がほしい。そもそも、GCD攻撃が成立する条件である同じ素数の利用が発生するのは、上の解説ページにもある通り主に乱数生成が腐っている場合である。ここで思い出してほしいのは先のスライドの内容で、これが乱数生成をガバガバにする話だったことを敷衍すると、その状態で生成された証明書なら素数の再利用が起こるかもしれない。この辺の背景設定のある作問なのだとすると面白くて、こういうシナリオも想像できる。

現実的には乱数生成に容易に介入できることは稀ではあるが、よく挙がる手法ではあるし、NSAの問題がその可能性を見せたように、逆に対策や気づくのも難しい。重要なシーンで乱数を利用する場合は必要の程度に応じてその信頼性を確認すべきだろう。

また、暗号化されたMS OfficeのファイルをsecretKeyで復号する処理はCTF後に手を入れてツールとして公開した。 https://github.com/nolze/ms-offcrypto-tool