SECCON 2019 Online CTF に参加して

SECCON Online CTF 2019に参加しました。

reversingの問題が解けたらと思って参加したのですが、解けませんでした。 解けたのは「coffee_break」と「Beeeeeeeeeer」の二つだけでした。

一応、自分のwrite upを公開します。

coffee_break

encrypt.pyというPythonのソースと暗号文が置かれていました。

encrypt.pyの中身を見てみるとencryptという関数とAESで暗号化していることがわかりました。

AESで暗号化している部分はもう秘密鍵がわかっているので復号してみると

'jff~|Ox9'34G9#g52F?489>B%|)173~)%8.'jff~|Q\x05\x05\x05\x05\x05

という文字列になりました。

最後の方の連続した\x05はパディングを暗号化したものだと思われます。

残るは、encrypt関数だけです。

encrypt関数は鍵であるkeyと平文であるtextを与えると暗号化された文字列を出力するという関数です。

encrypt関数は具体的に書くと以下の動作を行なっていました。

  1. i番目の平文のasciiコードから0x20を引く
  2. i番目の鍵(鍵の最後まで行ったら最初から)のasciiコードから0x20を引く
  3. 1と2で求まった値を足し、0x7e - 0x20で割った余りを求め、0x20を足す
  4. 3で求まったasciiコードを文字に直す

要するに、平文と鍵のasciiコードを足し合わせ、asciiコードの33番から126番の範囲に収めています。

3番の操作を少し変えて、1と2を引くようにすれば簡単に復号することができます。

最終的に以下のコードで解きました。

from Crypto.Cipher import AES
import base64

def decrypt(key, text):
    s = ''
    for i in range(len(text)):
         c = (((ord(text[i]) - 0x20) - (ord(key[i % len(key)]) - 0x20)) % (0x7e - 0x20 + 1)) + 0x20
         s += c

key1 = "SECCON"
key2 = "seccon2019"
cipher_text = base64.b64decode("FyRyZNBO2MG6ncd3hEkC/yeYKUseI/CxYoZiIeV2fe/Jmtwx+WbWmU1gtMX9m905")

secret_key = key2 + chr(0x00) * (16 - len(key2) % 16) 
crypto = AES.new(secret_key, AES.MODE_ECB)
cipher = crypto.decrypt(cipher_text) # <= 'jff~|Ox9'34G9#g52F?489>B%|)173~)%8.'jff~|Q\x05\x05\x05\x05\x05

print("Flag: {}".format(decrypt(key1, cipher))
$ python solve.py
Flag: SECCON{Success_Decryption_Yeah_Yeah_SECCON}?AA56

Beeeeeeeeeer

難読化されたシェルスクリプトが書かれたファイルが渡されました。

実行してみるとLet's decording!(≧∀≦*)という文字が表示され、何か入力すると、今度はランダムにビープ音が流れHow many beeps?という表示され何回なったか聞かれました。それに正解するとEnter the passwordと表示しパスワードを求められたので、解析することにしました。

スクリプトを読んでみると何やら長い文字列をbase64でデコードし、実行している部分があったので、実際にデコードしてみました。

デコードすると、また難読化されたシェルスクリプトが出てきました。Unicodeや16進数、8進数に変換された部分や、base64エンコードされた部分があったのでそれを元に戻すと、どうやらHow many beeps?の部分だったようです。

How many beeps?の部分は以下のような処理を行なっていました

  1. ビープ音を流す
  2. ランダムな時間スリープをかける
  3. 回数を聞き、間違ったらexit
  4. これを1~10回ランダムに繰り返す

上の処理を行なった後、3回ビープ音を流して何回流れたかを聞いていました。そして、そこで入力された値をnという環境変数に入れ、そのnを使ってAES暗号で暗号化されたシェルスクリプトを復号し、実行していました。

どんなスクリプトが実行されているのか気になったので、echo <base64でエンコードされた暗号文> | base64 -d | <復号処理> | bash| bashだけを消すと、実行されずにシェルスクリプトが見えました。

実際に以下のスクリプトが書かれていました。

__=$(. 2>&1);__=${__##*.};${__:$(($...(長いので省略)...((____=____^____||++____))}!&&printf "\n\033[?7l%1024s" " "&&echo SECCON{$S1$n$_____};echo -e '\033[?7h';"

echo SECCON{$S1$n$_____}という気になる記述が見えるのですが$____という変数の値だけわかりません。($S1はスクリプトの最初の方でhogefugaに設定されており、$nは先ほどの解読結果から3回とわかっています)

シェルスクリプトの難読化について調べていると次のサイトが見つかりました。

www.ryotosaito.com

なんとなくわかったのですが、全部解読できる気がしなかったので適当に${...}ごとにechoしてやるとpassword is bashという文字列が現れました。なので、それを入力して終わりだと思ったのですがフラグが表示されませんでした。

何故だろうと思ってみてみるとprintf "\n\033[?7l%1024sで表示を消していると気づいたので、そこを消すとSECCON{hogefuga3bash}と表示されました。

感想

SECCON Online CTFに参加して、改めて自分の力不足を思い知りました。

簡単な2問だけしか解けなかったのは残念だったけれど、それでも問題を解けたのでうれしかったです。

私は、reversing問やpwn問を解けるようになりたいのでそこをもっと勉強していこうと思いました。

あと、今回一人だったので次参加するときは誰かとチームを組んで参加したいです。