セキュリティキャンプ全国大会2017応募用紙

はじめに

セキュリティキャンプ全国大会2017に参加することが決定したので、勢いで応募用紙でも晒してやろうとブログ作ってみました。

問題を解き始めたのは10日前、応募用紙を書き始めたのは締め切り2日前ほどで、提出したのは締め切り当日の11時頃でした。文字数は約17,000文字ほどです。正直応募した時は落ちたと思い込んでいました(笑)。こんなギリギリでもなんとか受かったことに現在でも驚いています。

セキュリティキャンプに受かったというメールがきた時は、講義中でしたので声は出せませんでしたが、とても嬉しかったです。

では以下に応募用紙を晒します。勢いで作ったブログなので、読みづらい可能性大ですがご了承ください。

共通問題

共-1(1) これまで作ってきたもの

JavaScriptを用いたブロック崩しシューティングゲームといった簡易的なゲーム
Apache+PHP+MySQLを用いた高校内アンケートサイト
Javaを用いたフラクタル図形の描画プログラム
Javaを用いたスケジュール管理アプリ
・Nginx+PHP+MySQLを用いたチャットサイト
Arduino+Unityを用いたガンシューティングゲーム
C言語を用いた簡易ヘルプアシスタント
C言語を用いたRTA*検索でのお助け付き8パズルゲーム
C言語を用いたSSL/TLSマルチTalkサーバ・クライアント
C言語を用いた簡易HTMLサーバ

共-1(2) 共-1(1)の作成手段

JavaScriptを用いたブロック崩しシューティングゲームといった簡易的なゲーム
 ライブラリを用いずに作成した。全てのゲームは基礎部分しか作成していないため、ブロック崩しであればアイテム、シューティングゲームであればスクロール型にするなどといった追加機能の作成を行うようにしたかった。ただし中には、オセロに重み付けを用いたCPUの作成、おじゃま機能ありのパズルゲームなども作成することができた。
Apache+PHP+MySQLを用いた高校内アンケートサイト
 XAMPPを用いてApache, PHP, MySQLを動作させた。PHP関連のライブラリは用いていない。アンケート用のユーザデータとアンケート回答用のデータについては、第三正規化した状態でMySQLにテーブルを作成した。そしてApacheでWebサーバとして動作させ、PHPを書いてアンケートに答えられるようにした。最後にアンケートを自分で作成できるようにフォームを作成した。
Javaを用いたフラクタル図形の描画プログラム
 ライブラリは用いていない。シェルピンスキーのギャスケットやマンデルブロ集合、コッホ曲線といったフラクタル図形をAwtで描画するようにした。改善案としては、描画が荒いため、アンチエイリアスを行なって綺麗な描画を行えるようにしておきたい。
Javaを用いたスケジュール管理アプリ
 ライブラリは用いていない。Awtを用いて、年ごと, 月ごと, 日ごとのスケジュールを編集・参照できるアプリを作成した。スケジュール管理を行うためにはアカウントが必要であり、はじめに新規登録も可能であるログイン画面が表示される。そこでログインを行うことで、スケジュール管理を行うことが可能となる。登録したスケジュールがあると、参照しやすくするために、周りの色を変化させる。下にはTodoリストを登録することも可能になっており、年月日とTodoの内容を書くことで、書いた年月日にTodoの期限があることを示す色がつく。改善案として、アプリ自体のデザインの調整とUIの向上がある。
・Nginx+PHP+MySQLを用いたチャットサイト
 CentOS7を用いてチャットサイトを作成した。Ajaxやサイトのデザインに用いるためJQueryを用いている。まずWebサーバを立ち上げるために、Nginxを起動できるようにした。起動の際にはユーザディレクトリを用いるようにコンフィグファイルを書き換えた。またFastCGIを用いることができるよう、http://127.0.0.1:9000をパスとした。次にチャットを行うためのユーザデータ, チャット内容, チャットを行う部屋, チャットに用いた画像の保存パスをデータベースに格納することができるように、第三正規化した状態でMySQLにテーブルを作成した。そしてHTML, CSS, Javascript, PHPを用いてチャットサイトを作成した。チャットサイトでは、部屋を作成し、作成した部屋にユーザを登録することで、グループチャットを行うようになっている。そのため、グループチャットを行えるように登録画面やユーザ管理画面を作成した。最後にチャットデータを持ってくることができるようにAjaxを用いて、まだ送信していないチャット内容の送信や、ユーザから送信されて来たデータの受信及びデータベースへの登録ができるようにした。改善案として、Ajaxを用いたチャットでは、まだ新しい発言が届くまで数秒かかってしまうので、Node.jsなどを用いたWebSocketを用いたチャットサイトの開発を行うようにしたい。
