PythonでグローバルIPアドレスを取得する

最近グローバルIPアドレスを調べることが多くなったのでまとめておくことにする。

DynDNS

dyn.com

DynDNSは、ダイナミックDNSのサービスです。自分のグローバルIPアドレスを知りたい場合はここにアクセスします。返ってくる結果には余分な文字列が含まれるので少しだけ追加の処理を行う必要があります。

>>> import urllib2 as u
>>> url = 'http://checkip.dyndns.com/'
>>> res = u.urlopen(url)
>>> html = res.read()
>>> html
'<html><head><title>Current IP Check</title></head><body>Current IP Address: xxx.xxx.xxx.xxx</body></html>\r\n'
>>> html.split(':')[1].strip().rstrip('</body></html>\r\n')
'xxx.xxx.xxx.xxx'
>>> 

ieServer.Net

www.ieserver.net

ieServer.NetもDynDNSと同様にダイナミックDNSを提供してくれるサービスで、無料で利用することが出来ます。http://ipcheck.ieserver.net/にアクセスすると自分のグローバルIPアドレスを教えてもらえます。

>>> import requests
>>> url = 'http://ipcheck.ieserver.net/'
>>> res = requests.get(url)
>>> res.text
u'xxx.xxx.xxx.xxx\n'
>>> str(res.text.rstrip('\n'))
'xxx.xxx.xxx.xxx'
>>>

IP address information

http://inet-ip.info/

このWebサイトは接続してきたクライアントのIPアドレス、ホスト名や国名などの情報を表示してくれます。URLの末尾にipを付加するとIPアドレスのみが取得でき、jsonを付加するとIPアドレス以外の情報も含めた様々な情報をjson形式で取得できます。

>>> import requests
>>> res = requests.get('http://inet-ip.info/ip')
>>> res.text
u'xxx.xxx.xxx.xxx'
>>>

ifconfig.io

http://ifconfig.io/

こちらもグローバルIPアドレスやその他のネットワーク情報を出力してくれるWebサイトです。ただし、純粋にIPアドレスの文字列のみを返して欲しい場合はUser-Agentにcurlを設定してあげる必要があります。

>>> import requests
>>> headers = {'User-Agent': 'curl'}
>>> res = requests.get('http://ifconfig.io/', headers=headers)
>>> res.text
u'xxx.xxx.xxx.xxx\n'
>>> str(res.text.rstrip('\n'))
'xxx.xxx.xxx.xxx'
>>>

その他

ipify.org

>>> import requests
>>> res = requests.get('http://api.ipify.org/')
>>> res.text
u'xxx.xxx.xxx.xxx'
>>>

ident.me

>>> import requests
>>> res = requests.get('http://ident.me')
>>> res.text
u'xxx.xxx.xxx.xxx'
>>>

icanhazip.com

>>> import requests
>>> res = requests.get('http://icanhazip.com')
>>> res.text
u'xxx.xxx.xxx.xxx\n'
>>>

trackip.net

>>> import requests
>>> res = requests.get('http://www.trackip.net/ip')
>>> res.text
u'xxx.xxx.xxx.xxx'
>>>

dnsomatic.com

>>> import requests
>>> res = requests.get('http://myip.dnsomatic.com')
>>> res.text
u'xxx.xxx.xxx.xxx'
>>> 

セキュリティ・ミニキャンプ in 近畿 2017 (神戸)に参加してきた

7月1日に兵庫県立大学で開催されたセキュリティ・ミニキャンプ in 近畿 2017(神戸)の専門講座に参加してきました。

ハッカー(ペネトレーションテスター)の考え方とハンズオン」

前半は神戸デジタル・ラボの田中ザックさんによる講義でした。

脆弱性の存在するサービスが動いている仮想マシンに対して実際のペンテスターのように攻撃を行うというかなり実践的な内容でした。様々なツールを駆使したり時には直感に頼りながら試行錯誤してFlagを探しました。wpscanやniktoなどは今まで使ったことがなかったので非常に勉強になりました。また使ったことがあるツールでも自分の知らない使い方やオプションを学べたので楽しかったです。特に個人的にはPythonを使ってインタラクティブシェルを立ち上げたのが印象に残っています。ちなみに田中ザックさんは当日講義をしながらインシデントレスポンスの業務もこなしていたそうです・・・。

ハニーポット構築・分析ハンズオン」

後半はセキュリティキャンプ全国大会2015卒業生の中村綾香さんと神戸デジタル・ラボの松本 悦宜さんによる講義でした。

自分で構築したハニーポットに攻撃を行ってそのログを調査するという内容でした。ハニーポットを構築する際に、既存のハニーポットソフトウェアを使うだけでなく自分で脆弱なサービスを用意して一から構築する方法も学べたのでとても楽しかったです。WordPressが認証成功時に302を返すのは初めて知りました・・・。

まとめ

前半と後半の講義の内容がリンクしていて、前半で学んだことを後半の講義で応用できるようになっていたので段階的に知識や技術を吸収出来ました。情報セキュリティの第一線で活躍されている人達から現場の話を聞けたりもしたので面白かったです。

頂いたもの

f:id:tuz358:20170711142925j:plain


Pythonで多層パーセプトロンを実装する

多層パーセプトロン(multilayer perceptron)は、任意の関数を近似できるアルゴリズムです。ニューラルネットワークディープラーニングアルゴリズムを理解する前段階として、今回はこの多層パーセプトロンPythonで実装してみます。

多層パーセプトロンとは

多層パーセプトロンの前にまず単純なパーセプトロンについて理解しておく必要があります。 パーセプトロンは脳の神経細胞(ニューロン)をモデルとして作られています。

f:id:tuz358:20170628205802j:plain

上に神経細胞の模式図を示しました。神経細胞は情報の伝達や処理を行うための細胞で、以下のような性質を持ちます。

  • 多入力・多出力 : 複数の神経細胞から入力信号を受け、複数の神経細胞へ出力信号を送る。
  • 情報の重み : 神経細胞間の結合強度によって情報が伝達される度合い(重み)が変わる。
  • 発火 : 他の神経細胞から受け取った信号の総和がある閾値を超えると発火状態になり、信号を他の神経細胞に伝達する。

これらの性質を考慮して作られたモデルがパーセプトロンです。

f:id:tuz358:20170629162401j:plain

