Armoris日記 hkcert-ctf / CTF-Challenges 2020やってみた

このブログは、数年前にN高等学校を卒業し株式会社 Armoris にやってきたアルバイト Kaepi が書いています。

あるもりすぶろぐの内容は個人の意見です。

概要

  • CTFとは?

Capture The Flagの頭文字を取ったもので、旗取りゲームのことです。
サイバーセキュリティの分野では、Flagと呼ばれる文字列をサーバーに侵入して見つけ出したり、渡されたデータを解析して特定する競技のことを指します。

Write Up

目次

  1. 6FA
  2. Look into a photo & JPG as key

6FA

htmlに埋め込まれたjavascriptソースコードからPINを推定する問題
6桁のPINが6個あり、全部一致するとFlagが表示される

github.com

Key

var key = 2 * 72467 * 2;
var keys = (key + String(1 << 23)).split(8);

keyは普通に計算して289868 になる
keysはkeyに1を23ビット左にシフトした数を足して8でsplitした配列になっている
['2', '9', '6','', '3', '', '60', '']

1. PIN1

check1 = p => p == keys.join(keys[3]);    

keysの要素をjoin()でつなげただけなので、console.log(keys.join(keys[3]));とするとPIN1がわかる。
答えは296360となる

2. PIN2

check2 = p => p * 7 + keys[7] == keys[1].repeat(keys[2]);

keysを数字に置き換えてわかりやすくする

check2 = p => p * 7 + '' == 9.repeat(6);

こうするとp = 999999/7にして計算することができる
なので答えは142857

3. PIN3

check3 = p => p ** 3 - p ** 2 + key * p == 3.01781152450557e+16;

keyや指数表記を整数になおしてわかりやすくする

check3 = p => p ** 3 - p ** 2 + 289868 * p == 30178115245055700;

更に数式になおして解く
p^3-p^2+289868p=30178115245055700

  1. 両辺から30178115245055700を引く
    p^3-p^2+289868p-30178115245055700=0
  2. 左辺を因数分解して2個の方程式に分ける
    p-311337=0, p^2+311336p+96930706100=0
  3. 数学的にはまだ解が出ていませんが、p-311337=0からp=311337ということがわかる

なので答えは311337

4. PIN4

check4 = p => Number(p).toString(6 * 6) == "ctf" + keys[4] + keys[5];

keysを数字になおす

check4 = p => Number(p).toString(36) == "ctf" + '3' + ``;

toString()に引数を渡すと他の基数に変換してくれるので、36進数でctf3になる10進数が答えになる
なので答えは597999

5. PIN5

p.split("").reverse().join("") == String(keys[0] ** (keys[0] + keys[0] ** keys[0])).substr(-6);

keysを数字になおす

p.split("").reverse().join("") == String(2 ** (2 + 2 ** 2)).substr(-6);

右辺を計算すると777216になる
左辺ではpを一文字づつ分割してから配列にしてreverse().join()をしているので、右辺を右から読んだ数字になる
答えは612777

6. PIN6

check6 = function(p){
    var x = [];
    for(var i = 2; i <= keys.slice(6).join(6); i++)
        if(!x[i]){
            for(var j = 2 * i; j <= [6, 6].join(6) - keys[6]; j += i)
                x[j] = 1;
            p /= (i % 100 == 10 + 1) ? i : 1;
        }
    return p == 1;
}

読みやすいように整形してみる

check6 = function(p){
    var x = [];
    for(var i = 2; i <= 606; i++){
        if(!x[i])){ // 1
            for(var j = 2 * i; j <= 606; j+=i){
                x[j] = 1; // 2
            }
            if (i % 100 == 11) {  // 3
                console.log(i);
                p /= i;
            } else {
                p /= 1;
            }
        }
    }
    return p == 1; // 4
}

pがわからなくても手に入る情報をまとめる

  1. 606までの素数がif文を通る
  2. 配列xのj番目に1を入れる
  3. iを100で割った余り(剰余)が11だったらp/=i、違ったらp/=1をする。(除算代入)
  4. 入力されたpが3.の式で1になったらtrueを返す

この関数は受け取った数字が何回か割って1になるとtrueを返すので、素因数分解を使っていそうなことがわかる
なので3.のif文を通れる数をconsole.log(i);などで確認して全部かけると答えがわかる

答えは721831

おまけ

ちなみに総当りでも答えはわかる