Arduino+Unityを用いた無線ガンシューティングゲーム
 まずArduinoを用いたガンコントローラの作成について述べる。ライブラリは、XBeeのデータの送受信を行うためにhttps://github.com/andrewrapp/xbee-arduinoを用いた。ガンコントローラには、Arduino, 三軸ジャイロセンサ, 三軸加速度センサ, トリガー用のボタン, コントローラ内に入るような電源, 無線用のXBeeを載せる必要があった。これらの部品を, PlayStation2用のガンコントローラであるガンコン2の内部を取り外して格納した。格納にあたっては、できる限り小型化する必要があったため、コントローラー内に入れる電源は単三乾電池1本、Arduinoマイコンブートローダのみ焼いて使用することとした。この時、電源は単三乾電池1本だけでは足りないため、昇圧回路を用いてArduinoが動作するように電圧を上げた。そしてArduinoに三軸ジャイロセンサ, 三軸加速度センサ, ボタンの値を取得し, それをXBeeを通してシューティングゲームを行うパソコンに接続されているXBeeに送信する仕組みを作成した。なお受信するXBeeについても、もう1台のArduinoを用いて受信し、受信した値を相補フィルタを用いて、周波数領域特性を向上させている。
 次にUnityを用いたゲームの作成について述べる。Unity側では、Arduino側からガンコントローラの向いている角度を取得するために、SerialPortを開いてArduinoからのデータを取得できるようにしている。取得したデータを用いて画面の角度を変更することで、ガンコントローラの向いた方向にゲーム画面を向けることが可能となっている。そして周りに破壊対象のオブジェクトを作成し、動くオブジェクトを破壊することで点数を加算する方式とした。
 追加機能としては、背景の明るさを現実世界の明るさに適応させるため、分光器を用いて現実世界の光度を取得した上で背景を朝、昼、夜に変更するようにした。追加したい機能としては、ガンコントローラの精度向上、ビット誤りの減少、ランキング機能の実装、ステージの追加が挙げられる。改善案としては、XBeeからWi-fiモジュールへの変更によるデータ転送量の増加が挙げられる。
C言語を用いた簡易ヘルプアシスタント
 ライブラリはlibgtk3-devを用いた。プログラムを動作させると、まずヘルプアシスタントが画面上のどこかに出現する。出現したヘルプアシスタントは、画面をゆっくりと進んでいく。ヘルプアシスタントをクリックすると、会話やアプリケーションの起動、簡易的な対戦ゲームの起動、ファイル管理、タイマーの設定などを行うことができる。会話は簡単なものしかできないが、単語を認識して会話を行う。アプリケーションの起動では、よく使っているアプリケーションを登録しておくことで、よく使うアプリケーションのどれかまたは新しいアプリケーションを起動することができる。簡易的な対戦ゲームでは、ヌメロンというゲームでヘルプアシスタントと対戦することが可能となっている。ファイル管理では、バックアップしておきたいファイルや検索したいファイルを打つことで、ファイルの検索やバックアップを行う。タイマーの設定では、時間を設定してタイマーをスタートすることで、タイマーが起動する。追加したい機能としては、SNSへの対応などが挙げられる。改善案としては、会話機能の高精度化、機能の充実などが挙げられる。
C言語を用いたRTA*検索でのお助け付き8パズルゲーム
 ライブラリはlibgtk3-devを用いた。プログラムを動作させると、まずファイルをドラッグ&ドロップする画面が開く。画像ファイル以外は受け付けないようになっており、画像ファイルを入れると、ドラッグ&ドロップした画像を9分割して8パズルゲームがスタートする。8パズルでは、横に1手戻る, 1手進む, 初手配置に戻す, 初手配置をリセットする, お助け機能が存在する。お助け機能をクリックすると、RTA*探索を行ったのち、動かすパズルに色をつける。RTA*探索は5手先まで評価する。追加したい機能としては、LRTA*探索の実装が挙げられる。
C言語を用いたSSL/TLSマルチTalkサーバ・クライアント
 ライブラリはlibssl-dev, libmysqlclient-devを用いた。サーバ側のプログラムを動作させると、SSL/TLS通信が行えるように設定を行なったのち、サーバを起動する。作成時では、TLS1.2を用いている。また複数クライアント受信できるよう、selectを用いている。サーバはクライアントからのコネクション確立要請があると、accept関数を用いてコネクションを確立する。この時、SSL/TLS通信を行えるように
サーバとクライアントはSSL/TLSのHandslakeプロトコルを用いて確立を行う。そしてユーザ名・パスワードを受信することで、マルチTalkを行えるようにする。ユーザ名, パスワード, 会話内容は全てデータベースに保管され、ログイン時間、ログアウト時間はログファイルに出力される。追加したい機能としては、ファイル送受信プロトコルの実装などが挙げられる。
C言語を用いた簡易HTMLサーバ
 ライブラリはlibevent-devを用いた。プログラムを動作させると、epoll/kqueueを用いた複数クライアントへの対応を行うために設定を行う。設定後、コネクション確立要請を受けた時に動作するイベントを登録しておく。もしコネクション確立要請のイベントが発火した時、コネクションを確立した上で新たなイベントを登録する。新たなイベントが発火した時、HTTPヘッダーをパースして送信内容を確定する。送信の際には、スレッドを用いて高速化を図っている。送信を行なったのち、イベントを削除する。以上の手順を踏むことで、複数クライアントに対応したepoll+thread型HTMLサーバが起動する。追加したい機能としては、自作のHTTPヘッダーのパースのHTTP2.0への対応、サーバのSSL/TLS対応などが挙げられる。改善案として、epoll+threadをepoll+prethreadにすることが挙げられる。

共-1(3) 開発記のブログやスライドの資料

なし

共-1(4) TwitterアカウントやGithub、ブログのアカウントやURL

Twitterアカウント : who3411