上図のように、入力信号を x、出力信号を y神経細胞同士の結合の度合いを wで表します。また、発火する際の閾値 \thetaとします。入力信号 xを受け取ると、パーセプトロンはその信号に重み wを掛け、その値が閾値 \thetaを超えた場合に 1を出力し、そうでなければ 0を出力します。式で表すと、

 {\displaystyle  y = \left\{ \begin{array}{1} 1  \hspace{1em} ( wx > \theta ) \\ 0 \hspace{1em} ( wx \leqq \theta ) \end{array} \right.  }

入力信号が 2つ以上の場合は、複数の入力信号の総和を計算し、閾値 \thetaと比較します。

 {\displaystyle  y = \left\{ \begin{array}{1}  1 \hspace{1em} \left( \displaystyle \sum_{i=1}^{n} w_i x_i > \theta \right) \\ 0 \hspace{1em} \left( \displaystyle \sum_{i=1}^{n} w_i x_i \leqq \theta \right) \end{array} \right.  }

パーセプトロンには、後述しますが直線(線形関数)しか近似できないという問題点があります。そこで直線だけでなく任意の関数を近似できるよう考案されたのが多層パーセプトロンです。多層パーセプトロンとは、入力層と出力層を含めて層の深さが 3以上のネットワークを指します。

f:id:tuz358:20170629170654j:plain

左から入力層、隠れ層、出力層となります。隠れ層を増やすことで表現力が増し、より複雑な関数を近似できます。

パーセプトロンの実装

試しにパーセプトロンにAND演算の機能を持たせてみましょう。AND演算の真理値表を以下に示します。

入力 A 入力 B 出力 C
0 0 0
0 1 0
1 0 0
1 1 1

この表から、AND演算をパーセプトロンで実現するには 2つの信号を受け取って 1つの信号を出力するようなパーセプトロンを用意すればいいことが分かります(下図)。

f:id:tuz358:20170629171633j:plain

ただし、 (1,1)という信号を受け取ったとき 1、それ以外の信号の組み合わせを受け取ったとき 0を出力するような重 w_A w_B閾値 \thetaパーセプトロンに設定してあげる必要があります。これらのパラメータは当てずっぽうで決めてもいいのですが、AND演算のグラフを使うとそれらのパラメータの設定を直感的に行えます。

f:id:tuz358:20170629173549j:plain

横軸は入力 A、縦軸は入力 Bです。また、出力 C 1の点は赤色、 0の点は青で表しています。このとき、青と赤の点を分離する直線を引けられればAND演算を実現できます。例えば、次の直線

 B = -A + 1.2

はそのような条件を満たしています。

f:id:tuz358:20170629182603j:plain

この直線の式を変形すると、

 A + B - 1.2 = 0

入力A、Bの係数は 1なので、この直線は入力A、Bにそれぞれ 1の重みを掛けて足し合わせた値が閾値 1.2を超えたかどうかを識別する関数を表していることが分かります。よってパーセプトロンがこの関数を近似するために設定すべきパラメータは、

 w_A = 1 \\ w_B = 1 \\ \theta = 1.2

となります。ではAND演算をPythonで実装してみます。

>>> def AND(A, B):
...     W_a = 1
...     W_b = 1
...     theta = 1.2
...     out = W_a * A + W_b * B
...     y = 1 if out > theta else 0
...     return y
... 
>>>

入力信号を与えてやると、

>>> AND(0, 0)
0
>>> AND(0, 1)
0
>>> AND(1, 0)
0
>>> AND(1, 1)
1
>>>

パーセプトロンでAND演算を実装できました。

多層パーセプトロンの実装

同じようにXOR演算をパーセプトロンで実装してみましょう。真理値表は以下の通りです。

入力 A 入力 B 出力 C
0 0 0
0 1 1
1 0 1
1 1 0

AND演算と同様にグラフを書くと、

f:id:tuz358:20170629180749j:plain

赤と青の点を一つの直線で分類できるでしょうか?XOR演算は、どのようなパラメータを与えても直線では分類出来ません(線形分離不可能)。パーセプトロンで近似できるのは線形の関数のみなので、非線形の関数も近似できる新たな仕組みが必要です。そこで先程説明した多層パーセプトロンを使います。

f:id:tuz358:20170629182149j:plain

多層パーセプトロンを使えば上図のような曲線を近似させることが可能です。ただ、直線であれば重みや閾値を決めるのは簡単でも非線形のものではグラフを見てもパラメータを設定するのは困難です。そこでどうするかですが、結論を言ってしまうと既にパラメータが分かっているパーセプトロン(関数)を組み合わせるという方法があります。XORの場合は、

 XOR\left(A,B\right) = AND\left(OR\left(A,B\right),\ NAND\left(A,B\right)\right)

と表せることを利用します。OR演算やNAND演算は線形分離可能なのでAND演算と同じ方法でパラメータを決められます。これをパーセプトロンで表現すると、

f:id:tuz358:20170629191358j:plain

OR演算やNAND演算のパラメータの求め方はAND演算のときと同じなので省略しました。OR演算とNAND演算もそれぞれPythonで実装してみます。

>>> def OR(A, B):
...     W_a = 0.5
...     W_b = 0.5
...     theta = 0.4
...     out = W_a * A + W_b * B
...     y = 1 if out > theta else 0
...     return y
... 
>>> OR(0, 0), OR(0, 1), OR(1, 0), OR(1, 1)
(0, 1, 1, 1)
>>>
>>> def NAND(A, B):
...     W_a = -1.0
...     W_b = -1.0
...     theta = -1.2
...     out = W_a * A + W_b * B
...     y = 1 if out > theta else 0
...     return y
... 
>>> NAND(0, 0), NAND(0, 1), NAND(1, 0), NAND(1, 1)
(1, 1, 1, 0)
>>>

パラメータを変えるだけで様々な機能を持たせられることが分かります。OR演算とNAND演算を呼び出した後にAND演算を呼び出せばいいので、XOR演算のコードは次のようになります。

>>> def XOR(A, B):
...     h1 = OR(A, B)
...     h2 = NAND(A, B)
...     y = AND(h1, h2)
...     return y
... 
>>> XOR(0, 0), XOR(0, 1), XOR(1, 0), XOR(1, 1)
(0, 1, 1, 0)
>>>

多層パーセプトロンを使って表現力を上げることでXOR演算を実装できました。

セキュリティキャンプ2017に応募した

セキュリティキャンプ2017に参加できることになりました!参加決定のメールを受け取った時はとても嬉しかったです。思えば応募課題が発表されてから締め切りまでの1ヶ月はほとんど応募用紙のことしか考えていなかったような気がします。

例年その応募課題の用紙を晒す文化があるらしいので自分もそれにならって応募用紙を公開しようと思います。来年以降キャンプに応募する人の参考になれば幸いです。

共通問題

共-1(1).

あなたが今まで作ってきたものにはどのようなものがありますか?
いくつでもいいので、ありったけ自慢してください。

私は今までに以下のようなものを作ってきました。
・デスクトップマスコットを用いたIDS
・クアッドコプター
・Simple Ping Tool
自作PC

1つ目のデスクトップマスコットを用いたIDSは、高校の頃の課題研究・卒業研究で作成したホスト型IDSです。デスクトップ上にマスコットを配置して不正アクセス検知時にマスコットをアニメーションさせてユーザーにそれを通知します。このソフトウェアのユニークなところは、不正アクセスの通知にデスクトップマスコットを用いている点です。マスコットを用いるため不正アクセスをリアルタイムで受けていることを実感させることができ、なおかつメールなどによる通知よりもユーザーに危機感を持たせやすいことが売りです。このソフトウェアはメール等による通知は行いません。ですので私はこのソフトウェアを企業がサーバー向けに導入するようなものではなく一般家庭のデスクトップ用途で使うことを想定して開発しました。その理由はこの研究の目的が一般家庭向けのIDSを開発するというものだからです。一般家庭のセキュリティ対策といえばファイアウォールウイルス対策ソフトを導入するのがよく知られた形ですが、それらとは違って未知の攻撃も防御し得るIDSをぜひ普及させたい、認知してもらいたいという思いがありました。 また不正アクセスを検知する際に2種類の検知方式を使っています。パターンマッチングと異常検出です。パターンマッチングではあらかじめシェルコードやマルウェアのコード断片などのデータをシグネチャ(検知ルール)として保持しておき、ホスト上を流れる通信を監視して検知ルールにあるデータが含まれていた場合に不正アクセスがあったと判断します。もう一つの異常検出では、指定したファイル(例えばいくつかの/etc下のシステムファイル)の改ざん検知であったり、同一IPからの短時間での大量のパケットが送られた場合DoS攻撃として検知したりします。この他に各種ログファイルから取得したログを使って不正アクセスがあったかどうかを判定する仕組みも取り入れています。具体的には、例えばSSHポート22番へのブルートフォース攻撃を検知したいのであれば、/var/log/auth.logから「Failed password」という文字列が含まれている行を探し出し、その数と時間感覚を出してあらかじめ設定しておいた条件に照らし合わせます。

2つ目はクアッドコプターです。 モータードライブ回路や電源周りの回路について理解したかったので、フライトコントローラを使わずに制御回路を一から設計して作成したのが自慢できる点です。ジャイロセンサーと加速度センサーから得た値を使って機体の姿勢を推定し、PID制御によって4つのコアレスモーターの出力をPWMで調整することで機体を飛ばします。1号機は、3Dプリンターで作ったフレームを機体として使い、そこに基板をのせる形にしています。2号機はまだ制御プログラムの開発途中ですが基板自体を機体の形状にすることで1号機よりも軽量化しました。また、1号機は直径およそ12~13cm、2号機も 直径約9cmと小さいことも魅力です。出来るだけ機体を小型化出来るように意識しながら制御回路を設計しました。クアッドコプターを作ったことで電子回路の基本的な知識やQFPパッケージのICを半田付けする技術などを手に入れることができました。また、ものづくりの幅を広げることが出来ました。

3つ目は、「Simple Ping Tool」というAndroidアプリケーションです。 このアプリケーションは現在Google Playで公開しています。Simple Ping Toolはユーザーが指定したホストにICMPリクエストを送ることが出来るアプリケーションです。逆にそれ以外の機能は有していないので、シンプルなUIと使いやすさで手軽に通信状況を調べることが出来ます。また、過去にリクエストを送ったホスト名とその結果を保存しておき、後から閲覧することが出来ます。なぜこのアプリケーションを作ろうと思ったかというと、一つはソフトウェア開発の一連のプロセス(企画->設計->コーディング->マーケティング->保守・運用など)を学ぶのにAndroidアプリケーションを作って公開することは最適だと考えたからです。結果として企画から開発に到るまでの幅広い知識、技術を得ることができました。もう一つの理由はサーバーの通信状況を外出先などからスマートフォンで調べられるアプリケーションが欲しいと思ったからです。私は機能をPingに絞った手軽なアプリケーションが欲しかったのですが、Google Playには多機能なネットワークツールはあるのですが機能をPingだけに絞ったものは見つからなかったので自分で作ろうと考えました。

4つ目は、自慢できるほどのものではありませんが、自作PCを作りました。ただ一般的な自作PCと違うのは、ラックマウントケースを使用している点です。データセンターのようにラックにサーバーを格納するスタイルに憧れがあったので作りました。現在はLinux Mint17.3をインストールしてデスクトップ用途で使用しています。

共-1(2).

それをどのように作りましたか?
ソフトウェアの場合には、どんな言語で作ったのか、どんなライブラリを使ったのかなども教えてください。追加したい機能や改善の案があれば、それも教えてください。

1つ目のデスクトップマスコットを使ったIDSはPython2.7で作成しました。マスコットの画像を表示するためのGUIツールキットにはgtkを使い、pygtkというPython用のラッパーを使って、GUI開発をしました。またそのときにウィンドウの枠やメニューバーを消してマスコットの画像だけをデスクトップ上に表示したかったので、cairoというライブラリを使って非矩形の透過ウィンドウを表示させました。マスコットをアニメーションさせるときにはgobjectというライブラリを用いてパラパラ漫画のような形のアニメーションを実装しました。マスコットの画像はInkscapesvg形式のベクター画像を作成し、pngフォーマットに変換して使用しました。パケットキャプチャの処理の部分にはscapyを使いました。具体的には、scapyのsniff関数を使ってホスト上を流れるパケットを1つずつ逐次取得しています。またIDS本体のプログラムとは別ですが、研究データとしてペネトレーションテストの結果を集める必要があったので、「Hacking ~美しき策謀~」を読みながらC言語アセンブリを使ってシェルコードやエクスプロイトコードなどを書きました。
追加したい機能としては、まずシグネチャのパターン数が圧倒的に少ないのでもっと増やす必要があると思います。一人で研究を進めていたのでペネトレーションテスト用のコードを作成するのにあまり時間が割けなかったのが原因ではないかと考えています。自分でやることにこだわらずにexploit-db.comなどからダウンロードしてそれを使えば改善できたのではないかと思います。また出来ればIDSではなくIPSを作りたいというのもあります。侵入検知だけでなく実際に防御してくれる機能も追加したいです。具体的には、不正アクセスを検知したときに該当するTCPセッションにRSTフラグを立てたパケットを送信して強制終了させたりHost UnreachableというメッセージをつけたPingパケットを送信して通信を遮断させたりする機能を追加したいです。またこれは追加したい機能ではないのですがハニーポットを構築してその上で作ったIDSを動作させ、実際の不正アクセスをどれだけ検知出来るのか調べてみたいです。実際の不正アクセスがどのような流れで行われているのかを知ることでその対策を考えるときに大いに参考になると思います。

2つ目のクアッドコプターはAtmega328pというAVRマイコンを使用して作成しました。これはArduino UNOで使われているものであり、自分は普段Arduinoで電子工作をしているので、開発効率や扱いやすさを考えてこのマイコンを採用しました。開発手順としてはまずArduinoとブレッドボードを使ってFETでDCモーターを回転させる回路を作り実際にPWM制御が可能なことを確認しました。次にMPU-6050という6軸の加速度・ジャイロセンサを使い、i2cでArduinoと通信をして値を取得出来るかどうかを試しました。値が取得出来ることを確認したらx,y,z方向の加速度と角速度それぞれからロール、ピッチ、ヨー角を計算し相補フィルタを使って姿勢角を推定するプログラムを作りました。さらに念のためProcessingを使ってPCの画面上に立方体を描画し、Arduinoと接続したMPU-6050の傾きに合わせてその画面上の立方体が動くプログラムを作成してリアルタイムでの姿勢推定が出来ていることを視覚的に確認しました。
次に無線のテストを行いました。1号機ではESP-8266というWi-Fiモジュールを使い、ArduinoでアクセスポイントをたててPCとArduino間でデータを送受信出来ることを確かめました。2号機ではNRF24L01という2.4GHz帯の無線モジュールを使用しました。ArduinoとNRF24L01をそれぞれ2台用意して相互にデータのやりとりが行えることを確認しました。ここまでを行ってようやく回路設計のプロセスに移ります。回路はEagleを使って設計し、出来た回路図から基盤データを設計した後ガーバーデータに変換し、Elecrowという基板制作業者に発注してクアッドコプターの基板を作りました。基板が届いたところで部品を半田付けしていき、はじめにArduino as ISPを使って基板上のAtmega328p-auを内部クロック8MHzで動作させるためにブートローダを書き込みました。最後にPID制御やセンサーなどの各種テストを行ったあと制御プログラムを書き込みました。1号機では、機体は家にあるBS01という3Dプリンターを使ってABS樹脂で出来たフレームを作りました。ところが重量が重く機体が持ち上がらなかったので2号機では回路などの設計を見直して軽量化しました。
ただ安定して飛行させることがまだ出来ていないのでPID制御のプログラムの見直しやパラメータのチューニングが必要になってくると思います。またコントローラも現在PCのキーボード入力で行っているのでDualShock3を使うなどより直感的に操作しやすいものを使いたいです。追加したい機能としてはまずGPS受信機を追加して自己位置推定を行えるようにできたらいいと考えています。さらにカメラを追加してFPVを出来るようにしたり画像処理を行って障害物を避けながら飛行出来るように改良していこうと思います。

3つ目のSimple Ping ToolはJavaを使用してAndroid Studioで作りました。Pingの機能を実装する際にOSのpingコマンドを使いました。具体的には、JavaのRuntimeライブラリを通じてAndroidのOSの/system/bin/pingコマンドを呼び出しています。また過去のPing実行履歴を保存するのにSQLiteデータベースを使用しています。またコーディングより時間がかかったのがUI設計やロゴなどの各種画像のデザインです。UI設計はまず紙とペンで画面を描いてレイアウトを考えてからAndroid Studio上で設計するようにしました。自分の考えを整理してから設計作業に入ることが出来たので先に紙とペンでデザインを考えたのは正解だったと思っています。InkscapeGIMPを使ってアプリケーションのロゴ画像やGoogle Playに表示されるヘッダー画像などを作成しました。こういったデザインに関する作業はあまり慣れていなかったのですが、創造力や発想力が鍛えられたのでとてもいい経験になりました。
追加したい機能としては、まずホスト名やIPアドレスを入力する際の自動補完機能が欲しいです。具体的には、Bashシェルのように過去のPing実行履歴からユーザーが現在入力中のものと一致するデータを探し、入力している部分に出力するというものです。この機能が有るのと無いのでは使いやすさが格段に変わってきます。また、Pingを実行する際のオプションをより詳細に設定できるようにしたいです。現在はICMPパケットを投げる回数を設定画面で指定できるようにしていますが、ユーザーの中にはMTUサイズを指定してジャンボフレームを投げたり、タイムアウト時間を指定したいという人もいると思うのでより多くのオプションに対応できるようにしたいです。

4つ目の自作PCは、ケースにistarusa製の2Uのラックマウントケースを使い、マザーボードASUSの、DDR3のメモリ2GB、CPUにIntel Celeron G1820、定格230VのATX電源、HITACHI製1TBのHDDなどを使って組み立てました。今後はさらに新しくPCを組み立てて数を増やしたりCPUをより高性能なものに変えたりして性能を上げたいです。理想は自分のマシンでディープラーニングの演算を行ったりペタバイトクラスのストレージサーバを構築することなので、欲を言えばGPUを搭載したりもしてみたいです。

共-2(1).

あなたが経験した中で印象に残っている技術的な壁はなんでしょうか?
(例えば、C言語プログラムを複数ファイルに分割する方法など)

デスクトップマスコットを使ったIDSを開発している過程で、攻撃手法について学ぶ必要があり、そこでROPを使ってDEPを回避する方法が理解できず苦しんだ経験があります。技術的な壁というよりは自身の理解力不足が問題だったのですが、とても時間をかけて勉強したのでかなり印象に残っています。

共-2(2).

また、その壁を乗り越えるためにとった解決法を具体的に教えてください。
(例えば、知人に勧められた「○○」という書籍を読んだなど)

まず情報セキュリティに関する基本的な知識を得るために情報セキュリティスペシャリスト試験の勉強をしました。 そして、基本的なスタックバッファオーバーフローについて学びました。ROPはスタックバッファオーバーフローの発展形なので、ROPの前にまずこれを理解して自分で実装することが必要だと考えました。「Hacking ~美しき策謀~」や「セキュリティコンテストチャレンジブック」を読んだりそれに掲載されているサンプルコードを動かしたりすることで実際にシェルが起動することを検証しました。またこのときスタックバッファオーバーフローの仕組みを理解するためにスタックの状態の変化を紙に書くことで、その攻撃がどのように行われているのか理解しました。スタックメモリの仕組みや関数を呼び出すときの動作について学ぶことができました。
次に、スタックバッファオーバーフローではアセンブリで書かれたシェルコードを用意する必要があるので、アセンブリを勉強しました。また、gdbを使ってプログラムのデバッグをしたりROPガジェットを使う時にもアセンブリの知識は必要となるので出来るだけはやく習得しておいた方が良いと思い、「64ビット アセンブラ入門」や「デバッガによるx86プログラム解析入門」などを読んで勉強しました。
アセンブリを勉強する前は他の人が書いたシェルコードはただのバイト列にしか見えなかったのが、勉強したあとはその意味を理解できるようになっていたり、新しい命令を覚えたり、システムコールを使って文字列を出力する初歩的なプログラムを書けるようになりました。
そしてインターネットを使ってROPについて調べました。「ももいろテクノロジー」の中のROPに関連するエントリを読みながら、ここでも一つの処理ごとにスタックの状態を紙に書いてROPがどのように行われているのかを学びました。また、picoCTF2014のバッファオーバーフローやROPに関する問題を解いたり、海外の英語のサイトでROPのチュートリアルを読んだりしてさらにROPについて深い理解ができるよう努めました。

共-2(3).

その壁を今経験しているであろう初心者にアドバイスをするとしたら、あなたはどんなアドバイスをしますか?

私はまず情報工学情報科学の基礎知識を体系的に身につけることを勧めます。特にコンピュータアーキテクチャについて、スタックやヒープの仕組みなどについてです。というのも、私自身プログラミングやコンピュータアーキテクチャの勉強は独学で行ってきたのですが、Pythonなどのような高級言語に触れる機会の方が多く、そういった知識を断片的にしか持ち合わせていなかったので、バッファオーバーフローについて理解するときに非常に苦労したからです。今言ったような知識を事前に得られていれば、よりスムーズにそういった技術をものにできると思います。また、そのような知識をどのように身につけるかに関してですが、C言語アセンブリを学ぶと良いのではないかと思います。いきなりアセンブリは敷居が高いかもしれませんが、Cであれば初心者でも習得が容易な上ポインタやmallocなどメモリ管理を意識するので自然とコンピュータアーキテクチャに関する知識が身につくのではないかと思います。Cが書けるようになったらアセンブリへステップアップするのが良いと思います。アセンブリはCと比べてよりハードウェアに近い言語なので高級言語では見えてこなかった部分の知識を得られると思います。
次に私がアドバイスするのは、紙とペンを積極的に使うということです。なぜなら、それらを使って自分の頭の中の考えを整理することで目の前のタスクや事象をより理解しやすくなるからです。私はこれに何度も助けられました。AndroidアプリのUI設計を行うときに紙とペンを使ったことがきっかけでその重要性を知り、エンジニアリングというのは画面を睨んでキーボードを打つだけではないということを身をもって感じました。以降、私は何か設計をしたり行き詰まったときは紙とペンを使い、頭の中を整理するようにしています。
また、英語を勉強することを強く勧めます。少なくとも英語で書かれた海外のサイトから自分に必要な情報を読み取れるぐらいの力はあった方がいいです。日本語に翻訳されるのを待たずに英語で情報収集が出来ると情報収集の幅を広げることができ、最新の情報を世界から得られます。私自身この夏(8月末~9月末)に英語を勉強しにオーストラリアに1ヶ月研修に行く予定です。
次に私がアドバイスするのは、専門書やインターネットで得た知識は、ぜひ自分の環境で実践してほしいということです。学んだことを実践せずに知識として蓄えておくだけでは本当の意味でそれは身に付いたとは言えない気がします。プログラミングであれば、サンプルコードを眺めるだけでなく自分でキーボードを打って入力することでより深い理解が出来ます。また、専門書やインターネットにのっている手順通りに作業をしても環境の違いによるエラーが出ることがあるので、そういった問題を一つずつ解決していくことで自分自身をより成長させることができます。自分の手を動かすことで「知識」が、「知恵」となり、他へ応用できるようになるのだと思います。

共-3(1).

あなたが今年のセキュリティ・キャンプで受講したいと思っている講義は何ですか?(複数可)
そこで、どのようなことを学びたいですか?なぜそれを学びたいのですか?

私は以下の講義を受講しようと考えています。

Day2
・A1 Powershellベースのマルウェアとその防御手法
・D2~3 カーネルエクスプロイトによるシステム権限奪取
Day3
・E4 サイバー犯罪捜査の現場
・A5 Availability Challenge ~サービスの可用性を確保せよ~
Day4
・C6 スマートフォン向けのゲームセキュリティ
・A7 ファジング実習

・A1 Powershellベースのマルウェアとその防御手法
私はマルウェア解析やリバースエンジニアリングに非常に興味があります。その理由は、攻撃者がどのようなコードを使っているかを知ることで有効な防御策を考えられると思うからです。しかしマルウェア解析をするにあたっては、マルウェアの検体の扱い方やコードのどの部分に注目すればよいのかなど考慮すべきことがたくさんあります。一学生がそのようなことを学べる機会はこのキャンプしかないと思ったのでこの講義を受講したいと思いました。実際にマルウェアを解析することで実践的なスキルを身に付けたいと思っています。またファイルレスマルウェアというのを今まで聞いたことが無かったのでそれについても深く学びたいと考えています。また講師の凌 翔太さんに直接話を聞きたいというのもこの講義を受講したいと思った理由の一つです。マルウェア解析の最前線で活躍されているエンジニアだと思うので、このセキュリティキャンプという貴重な機会を利用してShinoBOTの色々な話を聞いたりマルウェア解析のテクニックなどを学びたいと思っています。

・D2~3 カーネルエクスプロイトによるシステム権限奪取
私がこの講義を受講したいと思った理由は主に2つあります。
一つは、この講義でカーネルソースコードを読めるようになって自分で脆弱性を発見できる技術を身に付けたいからです。私は今までに何度かLinuxカーネルソースコードを読んでみたことがありますが、その動作をまだ完全に理解できたことがありません。しかし、カーネルのコードを読み解けるようになれば世界中の優秀なプログラマのテクニックを学んだり新しい発想を得たりできるため、自身のプログラミング力の向上や論理的思考力、発想力を磨くことに繋がると考えています。なのでLinuxカーネルへの興味自体は大いにあります。また、私は他人が書いたエクスプロイトコードを読んで理解することはなんとか出来ますがまだ自分で新しい脆弱性を発見できるレベルには至っていません。セキュリティエンジニアになるためには、悪意あるクラッカーより先に脆弱性を見つけて未然に不正アクセスによる被害を防げるような能力が絶対に必要になってくると思います。そこで今回この講義を受講することで、カーネルソースコードを理解したり脆弱性を発見する技術を身に付けたいと考えています。また、Linuxカーネルの内部構造を知れたり応用力をつけられると思うととても楽しみです。そして得たスキルを今後自分で何かサービスやソフトウェアを作るときに役立てたいと考えています。具体的には、セキュリティを考慮したプログラム設計、コーディングをしたり、自分で書いたプログラムを後から見直して脆弱性を発見できるようになりたいと考えています。
2つ目は、iOSAndroidなどのモバイル端末のroot化に興味があるからです。私は以前モバイルアプリケーションがどんな通信をしているのか気になったことがきっかけでパケットをキャプチャするiOSアプリを開発したいと思ったことがあります。ただインターネットや書籍でそれに関して調べたところ、iOSの場合は端末のroot化をしないと単体でそれを行うのは不可能だということが分かりました。しかし当時は(今もですが)カーネルエクスプロイトや端末のroot化に関する知識や技術を持ち合わせておらず、また調べるにも非常に内容が難しく理解できなかったため、結局PCを中継させてパケットを取得しました。また以前CentOSを使ってWebサーバー構築をしていた時にDirty COW(CVE-2016-5195)という脆弱性の存在を知り、カーネルのバージョンを確認したところ影響を受けるシステムに該当していたので非常に慌てた経験があります。幸いこの脆弱性はローカルからのみ有効なものなので不正アクセスを受けた形跡はなかったものの、そこでカーネルエクスプロイトの怖さを実感しました。以上のことから私はカーネルエクスプロイトがどのようにして行われているのか非常に興味があり、また同時にその技術を理解して習得したいとも考えています。そういう意味で本講義は最適であると考えました。さらに複数の脆弱性を組み合わせた一連の攻撃の流れを学べるので防御に活かせると思い、そういった部分にも惹かれました。

・E4 サイバー犯罪捜査の現場
私は以前からサイバー犯罪捜査の現場に非常に興味があります。というのも、将来セキュリティエンジニアとしてサイバー犯罪対策課で働くことを視野に入れているからです。現役のサイバー犯罪捜査官から実際の捜査の現場の話を聞けたり、さらにサイバー犯罪捜査における捜索差し押さえの手続きを体験できるなどというのは滅多に経験出来ないことだと思います。この貴重な機会を逃すわけにはいかないと思ったためこの講義を受講したいと考えました。また、実習に関してもデジタルフォレンジックによって犯罪の手がかりを探すという実際の犯罪捜査に近い実践的な内容なので、現場で活きるスキルを得られるのではないかと期待しています。デジタルフォレンジックは今までCTFなどでもやったことがないのでこの講義を受けることでデジタルフォレンジックの基本的な解析技術を手に入れたいです。またデジタルフォレンジックの方法、使用するツールや機器、行う際の注意点などについて知ることでデジタルフォレンジックというものをより深く理解するための糸口にしたいとも考えています。

・A5 Availability Challenge ~サービスの可用性を確保せよ~
まず講義の内容が非常に実践的であるところに魅力を感じました。具体的にいうと、サービスの可用性を確保するという視点から検知、回復、防御など様々な対策を行っていくという点です。自分がどこかのサービスを提供している企業に属している立場を想定して行うような形になっており、実際の現場を意識した構成になっていると思います。そのためここで学んだことは確実に将来セキュリティエンジニアとして社会に出たときに役に立つと感じました。また講義の形式がグループに分かれて行う競技形式だということにも興味を持ちました。私は普段Webサイトで常時開催しているCTFの問題を解いたりしているのですが、グループでCTF関連のイベントに参加したりしたことは無いのでそういった経験ができるのはとても楽しみです。不正アクセスからサーバーなどを防御しながらも、いかにサービスを稼働させ可用性を確保していくかという問題は非常に難しいですが、だからこそこれに取り組むことで得られるものも大きいと思います。私はこの講義でそのような現場で活きる実践的な技術を身に付けたいと思っています。

・C6 スマートフォン向けのゲームセキュリティ
共通問題共-1(1)で述べたように、私はAndroidアプリケーションを作ったことがあります。ですので通信の解析やリバースエンジニアリングなどの技術が不正行為にどのように使われているのか開発者として非常に興味があります。特にゲームアプリを完全にセキュアに状態にするのが困難なケースとは具体的にどういう条件で起こるのか、どうしてそうなるのかということについて疑問を持ったので詳しく知りたいと思いました。また、アプリの仕様に基づきながらセキュリティを確保していくという難しい問題もあるので、そのときに必要となる考え方や知識なども学びたいと思っています。この講義で不正行為からモバイルアプリを守る技術や対策を習得し、今後自分がスマートフォン向けのアプリケーションを作るときに生かしたいと思っています。

・A7 ファジング実習
D1 の「Linuxカーネルを理解して学ぶ脆弱性入門」の部分でも述べたように、私は自分で新しい脆弱性を発見できる力を身に付けたいと思っています。そのためにファジングは不可欠なスキルであると思ったので、この講義を受講しようと考えました。特に、ファジングで使うデータがどのようなものなのかや、検査対象の機器やソフトウェアごとに異なる応答をどのように処理するのかなどに非常に興味があります。私はネットワークファジングに限らない様々なアプローチというのが具体的にどういうことなのか理解できていませんが、そういった自分の知らない新しいことに挑戦できるのもとても楽しみです。この講義で学んだことを土台として今後自分で開発したソフトウェアの脆弱性検査をしたりサーバーのセキュリティ診断をしたりなど様々なことに生かそうと考えています。

共-3(2).

あなたがセキュリティ・キャンプでやりたいことは何ですか? 身につけたいものは何ですか?(複数可)
自由に答えてください。

まず、私はこのセキュリティキャンプで自信を持ってこれだけは誰にも負けないと言えるような技術を一つ身に付けたいです。現在の私は、情報セキュリティに関することだけでなく電子工作や人工知能など幅広い分野に興味があって色んなことに取り組んでいますが、自分の武器と言えるような深く精通している技術はまだありません。もしかすると学生の時点ではそれは必要ではなくむしろ視野を狭めずに色んなことに興味を持った方がいいのかもしれませんが、将来社会でエンジニアとして活躍する時に自分のアピールポイントがあるのとないのとでは全く違ってきます。情報セキュリティの世界では、サイバー犯罪の状況が日々変化していたり新たなマルウェアが毎日出てきたりしていて、これらの最新の状況に追いついて対応するためには自分の専門分野を持っておくことがとても大事だと思います。またそれを持っていることによって自分の役割が明確になるので、例えば将来自分がどこかのCSIRTなどに所属していたとして、そのチームへの貢献度も変わってくると思います。セキュリティキャンプでは幅広い分野の様々な講義を受けられるので、自分の専門分野にしたい、身に付けたいと思えるようなものを見つけたいです。
また、私は今回のセキュリティキャンプを自分のまだ知らない技術や未知の分野に挑戦するきっかけにしたいと思っています。なぜなら、将来セキュリティエンジニアになるにはソフトウェア、ハードウェア関係なく幅広い分野の知識が要求され、当然その中には自分が今まで扱ったことがないものもあるからです。例えば私は今までハードウェアセキュリティ(TPM、サイドチャネル攻撃など)についてあまり触れる機会がありませんでした。ですがハードウェアセキュリティはIoTのような技術が今後さらに発展していくに伴って学ぶ必要、価値が出てくると思います。私はこのセキュリティキャンプで、自分の知らないことを知っている人や講師、チューターの方達に積極的に話しかけて知識を得たいです。自分より技術力の高い人たちや情報セキュリティの第一線で活躍されている講師たちと直接お会いすることができるので、そのような人達から未知の分野についてできるだけ多くのことを吸収したいです。私はそのような未知の分野について今回のセキュリティキャンプで少しでも多く触れることができたらいいなと思っています。
また、私はこのセキュリティキャンプで共に切磋琢磨し合える仲間を作りたいと思っています。情報セキュリティに関する話が出来たりチームを組んでCTFを一緒に解いてくれるような人が身近にいないため、セキュリティキャンプでそのようなことが出来る仲間を見つけたいと思っています。仲間を作ることで新たな知見が得られたり自分よりも高い技術力を持つ人から刺激を受けることでモチベーションの向上に繋がったりするのでキャンプでは積極的に色んな人に話しかけて交流を深めたいと思っています。キャンプ後もどこかのCTFチームに所属してさらなる技術力の向上を目指し頑張りたいです。
また、かなり具体的になってしまいますが私は今回のセキュリティキャンプで情報セキュリティに関する数ある要素技術の中でも特にマルウェア解析やリバースエンジニアリングについて学びたいと思っています。最近もwannaCryなどのマルウェアがニュースになっていますが、こういった情報セキュリティに関する最新の動向を追うには実際にそのマルウェアの検体を入手してその詳細な動作を解析することが必要だと思います。情報セキュリティの世界は移り変わりのスピードがはやく、つい最近まで有効だった防御手法が今日では既に回避策が発見されて意味のないものになっていることもあります。そのため私はこのキャンプでマルウェア解析の技術は確実に身に付けておき、最新の攻撃手法にも立ち向かえるようなセキュリティエンジニアになりたいです。

選択問題

選-A-1.

添付したファイルに記録された通信を検知しました。この通信が意図するものは何か、攻撃であると判断する場合は何の脆弱性を狙っているか。また、通信フローに欠けている箇所があるがどのような内容が想定されるか、考えられるだけ全て回答してください。なお、通信内容を検証した結果があれば評価に加えます。

この通信はApache Struts2に存在する脆弱性CVE-2017-5638を狙った攻撃であると判断しました。そのような判断をするに至った経緯をこれから説明します。まず添付されたファイルをWiresharkで開いて確認すると、一見クライアント(192.168.74.1)とWebサーバ(192.168.74.130)間の通常のHTTPのやりとりに見えます。まず分かるのは、クライアントから送られたHTTPリクエスト(4番のパケット)中のGETメソッドのパスに/struts2-rest-showcase/という文字列が含まれているということです。このことからサーバー上でApache Struts2が動作していると推測できます。また各パケットのEthernetフレームからMACアドレスを読み取るとサーバーとクライアントはどちらも仮想マシン上で動作しているということが分かります。さらによく見てみると同じHTTPリクエストのContent-Typeヘッダに通常では見られない不審な値が設定されています。調べてみるとこの値はOGNL式であることが分かりました。OGNLとは簡単に言うとJavaオブジェクトを扱える式言語です。OGNLを使うと、JavaコードをコンパイルすることなくJavaオブジェクトへアクセスしたりメソッドを参照したりすることが可能となります。ここで、OGNLが関係するApache Struts2脆弱性を調べたところ、CVE-2017-5638の脆弱性を見つけました。この脆弱性Apache Struts2がHTTPリクエストを処理するときに使うJakarta Multipart parserというプラグインの処理に問題があることが原因です。このプラグインはContent-Typeヘッダの値をパースする際にエラーがあるとその値をOGNLとして判断します。つまり、攻撃者はmultipart/form-dataという文字列に続けて自身の実行したいOGNL式を入れることでJakarta Multipart parserを呼び出しこの脆弱性を利用できるということです。また、この通信で設定されていたOGNL式から一部を抜粋すると、

…
(#cmd=‘whoami’).(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win’))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).   
…

さらにこれを読みやすくするために整形すると、

cmd = ‘whoami’;
iswin = getProperty(‘os.name’).toLowerCase().contains(‘win’);
cmds = iswin ? {‘cmd.exe’, ’\c’, cmd} : {‘/bin/bash’, ’-c’, cmd};
p = new ProcessBuilder(cmds);
p.redirecrErrorStream(true);
process = p.start();

これからJavaで任意のコマンドを実行するためにProcessBuilderを用いていることが分かります。また、getPropertyメソッドを利用してOS種別を判別し、起動させるプログラムを変化させることでプラットフォームに依存せずに攻撃を行えるような細工が施されています。
以上の検討した事柄をまとめ、私は、添付されたファイルに記録された通信はApache Struts2脆弱性CVE-2017-5638を狙った攻撃を意図しているものと考えました。

次に、添付されたファイルの中で通信フローの欠けた箇所を調べます。添付されたファイルを見て私がまず思ったのは、不正に細工したHTTPリクエストを送ったことは確認できるが、任意のコードを実行した結果をサーバーからクライアントへ送っているパケットが見当たらないということです。サーバー上で任意のコードが実行できたとしてもその結果が返ってこないのではこの脆弱性は利用できないので、それに相当する何らかの通信が欠けているのではないかと思います。この予想が正しいかどうか調べるために、実際に本脆弱性を検証することにしました。
まず今回の脆弱性を検証するにあたり、準備としてstruts2の動作するサーバーを用意する必要があります。以下のコマンドを実行して環境を構築しました。なおこの脆弱性の影響を受けるstruts2のバージョンは2.3.5~2.3.31なので今回は2.3.31を使うことにしました。

$ sudo apt-get install tomcat7
$ curl -O http://archive.apache.org/dist/struts/2.3.31/struts-2.3.31-all.zip
$ unzip struts-2.3.31-all.zip
$ cd /var/lib/tomcat7
$ sudo cp ~/struts-2.3.31/apps/struts2-rest-showcase.war ./webapps/

次にエクスプロイトコードを書きました。

添付されたファイルに設定されていたOGNL式を用い、サーバーに細工したHTTPリクエストを送るエクスプロイトです。

用意したエクスプロイトを使って実際に検証してみました。先に別ターミナルで以下のコマンドを打っておき、通信を記録します。

$ sudo tcpdump -i en0 -w cve-2017-5638.pcap host 192.168.11.6

オプションを指定してエクスプロイトコードを実行します。

$ ./exploit.py --url http://192.168.11.6:8080/struts2-rest-showcase/

tomcat7

$ 

サーバ上で任意のコードが実行できることを確認しました。また、記録した通信をpcapファイルに保存しておき、Wiresharkで開いて調べることにしました。添付されたファイルと検証したpcapファイルを比較してみると、まず予想通り後者の通信にはコマンドの実行結果を返すHTTPレスポンス(10番のパケット)が記録されていました。また前者のファイルにはみられなかったパケットとしてARPパケットのやりとりも記録されていました。本脆弱性を検証する際に使用したPCは同一LAN内にあったので、考えてみればARPプロトコルのパケットが記録されていたのは自然だと言えます。ただ添付されたファイルの記録の通信では各ホスト上にARPテーブルのキャッシュがまだ残っていたという可能性もあるので、ARPプロトコルの通信が記録されていないことが不自然だと言い切ることはできません。以上より、私は添付されたファイルには任意のコードを実行した結果を返す通信フローが欠けていると思います。

選-A-5.

以下のプログラムはLinuxカーネル3.8〜4.4に存在する脆弱性を悪用しています。このプログラムの実行により発生する不具合を説明してください。また、この脆弱性をさらに悪用することでroot権限昇格を行うエクスプロイトを記述し、自分が試した動作環境や工夫点等を説明してください。加えて、このような攻撃を緩和する対策手法をなるべく多く挙げ、それらを説明してください。
完全には分からなくても構いませんので、理解できたところまでの情報や試行の過程、感じた事等について自分の言葉で記述してください。また参考にしたサイトや文献があれば、それらの情報源を明記してください。

まずこの問題の脆弱性を検証するための環境を構築します。 Ubuntu14.04.1 LTSをPCにインストールし、そこにこの問題の脆弱性の影響を受けるバージョンであるLinuxカーネル3.18.25を入れてコンパイルし、再起動しました。また上のプログラムをコンパイルするのに必要なlibkeyutilsをインストールしました。

ここで環境構築を終了し、実際にこの脆弱性を検証してみます。 通常は、以下のようなコマンドを実行してみても/proc/keysはroot権限でないと読み込むことが出来ませんでした。

$ cat /proc/keys
$ sudo cat /proc/keys
0e74f99d I--Q---     2 perm 1f3f0000     0 65534 keyring   _uid.0: empty
1e2df5ee I------     1 perm 1f0b0000     0     0 keyring   .system_keyring: 1
254f14cd I--Q---     1 perm 1f3f0000     0 65534 keyring   _uid_ses.0: 1
288e5f4e I------     1 perm 1f030000     0     0 keyring   .dns_resolver: empty
2b61b971 I------     1 perm 1f0f0000     0     0 keyring   .ima: empty
35b666f5 I------     1 perm 1f030000     0     0 asymmetri Magrathea: Glacier signing key: 8157d14a6e925449d355fdd38f8cc1807dcc13e4: X509.RSA 7dcc13e4 []
$

しかし上のプログラムをコンパイルして実行すると以下のように一般ユーザー権限で/proc/keys中のleaked-keyring(上のプログラムで指定しているkeyringの名前)が読み込めてしまうという不具合が発生してしまいました。

$ gcc ./leak.c -o leak -Wall -lkeyutils
$ cat /proc/keys
$ ./leak
$ cat /proc/keys
05321907 I--Q---     100 perm 3f3f0000     1000  1000 keyring   leaked-keyring: empty

なぜこのような不具合が起こってしまうのかというと、Linuxカーネルのsecurity/keys/process_keys.c内のjoin_session_keyring関数に問題があるからだということが分かりました。

注目して欲しいのは20~23行目のelse ifブロックで、新しく作られたkeyringオブジェクトが同じセッション中で既に存在していた場合(同じ名前のkeyringオブジェクトがあった場合)にgotoでerror2へジャンプするのですが、その際にkey_putというkeyringオブジェクトを解放する関数を呼び忘れているため、前のkeyringオブジェクトへの参照が保持されたままになってしまっています。上のコードはこれを悪用していて、まずleaked-keyringという名前のkeyringオブジェクトを作成し、さらにまた同じ名前(leaked-keyring)のkeyringオブジェクトを同じセッションで作成することにより先ほどのelse ifブロックの条件を満たすようにしてkey_put関数をスキップするような処理を行わせていたということが分かりました。それによってkeyringが一般ユーザー権限でも読み取れるようになっていたのです。
次に私はこの脆弱性をさらに悪用してroot権限に昇格するにはどうすればいいか考えたところ、単純に参照、解放などといったキーワードからUse after freeの脆弱性を使ってカーネルエクスプロイトを行うことを思いつきましたが、今回のkeyringオブジェクトは参照を保持し続けるものの解放されていません。そのためこれを悪用するには何らかの方法でkeyringオブジェクトを参照可能なまま解放してそのメモリ領域に別のオブジェクトを割り当てる(Use after free状態にする)必要があるということに気がつきました。そのためkeyringオブジェクトを解放する方法を調べました。すると整数オーバーフローを使うことでkeyringオブジェクトをUse after free状態にする方法が見つかりました。struct key_type中のusageフィールドが、keyringの参照された回数を保持しているのですが、usageフィールドが0になったときkeyringオブジェクトはガベージコレクションにより不必要と判断されて解放されます。すなわち整数型の最大値を超える値をusageフィールドに格納してラップアラウンドを発生させ、usageの値を0にすればkeyringオブジェクトをUse after free状態に出来るということです。usageフィールドはいわゆる参照カウンタなのでkeyringオブジェクトを整数型の最大値(232)よりも多い回数だけ参照することで0に出来るのではないかと思いました。
keyringオブジェクトをUse after free状態にしたら、次はそれと同じメモリ領域にデータ書き込み用の別のオブジェクトを割り当てる作業に移ります。今回私はSystem V IPCに着目しました。System V IPCはプロセス間通信を可能にするインターフェースです。このオブジェクトを作成しkeyringオブジェクト解放直後にメモリに割り当てます。System V IPCはプロセス間通信に使うためのものですが、今回はmsgsnd関数を使用してkeyringオブジェクトのメモリ空間に任意のデータを書き込むために使います。次に、keytypeを参照し、このメモリ空間を使用するメソッドを探します。それを呼び出せばそのメソッドはkeytypeを参照しているつもりがmsgsndによって設定したアドレスにある関数を実行することになり、root権限昇格が達成できます。今回はkeyctlのメソッドの中でもkey_revokeを使うことにしました。key_reovkeメソッドのコードをみると、

key_type構造体のrevokeメンバに参照していることが分かります。つまり、revokeメンバに自分の呼び出したい関数のアドレスを指定したkey_type構造体をmsgsnd関数でメモリに割り当てれば攻撃が成功するということです。ここで、どんな関数を呼び出せばいいかということですが、私は[5]を参考にしました。これによると、prepare_kernel_cred関数の引数に0(NULLポインタ)を指定し、それをcommit_creds関数に渡すことでuid=0のcredentialを作ることができ、root権限への昇格が可能なようです。commit_creds、prepare_kernel_credのアドレスは以下のコマンドを実行することで確認できるとありました。

$ cat /proc/kallsym | grep commit_creds
$ cat /proc/kallsym | grep prepare_kernel_cred

しかし、以下のコマンドを実行してもアドレスを示す部分に0が並んでおり、アドレスが取得できませんでした。調べたところ、kptr_restrictというカーネルパラメータが有効になっているとアドレスが読み取れないことがあるということが分かりました。このパラメータはKernel Address Display Restrictionというものでroot以外でのカーネルシンボルのアドレスを表示しないようにするセキュリティ機構です。これを無効にするために以下のコマンドを実行します。

$ sudo sysctl -w kernel.kptr_restrict=0

このコマンドを実行後に再度commit_creds、prepare_kernel_credのアドレスを調べると今度はうまくアドレスを確認することができました。

以上のことをまとめて[3]を参考にしながらエクスプロイトコードを書きました。

工夫した点は引数にcommit_credsとprepare_kernel_credのアドレスを指定できるようにした点です。環境ごとにアドレスは違うのでコンパイルし直す必要がないようにしました。実行して見たところ、一般ユーザー権限でシェルが起動しました。そこでどうしてそうなってしまうのかroot権限が取れない理由を考えました。まず思いついたのはSELinuxなどのようなセキュリティ機構によってコードの実行がブロックされているということです。ですが調べたところSELinuxでは本脆弱性を回避することはできないようです[11]。さらによく調べたところ、SMAPとSMEPというセキュリティ機構があることが分かりました。SMAP(Supervisor Mode Access Prevention)は、その名の通り一般ユーザーがカーネルのメモリ空間にアクセスすることを禁止するものです。もう一つのSMEP(Supervisor Mode Execution Protection)は、スーパーバイザーモード(カーネルモード)のときにユーザー空間のアドレスからのコードの実行を禁止するものです。Intel 第3世代のCPUから搭載されています。そこで私は自分が検証した環境でこれらが有効になっているかどうか調べました。まずSMAPについては、3.18.25においてはカーネルパラメータでデフォルトで有効になっていました。またSMEPに関しては以下のコマンドを実行することで確認できます。

$ cat /proc/cpuinfo | grep flags

SMEPが有効の場合はflagsにsmepという値が設定されています。確認したところSMEPも有効になっていることが分かりました。そこで私はSMAPを無効にするためにカーネルを再コンパイルし、SMEPを無効にするためにgrubの設定を変更しました。 まずSMAPに関しては、以下のコマンドを打ってカーネルコンパイルし直し、インストールしました。最初にカーネルパラメータの設定をするプログラムが必要とするライブラリをインストールします。

# apt-get install libcurses-ocaml-dev

次にカーネルのパラメータを設定していきます。(/usr/src/linux配下)

# make menuconig

このコマンドを実行して出てきた一覧の中からProcessor type and featuresという名前の項目を選んでSupervisor Mode Access Preventionを無効にします。

カーネルコンパイルしていきます。

# make -j2
# make modules_install
# make install

カーネルコンパイルが終了したら、次はSMEPを無効化する作業を行います。

$ sudo vi /etc/default/grub

と打ってGRUB_CMDLINE_LINUX_DEFAULTの値にnosmepを追加します。あとはgrubをアップデートして再起動します。
再起動後に再度以下のコマンドを打ってSMEPが無効になっていることを確認しました。

$ cat /proc/cpuinfo | grep flags

ここでもう一度エクスプロイトコードを実行し、root権限に昇格できるかどうか調べました。しかし、またも一般ユーザー権限でシェルが起動してしまいました。
f:id:tuz358:20170622181503p:plain

ここで自分のプログラムに何かしらの問題がある可能性を考え、このプログラムを書くときに参考にしたエクスプロイトを使って調べてみることにしました。実行して見たところ、見事にroot権限を取得することができました。
f:id:tuz358:20170622181326p:plain

ということは、自分が書いたプログラムのどこかに問題があるということです。しかし、何が間違っているのかが分からず、自分の書いたプログラムでroot権限を取得することはできませんでした。

次にこのような攻撃を緩和する対策を考えました。この脆弱性は主にUse after freeと整数オーバーフローに起因するものです。Use after freeの脆弱性は一度解放したオブジェクトに再度参照するプログラムがあった場合に存在します。通常はそのようなミスは起こりにくいと思うのですが、コードが肥大化したり複雑になってくるとどうしても確認が十分に行えなかったり、特にブラウザだと解放済みメモリを後から使用しないようにするのは困難です。
このような攻撃を緩和する対策として、まずSMEP、SMAPを有効にすることがいい対策になるのではないかと思いました。これらを有効にしておけば、カーネルモードでユーザー空間にあるプログラムの実行ができなくなるので、攻撃者がこの脆弱性を悪用するのは非常に困難になります。また、特定のkeyringへの参照回数があらかじめ設定しておいた閾値を超えた際に参照しているプロセスをkillすることができればこれも有効な対策となると思います。そのような機能を持つ疑似コードを書いてみると、

THRESHOLD = 2**31
while(1):
    f = open(‘/proc/keys’, ‘r’)
    data = f.read()
    lines = data.splitlines()
    usages = [re.split(‘\s+’, x)[2] for x in lines]
    doubtful_keyrings = [lines[x] for x in range(int(usages)) if int(usages[x]) > THRESHOLD]
    pids = (doubtful_keyringsの情報から何度もkeyringに参照しているプロセスを特定する処理)
    if len(pids) > 0:
        for x in pids:
            os.kill(x, signal.SIGKILL)
    f.close()
    time.sleep(1)

特定のkeyringを何度も参照するプロセスを特定する方法は分かりませんが、このようなプログラムをサービスとして起動させ常時/proc/keysファイルを監視しておくことで今回の攻撃を緩和できるのではないかと考えました。また、これは実装上の対策ですが、できるだけプログラム中にgoto文を使わないというのも大事だと思います。今回の脆弱性も原因となったプログラムではgoto文を使っていました。goto文を使うと処理の流れが分かりにくくなりバグを生みやすくなってしまいます。またusageフィールドの整数オーバーフロー対策をしておくことも対策の一つになると思います。keyringへの参照があった時にその前後のusageフィールドの値の変化をみて整数オーバーフローが起きてないかどうか確認するのがいいのではないかと思います。また、本脆弱性では実装できませんが一般的なUse after freeの脆弱性に対する対策としてC言語ではなくPythonJavaなどプログラマがメモリ管理を意識しなくてもいい言語で開発を行うという選択肢もあると思います。先ほども述べたようにUse after free脆弱性はメモリ管理の何気ないミスや確認不足によって起こってしまうので、メモリ管理の部分をプログラミング言語の方に任せてしまえばUse after freeの脆弱性のリスクは軽減できると思います。

参考:
[1] JVNDB-2016-001537 - JVN iPedia - 脆弱性対策情報データベース http://jvndb.jvn.jp/ja/contents/2016/JVNDB-2016-001537.html
[2] Linux 鍵保存サービス入門 https://www.ibm.com/developerworks/jp/linux/library/l-key-retention.html
[3] Analysis and Exploitation of a Linux Kernel Vulnerability(CVE-2016-0728) http://perception-point.io/2016/01/14/analysis-and-exploitation-of-a-linux-kernel-vulnerability-cve-2016-0728/
[4] use-after-freeによるGOT overwriteをやってみる http://inaz2.hatenablog.com/entry/2014/06/18/215452
[5] 無条件で権限昇格するLinuxカーネルモジュールを書いてみる http://inaz2.hatenablog.com/entry/2015/03/21/175022
[6] Linuxカーネルモジュールでret2usrによる権限昇格をやってみる http://inaz2.hatenablog.com/entry/2015/03/21/175433
[7] IPA ISECセキュア・プログラミング講座: C/C++言語編 第10章 著名な脆弱性対策: 整数オーバーフロー攻撃対策 https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c907.html
[8] Exploits Database by Offensive Security https://www.exploit-db.com/exploits/39277/
[9] Exploits Database by Offensive Security https://www.exploit-db.com/exploits/40003/
[10] GitHub https://github.com/bittorrent3389/cve-2016-0728
[11] Linuxカーネルキーチェーン管理における「Use after free」脆弱性(CVE-2016-0728) - Red Hat Customer Portal https://access.redhat.com/ja/node/2133111
[12] 0-day in Linux Kernels | Solutionary Blog https://www.solutionary.com/resource-center/blog/2016/01/zero-day-in-linux-kernels/

選-A-8.

いわゆる「セキュリティ技術」の中で、あなたがもっとも興味があるテーマひとつについて、好きなだけ語ってください。

これは一つとは言えないかもしれませんが、私はサンドボックスハニーポットなどのおとり、罠のようなセキュリティ技術にもっとも興味があります。サンドボックスは、まず応用範囲が広いことに興味があります。マルウェア解析ではもちろんのこと、iOSAndroidなどのモバイル端末用のOSでも悪質なアプリケーションからシステムを防御したり管理者権限を取られないためにサンドボックスが使われています。またGoogle chromeは不正なJavascriptコードが実行されても影響を最小限にとどめるためにサンドボックスを導入してプロセス毎にコンピュータのリソースへのアクセスを制限しています。このように、サンドボックスは身近なところで私たちを不正アクセスの脅威から守っており、情報セキュリティ対策として欠かせない技術となっています。また、サンドボックスの仕組みやそれがどのように実装されているのかについても興味があります。具体的には、サンドボックスは一種のソフトウェアでSELinuxのようにシステムコールをフックしてアクセス制御を行なっているのか、それとも対象のアプリケーションに対してハイパーバイザのように振る舞うものなのか、はたまた全く違う実装形態なのかというようなことが気になっています。また、近年のマルウェアが搭載しているサンドボックスを検知する技術と、それに対するサンドボックス側のいわゆる隠蔽技術についても非常に興味があります。マルウェアはシステム稼働時間を見てその時間が少ないと潜伏活動をしたり自身を削除したりします。サンドボックスは、マルウェア解析のために、起動させてからすぐ検体を実行することがほとんどだからです。また、MACアドレスを取得してそのベンダーコードから仮想環境であるかどうかを判断します。またマルウェアARPテーブルも調査の対象としていて、ARPテーブルの件数が少なかったりすると仮想マシンだとみなして目立った行動を控えたりします。さらに個人的に印象に残っているのが、一定時間ごとにスクリーンショットを取得してデスクトップに変化が見られなかったり明らかにウィンドウの数が少ないとサンドボックスだと判断して解析されるのを回避するという手法です。システム情報や統計情報などの判断基準よりも実際の環境を想定したもので、攻撃者の高度な工夫が見えます。このような回避技術についてサンドボックス側でも様々な対策が施されていて、システム情報(システム稼働時間、MACアドレスなど)を偽装したり、実際にインターネットに接続してマルウェアが攻撃者のC&Cサーバにアクセスできるようにしてマルウェアに仮想環境ではないと信じ込ませる手法なども考えられています。ここまでマルウェア解析におけるサンドボックスの話になってしまいましたが、iOSAndroidに搭載されているサンドボックスに関しても同様のことが言えると思います。iOSバージョン1はDEP無効、ASLR無効、全コードがroot権限で動くなど、現在では考えられないような状態でした。しかしオリジナルの設定を行なってカスタマイズしたりキャリアの限定を解除したいという一部のユーザーによってカーネルエクスプロイトによるjailbreakが行われたりするようになると、iOSも8、9、10とバージョンが上がるにつれてサンドボックスやアプリケーション審査などの様々なセキュリティ対策が適用されるようになりました。私は、世の製品のセキュリティを向上させるには、こうした攻撃側と防御側が共に発展していくことが必要なのではないかと思います。もちろん、理想的には防御側のみがより発展していくことでセキュリティを大きく向上させることが望ましいと思います。しかし現在は防御側がいくら入念に対策を講じても攻撃側がそれに対する新たな迂回手法を編み出し、さらに防御側が新たな対策を考えてという一連の流れが出来上がっており、攻撃者がいることでセキュリティが高まっているというのが現実です。サンドボックスもそのようなプロセスで今まで発展を遂げてきましたが、これからますますサンドボックスの技術は必要になってくると思うので今後も目を離さず情報を収集していきたいです。
ハニーポットは、実際の不正アクセスを観測できることが一つの大きな特徴だと思います。攻撃者はどのような手順でコンピュータに侵入し、情報を搾取し、痕跡を消すのかといったところから攻撃者の心理を学ぶことができるのでセキュリティ対策をする上で非常に役に立つと思います。また、インターネットでハニーポットを構築してログを収集している記事やブログを読んでいるととても面白いです。例えば次のURL(https://github.com/mrtc0/honeypot-log)のログを見ると、攻撃者はwコマンドやunameコマンドを多く使い、またiptablesなどのファイアウォールを停止させたりする操作も行うということが分かります。中でも特に興味深かったのは、echo -n testというコマンドです。私はこれを初めて見たとき攻撃者がなぜこのような行動をとるのか分かりませんでした。しかしよく調べてみるとこれは攻撃者が攻撃先のサーバーがハニーポットであるかどうかを判断するためのものであるということが分かりました。Kippoのようなハニーポットソフトウェアではechoコマンドを完全にはエミュレートできておらずオプションが文字列と認識されてしまうのです。攻撃者はこれを利用してオプションをつけたechoコマンドを実行し、自身が監視されているのかどうかを判断していました。私はこのような攻撃者の心理を学べるという点でハニーポットに非常に興味があります。いつか自分で構築してみたいとも思っています。また、ハニーポットの歴史についても興味があります。ハニーポットの発祥は、アメリカのとある研究所が軍事施設のデータベースを狙う攻撃者の正体を調べるために構築したSDIネットという偽のデータベースだそうです。そこからハニーポットの技術は発展を遂げ、現在ではOSやアプリケーションに脆弱性を残しておいて攻撃者を誘導するという手法が主流となっています。また、ハニーポットにはいくつかの種類があります。一つは低対話型ハニーポットというものです。これはあるOSやアプリケーションを模倣したものを使うことで安全性を維持しながら不正アクセスを監視するタイプのハニーポットです。ただ本物のOSやアプリケーションではないので攻撃者にハニーポットであると気づかれてしまう可能性は高くなります。もう一つは高対話型ハニーポットです。低対話型では模倣したOSやアプリケーションを使うのに対して高対話型では本物のものを使います。本物を使うことで低対話型と比べて取得できるログの種類が多いので詳細な解析ができます。また攻撃者にハニーポットであると気づかれにくくなるというメリットもあります。ただ、低対話型よりもハニーポットが破られた時のリスクは当然高いので注意して運用する必要があります。他にも複数のサーバーでハニーポットを運用しログを解析する分散型ハニーポットというものや仮想マシン上でハニーポットを動作させることで安全性を向上させた仮想ハニーポットなどがあります。


選-A-1はもう少し考察できたかも・・・。

Hello World

ブログを始めました。 コンピュータ技術についての個人的なメモを書いていきたいと思っています。