for (let i = 111111; i < 999999; i++) {
    if (check1(i.toString())) console.log("1: "+i)
    if (check2(i.toString())) console.log("2: "+i)
    if (check3(i.toString())) console.log("3: "+i)
    if (check4(i.toString())) console.log("4: "+i)
    if (check5(i.toString())) console.log("5: "+i)
    if (check6(i.toString())) console.log("6: "+i)
}

JPG as key & Look into a photo

1枚の画像に隠されたFlagを見つける問題

環境

大まかな流れ

画像に情報を埋め込む手段はいくつかあるので、それぞれ試してみる

  • exif情報
    本来の用途は撮影したカメラの情報などを保存しておくための機能
    exiftool xxx.pngなどで確認できる
  • stringsコマンド
    バイナリファイルなどから表示可能な文字列を表示する strings xxx.pngなどで確認できる
  • binwalkコマンド
    埋め込まれているファイルを確認したり取り出す事ができる binwalk xxx.pngなどで確認できる

Look into a hoto

Flagはexiftoolとstrings両方で見つけることができた

Image DescriptionのほうはBase64エンコードされていそうなのでデコードする

┌──(kali㉿kali)-[~/Desktop]
└─$ exiftool q2.jpg
Image Description               : aGtjZXJ0MjB7ZG9jN29yX3MzaWRfbjlfbWVkaTZpbmVfbmU1ZH0=
┌──(kali㉿kali)-[~/Desktop]
└─$ echo 'aGtjZXJ0MjB7ZG9jN29yX3MzaWRfbjlfbWVkaTZpbmVfbmU1ZH0=' > exif.txt
┌──(kali㉿kali)-[~/Desktop]
└─$ base64 -d exif.txt                                                    
hkcert20{doc7or_s3id_n9_medi6ine_ne5d} 

stringsではそのままFlagが出た

┌──(kali㉿kali)-[~/Desktop]
└─$ strings q2.jpg
hkcert20{doc7or_s3id_n9_medi6ine_ne5d}

JPG as key

こちらはexifとstringsではFlagは見つけられなかったのでbinwalkで見てみると、画像内にパスワード付きのzipファイルが埋め込まれていることがbinwalkによってわかった。

┌──(kali㉿kali)-[~/Desktop]
└─$ binwalk left_exit.jpg

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
30            0x1E            TIFF image data, big-endian, offset of first image directory: 8
57895         0xE227          Zip archive data, encrypted at least v2.0 to extract, compressed size: 3007, uncompressed size: 3048, name: flag.png
60940         0xEE0C          Zip archive data, encrypted at least v2.0 to extract, compressed size: 51376, uncompressed size: 51405, name: left_exit.jpg
112544        0x1B7A0         End of Zip archive, footer length: 22

このzipファイルにはダウンロードしてきた画像(left_exit.jpg)と同じものが入っているので、 既知平文攻撃というものが使える
既知平文攻撃とは、暗号化されたファイルの中身の一つと同じものを用意できれば、そのファイルを復号できるというもの

今回はpkcrackというツールを使って復号する

  • インストール
$ wget http://www.unix-ag.uni-kl.de/~conrad/krypto/pkcrack/pkcrack-1.2.2.tar.gz
$ tar xzvf pkcrack-1.2.2.tar.gz
$ cd pkcrack-1.2.2/src
$ make
  • 必要なファイルを用意する
$ binwalk left_exit.jpg --dd='.*
$ binwalk ./left_exit.jpg -o 0 -l 51405 --dd='.*''

一行目では暗号化されたzipを、二行目ではzipが埋め込まれていないleft_exit.jpg単体を取り出している。

2行目で生成されたフォルダーに移動して

$ 7z a left_exit.zip left_exit.jpg

暗号化されていないzipファイルを用意する。

  • パスワード付きのzipを解凍する
pkcrack -C 暗号化されたzip -c left_exit.jpg -P left_exit.zip -p left_exit.jpg -d flag.zip

-C 暗号化されたzip
-c 暗号化されたzip内で平文のわかっているファイル
-P -cで指定したファイルをパスワードなしで圧縮したファイル
-p 平文のファイル
-d 出力先

解凍して得られたflag.pngQRコードになっており、スマホなどで読み込むと hkcert20{n0w_y0u_can_crack_z1p} Flagを見つけることができた。

まとめ

今回はソースコードから機密情報などを抜き出すReverse Enginneringと、ファイルに隠された情報を見つけ出すForensicsの問題を解いてみました。 CTFには他にも暗号を解読するCryptographyや、脆弱性を突いてFlagを入手するExploitationなどさまざまな分野の問題があるので得意な分野の問題を解いてみたり、まだ知らない分野の問題に触れてみると面白いかもしれません。