共-2(1) 印象に残っている技術的な壁

・readとfread、writeとfwriteの違いを探る時に、glibcソースコードから違いを探ろうとした。その時のソースリーディングの方法

共-2(2) 共-2(1)を乗り越えるための解決法

・readとfread、writeとfwriteの違いを探る時に、glibcソースコードから違いを探ろうとした。その時のソースリーディングの方法
 まず普段使わないようなマクロの使い方(#演算子,##演算子を含んだマクロなど)について一つ一つ調べて、最低限ソースリーディングを行えるような状態にした。次にソースリーディングを行なったことがある先輩に、どのように読んでいけば良いのかのアドバイスをもらった。結果的にreadはアセンブリシステムコールを行っていたこと、freadはバッファリング関連の処理を行なったのちに_IO_SYSREADを用いてread関数を呼び出していたことがわかった。

共-2(3) その壁を体験している初心者にアドバイスするとしたらどうする?

・readとfread、writeとfwriteの違いを探る時に、glibcソースコードから違いを探ろうとした。その時のソースリーディングの方法
 glibcなどでは、マクロが多くてソースリーディングが難しいので、まずは自分がわからないマクロの記述を読めるようにしてからソースリーディングした方が良い。マクロを読めるようになったら、grepを用いて目的の関数に近いと思われる関数を探し、ctagsなどを使って探すと良い。

共-3(1) 受講したいと思っている講義

受講できるのであれば全て受講したいが、その中でも受けたい講義は以下の通りである。
Linuxカーネルを理解して学ぶ脆弱性入門
 現在、私はLinuxカーネルの解読を行いたいと思っている。そのためLinuxカーネルを理解する講義であり、かつ興味ある脆弱性に関する講義であるため受講したい。
カーネルエクスプロイトによるシステム権限奪取
 バッファオーバーランを起こし、/sh/binを起動させたことはあるが、システム権限の奪取といったことは行ったことがない。またブラウザの脆弱性を組み合わせた高度な攻撃と書いてあり、とても興味が湧いた。
・Embedded System Reverse Engineering 101
 組み込みシステムのリバースエンジニアリングは、前に聞いてから一度実験してみたいと思っていた。しかし実際に組み込みシステムの解析を行ったことはない。そのため専門家のいる中で、リバースエンジニアリングを一度体験してみたいと思っている。
・暗号運用技術
 私がこれまで作成してきたプログラムでは、SSL/TLS通信や暗号化を用いたことはあるものの、ライブラリに頼ったものである。情報処理安全確保支援士の試験勉強をしていた時に、ある程度暗号運用については学んだが、現状では曖昧な理解にとどまっていると感じている。そのため暗号運用がどのように行われているのか、どのような脆弱性が原因となって盗聴が行われるのかなどを知りたい。
・LSMから見たLinuxカーネルのセキュリティ
 LSMについてはSELinuxを調べている時に聞いたことがあるが、実際に使って見たことはない。実際にサーバを運用するということがないため、本格的なセキュリティを行おうとしたことがないからである。LSMを用いることで、自身のセキュリティモジュールが作成できると聞き、一度触って見たいと思った。また安全で丁寧なプログラムを作成したいと思っていたため、本講義を是非受講したいと思った。
・ファジング実習
情報処理安全確保支援士の試験勉強をしていた時に、ファジングという言葉を聞いたことはあったものの、脆弱性検出に使われるということしかわからなかったため、どのようにして行うのかは知らないままの状態である。今後、セキュリティが更に重要となってくると思われる社会において、ファジングを学ぶことは必要であると感じている。学習だけでハンク、実践も行えるということなので、行ってみたいと思った。

共-3(2) セキュリティキャンプでやりたいこと

セキュリティキャンプでは、セキュリティに関する学習・実践を通した技術の取得はもちろんのこと、Linuxカーネルに関する知識なども学びたいと思っている。特に機器を用いた実践は、滅多に行うことができない重要な機会であると思っている。実践を通すことで、知識を定着させるとともに、知識をどのように活かせば実践にも活かすことができるのかを身に付けたい。

選択問題

選択A-1 添付ファイルの通信から読み取れることなど

 解答中にあるNo.については、添付ファイルにあるパケットの順番を示す。また192.168.74.1をクライアント、192.168.74.130をサーバと呼ぶ。
 まずNo.1~3について述べる。ここでは、特に変わった通信は行われておらず、通常の3ウェイハンドシェイクが行われていると考えられる。もしARPテーブルにサーバのMACアドレスが登録されていないとすれば、No.1~3の前にARPの通信が行われているはずである。しかし今回は、通信フローを検知したのみであり、ARPテーブルがどのようであったかを判断することが不可能であるため、ARP通信の有無は問題ではないものの、通信フローに欠けている可能性がある。またサーバの名前解決が行われていない場合、No.1~3の前にDNSの通信が行われているはずである。こちらについても、サーバへのアクセスにIPアドレスを直接URLとして用いたのか、ホスト名を用いたのかが不明である。そのためDNS通信の有無は問題ではないものの、通信フローにかけている可能性がある。
 次にNo.4~5について述べる。No.4では、struts2-rest-showcase/orders.xhtmlに対してGETオプションを用いて、HTTPリクエストを送信している。またNo.5では、No.4でのHTTPリクエストがサーバに届いたことを通知している。事実、No.4のLengthは1146ではあるが、キャプチャした内容をWiresharkで確認すると、[Next sequence number: 1093]と書かれており、次のクライアントのシーケンス番号及びサーバの確認応答番号が1093となることがわかる。No.5のサーバからクライアントへのTCP送信の際には、確認応答番号が1093になっているため、通信が正常に行われたことを示している。しかしNo.6以降にGETに対するHTTPレスポンスが存在しないため、今回の通信フローにはHTTPレスポンスが欠けている。
 No.4について詳しく見てみると、Full request URIが「http://192.168.74.130」を先頭に記述されている。これは、No.1~3で述べたDNS通信の可能性が存在しないことを示す。そのためNo.1~3のDNS通信は欠けているわけではなく、初めから存在しない通信であることがわかった。次にContent-Typeを見てみると、通常ではありえないような文字列が記述されており、何らかのプログラムであることが推察される。調べてみると、https://www.ipa.go.jp/security/ciadr/vul/20170308-struts.htmlに書かれているCVE-2017-5638において、Apache Struts 2が影響し、リモートから任意コードが実行可能であることがわかった。Content-Typeに書かれているプログラムを見てみると、`cat /etc/passwd`が書かれていること、windowsまたはそれ以外において、cmd.exeまたは/bin/bash -cに加えて`cat /etc/passwd`が実行されていると推察できるプログラムが記述されていることから、サーバの/etc/passwdファイルを取得しようとしていると考えられる。以上から、No.4はCVE-2017-5638を悪用した通信であること、No.6以降に存在するはずの/etc/passwdファイルのキャプチャデータが存在しないことがわかる。
 次にNo6~7, 10, 13~14について述べる。ここでは、クライアントがサーバに対してSSH通信を行なっている。SSH通信では、初めに行われるはずの3ウェイハンドシェイクに関するパケットが存在しないため、3ウェイハンドシェイクのパケットデータが欠けていると考えられる。クライアントの確認応答番号が増え続ける一方、シーケンス番号が一切増えないことから、サーバから送られてくるデータを受け取っているだけであると考えられる。この時、もし受け取っているデータが先ほどNo.4で脆弱性を狙って取得しようとした/etc/passwdであれば、GETに対するHTTPレスポンスは、SSHを通じたポートフォワーディングで行われているのではないかと考えられる。もしそうであるとすれば、SSHに関する通信記録が存在しないことから、ポートフォワーディングで送信されているデータが欠けていると考えられる。
 最後にNo.8~9, 11~12について述べる。No.8~9, 12では"TCP ACKed unseen segment"、No11では"TCP Previous segment not captured"がそれぞれ発生している。"TCP ACKed unseen segment"は、正常にパケットが届いているもののWiresharkではそのパケットを認識することができなかった場合に発生する。また"TCP Previous segment not captured"は、直前のパケットが正常に届いていない場合に発生する。ただし"TCP Previous segment not captured"は、Wiresharkの処理速度によっても発生することがあるので、必ずしも届いていないとは限らない。また2つは同様の現象から発生する可能性も存在する。
 No.8~9では、確認応答番号が1464になっていることから、No1~7までの間に1463オクテットのデータをサーバから取得しているが、Wiresharkではキャプチャされていないと考えられる。またNo.7とNo.8の間のtimeを確認すると、約60ミリ秒遅れていることがわかる。60ミリ秒遅れた上でエラーが発生したということは、サーバがDoS攻撃などといった何らかの攻撃を受けている可能性がある。もし攻撃を受けているのであれば、サーバが攻撃を受けている時のパケットデータが欠けていると考えられる。No.11ではサーバ側からクライアントへデータを送信しているが、正常に届いていないことがわかる。そのため、後から送信されている可能性も存在する。No.12ではクライアント側からサーバへデータを送信しているが、これは確認応答番号が1465になっていることから、No.11で送信されたと考えられるデータは正常に送信されていたのではないかとも考えられる。ただしNo.12のパケットは、Wiresharkではパケットをキャプチャできていない可能性が存在する。以上のことから、No.8~9, 11~12は、コネクションの切断である4ウェイハンドシェイクを、Wiresharkから見るとエラーが発生している状況下で行っていたと考えられる。
 ここまでのことから、今回の通信フローでは、ARPやHTPレスポンス、SSH通信のデータなどが欠けている可能性が存在すること、No.4でCVE-2017-5638のApache Struts 2を狙った脆弱性攻撃を行っていること、Wireshark内ではエラーが発生していたと考えられる状況下でコネクションを切断していることなどが考えられる。つまりこの通信が意図していることは、サーバ側に存在する/etc/passwdのデータを取得することであり、そのデータを元にブルートフォース攻撃や辞書攻撃を用いたサーバへのSSH接続を行っていくのではないかと想定される。

参考文献:
[1]Wireshark User's Guide, https://www.wireshark.org/docs/wsug_html_chunked/, 2014

選択A-4 printfとforkについての説明

調べたもの: printf()関数
問題解答環境
OS: Fedora25
gcc: 6.3.1

まず以下のようにmyprintf1.c, myprintf2.cを用意した。
・myprintf1.c
#include <stdio.h>
int main(void){
printf("hello world\n");
}
・myprintf2.c
#include <stdio.h>
int main(void){
int i = 12345;
char *name = "hello world!\n";
printf("%s : %d\n", test, i);
}
 次にmyprintf1.cをgcc -S -o myprintf1.s myprintf1.cのようにアセンブルすると、myprintf1.s内にcall putsが現れた。これは、printf()関数を用いるよりもputs()関数を用いたほうが最適化が行われるとコンパイラが判断したからだと考えられる。また.rodataが現れた。.rodataの内容を探るためにobjdump -s -j .rodata myprintfコマンドを実行すると、.rodataの内容として"................hello."という内容を見ることができた。
 次にmyprintf2.cをgcc -S -o myprintf2.s myprintf2.cのようにアセンブルすると、myprintf2.s内にcall printfが現れた。今回はprintf()関数を用いてフォーマット通りに出力する必要があるからである。12345という数値に関しては、movl $12345, -4(%rbp)という記述があり、ここで格納していることがわかる。また.rodataが現れた。.rodataの内容を探るためにobjdump -s -j .rodata myprintf2コマンドを実行すると、.rodataの内容として"hello world.%s : %d.."という内容を見ることができた。
 次にmyprintf2.cをgcc -o myprintf2 myprintf2.cのようにコンパイルして、objdump -d myprintf2コマンドを実行して逆アセンブルすると、objdumpを行ったmain内にcallq 4003f0 <printf@plt>が現れた。printf@plt内ではjmpq *0x0200c22(%rip) #601018 <_GLOBAL_OFFSET_TABLE_+0x18>が現れた。0x601018を探すためにreadelf -a myprintf2を行うと、'.rela.plt'内にR_X86_64_JUMP_SLO型のシンボル名printfを見つけた。
 以上のことから、固定文字列は.rodataセクションに格納されること、固定数値はアセンブリ上で即値として処理されること、_GLOBAL_OFFSET_TABLE_によってprintfなどといった関数群がまとめられていると考えられることなどがわかった。

 次に、printf()関数の内部を探るためにglibcをインストールした。インストール方法としては
sudo dnf download --source glibc
sudo rpm -ivh glibc-2.24-4.fc25.src.rpm
cd rpmbuild/SOURCES/
tar zxvf glibc-2.24-33-ge9e69e4.tar.gz
のようにコマンドを入力することでインストールした。
stdio-common/printf.cを見ると、
va_start(arg, format);
done = vfprintf(stdout, format, arg);
va_end(arg);
とある。va_startとva_endについては、可変長配列の開始・終了を示している。vfprintf関数を見ると、JUMPマクロが多く存在する。このJUMPマクロを用いて、goto文でLABELマクロで記述されたラベルに移動することで、フォーマットを処理している。JUMPマクロを用いる際には、REFマクロを用いて、どの値であればエラーが発生するか、異なるステップへ移動するか、どの型の値を出力するかといったstep_jumps配列が存在する。step_jumps配列は記述されている状態でstep0, step1, step2などに分かれていく。決められた書式に従ってだんだん遷移先が変わっていく。最終的に一つの書式を取り出すと、process_argとprocess_stinrg_argによって必要な書式が処理されたのち、書式に従って出力処理が行われる。なおstep_jumps配列は、初めの%を識別した後に行われるが、初めの%を識別する処理は__find_specwc()関数または__find_specmb()関数によって行われる。もし確保している領域が足りない上で書式に従って変更していく場合、alloca()関数などを用いて領域を確保している。
 結果的に、printf()関数を呼び出すと、内部でstdoutをファイルディスクリプタとしたvprintf()関数が呼び出されること、初めの'%'を識別した上でjump_table配列やstep1, step2といったstep_jump配列を用いて書式をパースしていること、パースした結果をprocess_argマクロとprocess_string_argマクロによって書式通りの値になるように内部処理していることなどがわかった。
 ここで、ポインタに関する処理を観察するために、以下のようなmyprintf3.cを作成した。
・myprintf3.c
#include <stdio.h>
int main(void){
printf("%p\n", NULL);
printf("%p\n", "a");
}
myprintf3.cをコンパイルして何度か実行すると、
printf("%p\n", NULL);は毎回(nil)
printf("%p\n", "a");は毎回同じアドレス
が表示される。
 NULLについては、vprintf()関数をトレースしていくと、process_argマクロで
string = (CHAR_T *)L_("(nil)");
goto LABEL (print_string);
と記述されている。この部分のプログラムを実行する条件はポインタがNULLである場合である。そのため、NULLを引数に"%p"のprintf()関数を実行すると、毎回同じ(nil)が出力される。
 "a"については、vprintf()関数をトレースしていくと、process_argマクロで
base = 16;
number.word = (unsigned long int) ptr;
is_negative = 0;
alt = 1;
group = 0;
spec = L_('x');
goto LABEL (number);
と記述されている。この部分のプログラムを実行する条件はポインタがNULLでない場合である。そのため、"a"を引数に"%p"のprintf()関数を実行すると、毎回同じアドレスが出力される。毎回同じアドレスが出力されるのは、スタック領域に書き込まれた同じアドレスの値を用いているためである。またポインタの値を出力する場合はnumberとして処理されることがわかった。

参考文献:
[1]ポインタの話をしていたらいつの間にか配列の初期化とかglibのprintfとかを読んでいた話, http://attonblog.blogspot.jp/2014/07/glibc-printf.html, 2014

選択A-8 セキュリティ技術で最も興味のあるテーマ

最も興味のあるテーマ: PKI(公開鍵基盤)
 PKIは、暗号化やデジタル署名を行うことで、守秘性・認証・完全性・否認防止などといったセキュリティ対策を行うことができる技術である。暗号化方式やハッシュ化方式については、いくつもの種類が生まれており、現在でも暗号化・復号時のブロック数を大きくすることによって技術力を高めている節がある。しかしPKIでは、PKIを行うための方式は大きく変化することなく今日まで続いており、完成度の高い技術であると言える。SSL/TLSでのサーバ証明書として用いることができる点が普及するきっかけになったのではないかと考えられる。
 X.509規格に沿った電子証明書を作成することで、偽造することは可能である。事実、私自身も個人用のX.509規格の電子証明書を作成して、SSL/TLS通信を行ったことがある。PKIでは、そのような偽造を行うことができないよう、上位の認証局であるCAからディジタル署名を得る必要がある。上位の認証局からのディジタル署名をもらい、その上位の認証局がさらに上位の認証局からのディジタル署名を得る階層構造によって、利用者から信頼を得ることが可能である。そのため、自ら作成した電子証明書は自己証明書でしかなく、信頼を得ることは到底不可能である。
 PKIに用いるディジタル証明書には、フォーマットのバージョン情報やシリアル番号、証明する公開鍵の情報、有効期限などが含まれている。またオプションとして認証局や認証対象に付与されるユニークIDなども存在する。これらの情報を全てまとめてハッシュ化することで認証局からのディジタル署名として機能する。ディジタル証明書の呼び方は用途によって変化する。例えばクライアント認証に用いるディジタル証明書であればクライアント証明書、S/MIMEで用いるディジタル証明書であればS/MIME証明書などとなる。ディジタル証明書を発行するように認証局に申請する場合、申請者はCSRを生成する。CSRには、公開鍵に加え、証明書を使用するサーバのURLや組織名、部門名などといった情報を記述する。これらの情報を証明書に付与する。ディジタル証明書の有効期限が切れた場合、ディジタル証明書を再発行する必要がある。また何らかの理由によって有効期限前に証明書が失効された場合についても、ディジタル証明書を再発行する。
 先ほど述べたように、電子証明書を作成する際にはX.509規格に従う必要があるが、現在はX.509v3に改訂が行われた状態のものが使用されている。改定前には証明書に拡張領域を設けていたが、改定後には属性証明書であるAC、ACの失効リストであるACRLが新たに定義された。ACでは所有者の役割や権限といった情報を証明する。ACはディジタル証明書とは異なり、属性認証局と呼ばれるAAから発行される。
 PKIでは、先ほど述べたようなCAやAA以外にも以下のような要素から構成されている。
・RA
 証明書発行や失効といった審査の実施、ディジタル証明書利用者情報の登録、鍵の一括管理、、公開鍵を公開するためにリポジトリへ保管、証明書や鍵の配布などを行う。RAでは、ディジタル証明書保持者の身元を保証している。場合によっては、CAがRAを兼ねている場合も存在する。
・VA
 ディジタル証明書失効情報の集中管理、公開鍵での署名検証、ディジタル証明書有効期限の確認、CRLの確認などを行う。
リポジトリ
 ディジタル証明書利用者の情報やディジタル証明書、CRLなどを格納し、検索などのサービスを提供するシステム。LDAPを用いてることが多い。
・CRL
 ディジタル証明書の悪用や誤発行など、何らかの要因によって発生した不測事態によって有効期限内に失効させるディジタル証明書が登録されたリスト。ディジタル証明書が悪用されたなどの事故があればすぐに登録される。ディジタル証明書の有効性を検証する場合には、有効期限はもちろんであるが、CRLへの登録の有無についても確認する必要がある。CRLへの登録は、有効期限が過ぎた時点で終了してしまうため、確認時点ではすでに削除されている可能性も存在する。CRLの更新頻度はCAによって異なるが、12時間程度で更新されることが多い。CRLには、CRL発行日時や次回発行日時、CRLフォーマットバージョン情報などが書かれており、その後ろに失効証明書のリストが格納されている。CRLには以下のような種類が存在する。
-完全CRL
 1つのCRLに全ての失効情報を記載したもの。大規模ドメインでの運用はデータサイズが巨大なものになってしまうため、利用者のCRL取得負荷が重くなってしまうが、適切な規模での運用を行うことで、利用者の取得時間が効率化され、管理も容易になる。
-区分CRL
 完全CRLに対し、複数のCRLに分割して公開する。シリアル番号や失効理由といった、何らかの要因によってCRLを分割することで、CRLのサイズを小さくしている。それ以外にも、CRLの所在が証明書に記載されることから、利用者がCRLの所在を容易に知ることができる。
-デルタCRL
 ベースとなるCRLを発行し、そのCRLの失効情報の差分を記載したCRL。証明書利用者は、ベースとなるCRLと差分のCRLを取得したのち、各失効情報を足し合わせることで完全な状態のCRLを取得することが可能となる。転送効率を高めることが可能である。完全CRLもしくは区分CRLに用いられる。
-間接CRL
 複数のCAから発行されたCRLをまとめて一つのCRLとして発行するCRL。間接CRLには、発行者の署名がつけられるため、利用者は発行者が信頼にたる者であるかどうかを確認する必要がある。複数のCAを利用している利用者が1つのCRLを見れば良いため、確認が容易になる。
-ARL(認証局失効リスト)
 CRLが利用者のディジタル証明書に対する証明書失効リストであるのに対し、ARLはCAのディジタル証明書に対する証明書失効リストである。証明書の有効期限内に鍵が危殆化した場合や相互認証基準違反を犯した場合に、自己署名及び相互認証に用いられた証明書が両方とも記載される。
-CRT(証明書失効ツリー)
 ハッシュツリーを用いて失効情報を証言するための技術。OSCPのバックエンドとして使用されている。

 またCRLを速く取得するための技術は以下のようなものが存在する。
-OSCP
 ディジタル証明書の有効性を、発行されたCRLの情報からリアルタイムで確認する仕組み。OSCPを実装したサーバであるOSCPレスポンダに利用者が問い合わせることで、CRLの取得・照合を行うことが可能となっている。しかしOSCPレスポンだでは失効情報について調べるだけであるため、信頼関係についてはチェックを行わない。場合によっては、複数のOSCPレスポンダに対して問い合わせを行わなければ、目的のCRLを取得することができない可能性も存在する。
-DPV+DPD
 「パス検証(Path Validation)とパス構築(Path Discovery)をサーバに任せるもので、電子証明書の検証方針などをクライアント側が持つことができるプロトコル」[1]。計算能力や処理をサーバ側に任せることで、クライアント側の処理の低減を図っている。
-SCVP
検証専用のサーバを作成することで、OSCPで行われなかった信頼関係のチェックを行うことが可能となっている。

以上の構成要素によって、高信頼性を保ちつつ、情報伝送の効率化を行っている。
 PKIを行う上で、ディジタル証明書はもちろん必要であるが、電子的な署名を行うディジタル署名についても、ディジタル証明書を構成する重要な要素である。ディジタル署名は、以下のような手順で行われる。([3]参照)
1.送信文書をハッシュ関数の引数とすることで、送信文書のメッセージダイジェストを作成する。
2.作成したメッセージダイジェストを送信者の秘密鍵で暗号化、ディジタル署名を作成する。
3.送信文書, ディジタル署名, ディジタル証明書を送信する。
4.受信者は文書のみを取り出し、送信者と同じハッシュ関数を用いてメッセージダイジェストを作成する。
5.受信者は受信データ中のディジタル署名と送信者のディジタル証明書を取り出し、送信者のディジタル証明書内に存在する公開鍵を用いて復号を行う。
6. 4で作成したメッセージダイジェストと5で復号したメッセージダイジェストを比較することで、完全性、正当性を確認する。
ディジタル署名のフォーマットには、PKCS#7を拡張したCMSが用いられる。CMS以外の方式としてはXMLディジタル署名を行うXMLディジタル署名が存在する。
 XMLディジタル署名では、Enveloped署名, Enveloping署名, Detached署名の3つが存在する。Enveloped署名は署名対象のオブジェクト内部に署名が置かれ、Enveloping署名は署名内部に署名対象のオブジェクトが置かれ、Detached署名は署名対象のオブジェクトと署名が独立しておりURIによってオブジェクトを参照する。

 ここまではディジタル証明書に関する話題を行なってきたが、PKIには他にもいくつかの技術が存在する。その中でもタイムスタンプについて述べていく。タイムスタンプはe-文書法の施行などによって、電子データの長期保存を前提とした問題解決の手法であり、タイムスタンプとディジタル署名を組み合わせることでいくつかの問題を解決することが可能となる。
 電子文書といった電子データは、年が経つにつれて変化するようなものではなく、長期間の保存を行うだけだとしたら適した手法であると言える。しかし電子データは、複製や作成日時の偽造を容易に行うことが可能である。その影響もあり、本来作成された日時や原本を書いた作成者を特定することが難しい。そこで解決方法としてタイムスタンプを用いる。タイムスタンプは、時刻認証局であるTSAが時刻情報を含んだ状態で付与する。このタイムスタンプによって、電子文書が作成された日時、時刻以降の改ざんが行われていないことを保証することが可能となる。2005年には、「タイムビジネス(時刻配信及び時刻認証業務の総称)を提供する事業者が, 適切な技術, 運用, 設備の基準を満たし, 業務が厳正に実施されていることを認定する」[3]タイムビジネス認定制度が運用されている。
 上記の方法を用いることで、先ほど述べた作成時刻や改ざんに関する問題は解決することができるが、このままでは記録媒体の劣化、記録媒体や電子文書作成技術の衰退などといった問題が解決できていない。年が経っても変化することのない電子文書は、長期保存には適した手法ではあるものの、周りの技術によって、その有用性が確保されづらいのが難点である。他にも自然災害発生時の電子文書の紛失などに対しても、適切に対処する必要がある。
 電子文書を長期保存する場合、いくつかのファイル形式が推奨されている。例えば、フォーマットが公開されていてかつ他の文書形式からも変換することが可能なPDF/A形式、OSやアプリケーションに依存せずに利用できる上で画像の劣化が少ないTIFFイメージ形式などが存在する。現在、国立国会図書館などでは電子データの長期保存化が進んでいる。また複数の電子データ長期保存サービスを提供している会社も存在する。
 タイムスタンプは以下の手順によって生成される。([3]参照)
1.タイムスタンプ取得要求者が電子文書をハッシュ関数を用いてハッシュ化し、ハッシュ値を生成する。
2.1で生成したハッシュ値をTSAに送信する。
3.TSAは時刻配信局であるTAから時刻を受け正確な時刻を保持する。
4.TSAは受け取ったハッシュ値に時刻情報を連結したデータを、改ざん防止処置を行なったのちにタイムスタンプとして生成する。
改ざん防止処置には以下のようなものが存在する。
ディジタル署名
TSAがCAから公開鍵証明書の発行を受けた専用の暗号鍵を用いて各タイムスタンプにディジタル署名を施すことで信頼性を確保する。
・リンキング方式
TSAが複数の利用者のMDを関連づけたリンク情報を生成してタイムスタンプに含め、それぞれのタイムスタンプを他のリンクしているタイムスタンプに依存させておくことで、信頼性を確保する。
・アーカイビング方式
TSAが受信したハッシュ値と正確なタイムスタンプ付与時刻を特定する情報を、タイムスタンプ検証の際の照合用データとする。
5.TSAは生成したタイムスタンプを送信元に返信する。
 ここまでに述べたタイムスタンプの方式では、ハッシュ関数や暗号技術が危殆化する恐れがある。もし破られるような状態になってしまった場合、タイムスタンプを付与したとしても完全性を証明することができない。そこで、過去にディジタル署名が有効であったことを検証できるようなタイムスタンプを付与することでこの問題を解決する。この方式は、何度もタイムスタンプを付与する方式であり、アーカイブタイムスタンプと呼ばれる。この方式を用いる場合、付与するたびに最新技術を用いたアーカイブタイムスタンプを用いる必要がある。

 ここまでがPKIに関する基礎的な技術である。これらの技術の一部については、オープンソースソフトウェアであるOpenSSLを用いることで実現することができる。OpenSSLはSSL/TLSプロトコル関連のライブラリであるが、PKIを構築することも可能である。
 構築環境はFedora25, Openssl 1.0.2k-fipsを用いている。また[4]を参考にしている。まずCSRを作成するための秘密鍵を作成するには`openssl genrsa`コマンドによって作成することが可能である。この時、出力ファイルや鍵の方式、ビット長などをオプションでつけることができる。また秘密鍵を作成する際、オプションの付け方によってはpass phraseをつける必要がある。秘密鍵を作成した後に`openssl req`コマンドによってCSRを作成することができる。この時、コンフィグファイルや用いる秘密鍵、出力ファイルなどをオプションで設定することができる。コマンドを実行すると、国名や都道府県名などを入力するように促されるので、入力内容に従って入力していく。CSRを作成した後に、`oepnssl ca`コマンドによって証明書を作成することができる。この時、コンフィグファイルや用いる秘密鍵、証明書の形式、出力ファイルなどをオプションで設定することができる。コマンドの実行が終了すると証明書の作成が終了する。ただし他の証明書の鍵を用いて証明書を作成しない限り、自己証明書となる点に注意する必要がある。
 結果的に`oepnssl genrsa`コマンド、`openssl req`コマンド、`openssl ca`コマンドの3つを用いるだけで秘密鍵CSR、証明書の3つを作成することが可能である。もし作成した自己証明書をルートCAとするのであれば、先に作成した証明書の鍵を用いて次に作成する証明書の鍵とすれば良い。それだけでは作成した証明書からルート証明書を探索することはできないので、2つの証明書に対して`openssl x509`コマンドを用いて、証明書から必要な情報のみにした証明書を別途作成し、別途作成した2つの証明書をつなぎ合わせて一つの証明書にすると良い。

 最近のPKIに関する記事としては[5]が挙げられる。[5]は、IoTデバイスメーカーに向けて電子証明書を発行するサービスを提供する発表を、GMOグローバルサインが行ったものである。IoTの普及によりデータをやり取りすることが増えたこの世の中において、電子証明書を大量に発行できるサービスは魅力的なものである。また認証局についてもパブリック認証局とプライベート認証局から選べる点についても良い点であると感じる。プライベート認証局であれば、証明書の最長有効期限が40年も持つため、プライベート空間のネットワークで電子証明書を利用するのであれば、とても良い選択肢の一つである。IoTの全てに対して電子証明書を発行してPKIを適用するのは難しいことではあるが、セキュリティの向上を行う手段としては手頃なものであるとも感じる。
 
参考文献:
[1]PKI技術関連情報, https://www.ipa.go.jp/security/pki/index.html, 2012
[2]電子認証フレームワークIPアドレス認証の展開に関する調査報告書, https://www.nic.ad.jp/ja/research/200707-CA/all.pdf, 2007
[3]上原孝之, 情報処理安全確保支援士2017年度版, 翔泳社, 2017
[4]OpenSSLによるPKI構築, http://www.prosper2.org/devwiki/index.php?OpenSSLによるPKI構築, 2016
[5]GMOグローバルサイン、IoTデバイス向けのSSL証明書大量発行サービスを開始, http://www.atmarkit.co.jp/ait/articles/1705/08/news075.html, 2017