sonickun.log

備忘録

SECCON CTF 2014 Online Qualifications Write-Up

 SECCON 2014 オンライン予選(英語)にチームm1z0r3(みぞれ)として参加しました.結果は2100点で1067チーム中65位.完敗です.世界の壁は高かった...
 ただ,m1z0r3は国内予選6位で既に全国大会の切符を手に入れているので,本番までにもっと力を蓄えていきたいです.

 今回のSECCONで自分が解答した or 解答に携わった問題のWrite-Upを書きます.実際大した問題はあまり解いていない.鮮やかにHeartBleedの攻撃とかやりたかったです(HearBleedはリーダーが解いてくれた).
 

SECCON CTF 2014 Online
 


Reverse it (Binary, 100)

 "Reverseit"というファイルが渡される.fileコマンドを打つと"data"とだけ.さっそくバイナリを上から下まで眺めていると終端が"FF 8D FF"になっている.
 これどこかで見たことある......と思ったら"FF D8 FF"はJPEGの先頭のマジックナンバーであること気づく.先頭のバイナリに戻って見てみると"9D FF"...JPEGの終端のバイナリは"FF D9"...
 どうやらJPEGのバイナリが4bit(16進数1桁)ごと逆順に並んでいるよう."Reverse it"とはこのことか(笑) Pythonでコードを書いて並び替える.

f = open("Reverseit","rb").read()
g = open("Reverseit.jpg","wb")

f = f.encode('hex')[::-1].decode('hex')

g.write(f)
g.close()

 生成したファイルを開くとこの画像.またもやReverseになっている.
f:id:sonickun:20141207224641j:plain
 ということでこれも逆さにして,フラグはSECCON{6in_tex7}.バイナリからすぐマジックナンバーを思い出せたのは嬉しかった.
 
 
 

REA-JUU WATCH (Web, 200)

 問題文のURLにアクセスすると「リア充ウォッチ」という,なんとも愉快で不愉快なWEBアプリが現れる.
 f:id:sonickun:20141207225243p:plain
 次の画面でIDとパスワードが生成され,ログインすると次のような問題が始まる.
 f:id:sonickun:20141207225256p:plain
 6問ほど回答すると,リザルト画面に自分のリア充度スコアが表示される.
 f:id:sonickun:20141207225302p:plain

 FiddlerでHTTPリクエストを眺めていると,最後の問題を解いた後,別のURL(http://reajuu.pwn.seccon.jp/users/chk/[id])にもアクセスしている.[id]にはユーザーIDに対応した数字が入っているよう.試しにid=1にして,1番目のユーザーのところへつなぎに行くと,以下の文字が出現する.

{"username":"rea-juu","password":"way_t0_f1ag","point":99999}

 このようにユーザーの名前,パスワード,ポイントの情報が表示される.この情報を用いてもう一度ログインして問題に解答し,リザルト画面へ行くとフラグが現れた.
f:id:sonickun:20141207230211p:plain
 ということで晴れて真のリア充になることができた.フラグはSECCON{REA_JUU_Ji8A_NYAN}


 

Get the key.txt (Forensics, 100)

 "forensic100"という名前のシステムファイルデータが渡される.FTK-Imagerでマウントして見てみる.すると,中身に"key.txt"とあるファイルを見つける.
f:id:sonickun:20141207231543p:plain
 先頭バイナリ"1F 8B"を見た瞬間にgzipだと気づいたので,このファイルをエクスポートして解凍するとフラグが得られた.
 フラグ→SECCON{@]NL7n+-s75FrET]vU=7Z}
 
  
 

Get the key (Network, 100)

 nw100.pcapというpcapログが渡される.

  • だーっと眺めているとHTTP通信をしているよう.(10秒)
  • とあるURLにアクセスしている(中には"key.html"ヘのリンクが含まれている).(20秒)
  • 実際にアクセスするとBasic認証が必要みたい.(30秒)
  • pcapにもどり,BASE64エンコードされた認証データを抜き出す.(40秒)
  • BASE64をデコードし,IDとパスワードを取得.(50秒)
  • Basic認証を突破しkey.htmlにアクセスしてフラグゲット.(1分)

 瞬殺でした.フラグ→SECCON{Basic_NW_Challenge_Done!}

 フレーム番号21番のパケットのHTTPヘッダーが以下のようになっている.

Hypertext Transfer Protocol
    GET /nw100/ HTTP/1.1\r\n
        [Expert Info (Chat/Sequence): GET /nw100/ HTTP/1.1\r\n]
            [GET /nw100/ HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: GET
        Request URI: /nw100/
        Request Version: HTTP/1.1
    Accept: text/html, application/xhtml+xml, */*\r\n
    Accept-Language: ja-JP,en-US;q=0.5\r\n
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko\r\n
    Accept-Encoding: gzip, deflate\r\n
    Host: 133.242.224.21:6809\r\n
    Authorization: Basic c2VjY29uMjAxNDpZb3VyQmF0dGxlRmllbGQ=\r\n
        Credentials: seccon2014:YourBattleField
    Connection: Keep-Alive\r\n
    DNT: 1\r\n
    \r\n
    [Full request URI: http://133.242.224.21:6809/nw100/]
    [HTTP request 1/1]

 http://133.242.224.21:6809/nw100/へ接続している.
 "Basic c2VjY29uMjAxNDpZb3VyQmF0dGxlRmllbGQ="とあるので,これをBASE64エンコードすると"seccon2014:YourBattleField"となる.つまりBasic認証のユーザー名が"seccon2014",パスワードが"YourBattleField"となる.


 

Get from curious "FTP" server (Network, 300)

 FTPサーバのURLが渡される.ここにアクセスしてフラグとなるファイルをダウンロードしてくる問題らしい.
 さっそくアクセスして,ディレクトリ情報を取得しようとしたが,lsdirなどの,いわゆるLISTコマンドが使えない.

$ ftp ftpsv.quals.seccon.jp
Connected to ftpsv.quals.seccon.jp.
220 (vsFTPd 2.3.5(SECCON Custom))
Name (ftpsv.quals.seccon.jp:****): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful. Consider using PASV.
502 LIST not implemented.
ftp> dir
200 PORT command successful. Consider using PASV.
502 LIST not implemented.
ftp> quote list
502 LIST not implemented.

 "502 LIST not implemented."とは,つまりLISTコマンドが実装されていないという意味.ほかにもNLSTなどのコマンドも使えない.この状態でどうやってディレクトリ情報を取り出すかがこの問題のミソとなっている.また,get ./*のようにワイルドカード(*)を使用することもできないようになっている.

 他の使えそうなコマンドを探してみる.以下のサイトを参考にした.

 このサイトを見てみるとSTATコマンドの説明に「現在のシステムや転送状態の情報を表示する。ファイル/ディレクトリ名が与えられた場合は、その情報を表示する(NLSTなどとほぼ等価)」とある.そこでquote stat /を実行してみるとディレクトリ情報が取得できた.

ftp> quote stat /
213-Status follows:
drwxr-xr-x    2 0        107          4096 Nov 29 04:43 .
drwxr-xr-x    2 0        107          4096 Nov 29 04:43 ..
-rw-r--r--    1 0        0              38 Nov 29 04:43 key_is_in_this_file_afjoirefjort94dv7u.txt
213 End of status
ftp> get key_is_in_this_file_afjoirefjort94dv7u.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for key_is_in_this_file_afjoirefjort94dv7u.txt (38 bytes).
226 Transfer complete.
38 bytes received in 0.000115 seconds (330434 bytes/s)

 ということで,"key_is_in_this_file_afjoirefjort94dv7u.txt"をgetでダウンロードして開くとフラグが得られた.
 フラグ→SECCON{S0m3+im3_Pr0t0c0l_t411_4_1i3.}

 
 

version2 (Network, 200)

 「もうすぐ version 2 が来るけど準備はいいかい?」という問題文で,さらに「srv h2o.pwn.seccon.jp.」とドメインが与えられる.雑魚なのでこの時点で「version 2」が何のことが分かっていない.とりあえず「srv」とは何なのかを調べる.
 SRVとはDNSに含まれるレコードの一つで,動いているサービスやプロトコル,使用しているポート番号などの情報を保持しているらしい.SRVテーブルの情報はnslookupを用いて以下の様なコマンドで取得することができる.

$ nslookup
> set type=srv
> h2o.pwn.seccon.jp
Server:         10.1.2.1
Address:        10.1.2.1#53

Non-authoritative answer:
*** Can't find h2o.pwn.seccon.jp: No answer



Authoritative answers can be found from:
seccon.jp
        origin = ns1.value-domain.com
        mail addr = hostmaster.seccon.jp
        serial = 1417939413
        refresh = 16384
        retry = 2048
        expire = 1048576
        minimum = 2560
> _http._tcp.h2o.pwn.seccon.jp
Server:         10.1.2.1
Address:        10.1.2.1#53

Non-authoritative answer:
_http._tcp.h2o.pwn.seccon.jp    service = 1 1 65080 h2o.pwn.seccon.jp.

Authoritative answers can be found from:
> _https._tcp.h2o.pwn.seccon.jp
Server:         10.1.2.1
Address:        10.1.2.1#53

Non-authoritative answer:
_https._tcp.h2o.pwn.seccon.jp   service = 2 0 65432 h2o.pwn.seccon.jp.

Authoritative answers can be found from:

 SRV,nslookupに関して参考にしたサイト

 以上のことから,h2o.pwn.seccon.jp.では,65080番ポートでHTTP,65432番ポートでHTTPSが動いていることがわかった.実際にこのポートを指定してHTTP接続してみると以下の様なページが表示される.
f:id:sonickun:20141207235607p:plain
 この時点で「version 2」とはHTTP/2のことであると気づく. 
 HTTP/2 - Wikipedia, the free encyclopedia
 要はこのページにHTTP/2で接続すれば良い.Chrome ChromeのSPDYではHTTP/2が実装されている.HTTP/2の設定を有効にして接続すると以下の様なページが表示される.(HTTP/2はSSL必須なのでHTTPSで繋ぎに行く)
 f:id:sonickun:20141207235940p:plain
 「HPACK」とはデータの圧縮形式の一つで,HTTP/2で通信を行う際はこの形式でデータを圧縮して送信しているらしい.
 Chromeデベロッパツールでレスポンスヘッダを見てみたらフラグの書かれているヘッダが含まれていた.
 f:id:sonickun:20141208000247p:plain
 ということで,フラグはSECCON{spdy4isSoC001}

 HTTP/2,SPDYに関して参考にしたサイト

 余談だが,自分の代わりにHTTP/2でWEBサイトにつなぎに行ってくれるサービスもあったらしい.

f:id:sonickun:20141208000850p:plain



所感

 前回の国内予選から約半年間で,ある程度成長が実感できた.自分の知識や経験則からある程度攻略が楽になった問題もあったし,瞬殺すべき問題はきっちり瞬殺できたのもよかった.ただ,CTFを始めて数ヶ月の雑魚には変わりはないので,今後は自分の得意分野を伸ばして全国大会でも戦える力をつけていきたい.あとCTFはやっぱり楽しい.ちなみに近々チーム内CTFが開催されるのでとても楽しみです.
 

確率的情報検索 Okapi BM25 についてまとめた

 ひょんなことで情報検索の知識が必要になったので,勉強したことを簡単にまとめておきます.

 情報検索とは,コンピュータを用いて大量のデータ群から目的に合致した物を取り出すことです.
 Okapi BM25は情報検索における文章中の単語の重み付けの手法の一つであり,他にもTF-IDFと言ったアルゴリズムがあります.

 Okapi BM25 - Wikipedia, the free encyclopedia

 一般的にはTF-IDFよりも良い結果が得られると言われ,比較手法としてのベースラインになっています.
 
 

Term Frequency (TF)

 文書中において出現頻度の高い単語は重要であるという考え方です.
 ある単語Tiの文書Dj中における重みを考えると

TF(i,j) = (文書Djにおける単語Tiの出現回数) / (文書Djのの総単語数)

 となります.
 
 

Inverse Document Frequency (IDF)

 多くの文書において出現頻度の高い単語は重要ではないという考え方です.

n = 単語Tiが出現する文書数
N = 文書の総数
としたとき
IDF(i) = log(N) - log(n) = log(N/n)

 となります.
 ちなみに,TFとIDFをかけ合わせるとTF-IDFの式となり,文書とクエリの特徴ベクトルの内積から類似度を評価することができます.BM25はこのTF-IDFをさらに改良したものとなります.
 
 

Document Length (DL)

 これがDM25のミソとなってきます.
 ある単語の出現回数が同じ2つの文書について,総単語数の少ない文書と多い文書では,前者のほうがより価値があると考えます.

DL(j) = 文書Djの総単語数
としたとき
NDL(j) = DL(j) / (すべての文書の平均DL)

 となります.
 
 

Okapi BM25b (Combined Weight)

 これまでの3つの重み付けをインプットとして,BM25のCombined Weight(CW)を作ります.

CW(i,j) = [ TF(i,j) * IDF(i) * (K1 + 1) ] / [ K1 * (1 - b + (b * NDL(j)) + TF(i,j) ]

 となります.定数K1とbについて以下に説明します.これらはどちらもチューニングの役割を果たします.

 K1は単語の出現頻度による影響を調節します.1 < k1 < 2 で,K1 = 2が最も効果的と言われています.
 bは文書の長さによる影響を調節します.0 < b < 1で,b = 0.75が効果的と言われています.

 まとめると,この式でより大きく重み付けされるのは

  • 文書における単語の出現頻度の高いもの
  • 多くの文書における単語の出現頻度が低いもの
  • より短い文書において出現回数が大きいもの

 となります.

tkbctf4 みんなのWrite-upまとめ

 tkbctf4参加者の皆さんのWrite-upをまとめました.新しいWrite-upが公開され次第随時更新していく予定です.

tkbctf

問題一覧

No. Title Genre Score
1 monochrome bar steganography 100
2 rcrypto cryptography 200
3 high-low cryptography 400
4 high-low2 cryptography 400
5 gradius binary 100
6 Simple Serial Code binary 200
7 Just Do It ? binary 200
8 rakuda binary 300
9 Cheer of CPU binary 300
10 fourbytes binary 500
11 rand javascript 200
12 args javascript 200
13 amida misc 400
14 ITF point system web 300

Write-up

 上の問題一覧の表の番号が対応しています.
 「途中まで解いた」または「あとで書く」とあった問題は△で表記しています。

Write-up 1 2 3 4 5 6 7 8 9 10 11 12 13 14
電気通信大学MMA
kusano_k’s blog
mt_caret.blog
st98's Gists
わふぅ。
misodengakuのブログ
aki33524の日記
math314のブログ
jujube2333's Gists
sonickun.log
[:title=]
[:title=]
[:title=]

 
tkbctf4問題のソースコードはこちら

tkbctf/tkbctf4 · GitHub
 
記事の間違い,新しいWrite-upの情報等ありましたらsonickun@y_hagまでお知らせください.
 

その他 Write-up まとめ


SECCON 2014 CTF オンライン予選 みんなのWrite-upまとめ - sonickun.log

tkbctf4 Write-up rcrypto (cryptography200)

 tkbctfにチームm1z0r3として参加しました.結果は600点で71チーム中15位.自分が解いたのはrcryptoの1問だけでした.(つらい)
 javascriptの2問が解けそうで解けなかったのが悔しかったです.引き続き解いてみてWrite-upを書こうと思います.

rcrypto (cryptography200)

 encrypto.pyとresul.txtの2つのファイルが渡されます.

encrypto.py
import struct

N = 0xcd892c64f1803742a89e68567aea60283066b368d2c2454ad55f740fb0ff8e98e03f70cd9a2c9168294eee89a9f995d2596f2c0be881bfbfa72cccbb18a77287134e2ffc89e7bce766cca9136074a19f6807c3ff51e5cf53ec79838067cbefac809fccbbe2c1c4d170f3be58b34b9c3dfaeb2914341697d6503a826716073f9808b91c747030e2cdeaf20620b269c701d5ce96623c95c983678fd1175243b9eda7f6a310b4ccfd6c69fa836adb82271c56aa7ca267c1d47e64e69a0c3541f63432b4d75b881c1694a07b546eaee036521213967201cd91ff6a4e1e7c89027eb57096f2e3e5cd274d5cd272be44435b21648538f31760b4f8b50cbcd42f0873d5

def encrypt(M, B):
    return M * (M + B) % N

if __name__ == '__main__':
    s = open('flag', 'r').read()
    tkbctf = struct.unpack('<2I', 'tkbctf4\0')
    flag = int(s.encode('hex'), 16)
    print "1: %d" % encrypt(flag, tkbctf[0])
    print "2: %d" % encrypt(flag, tkbctf[1])
result.txt
1: 2302864379938375384787522457289953058762346515694717964988017701221632252068998721016780659512789645178039929483162432085291148128875938650563974652974607897108753247761759018159346471819526717450317624121005691577913298753406802656066710989103644069541226797561238591832001786871030959032526532824897381416113004865029523158041337929652792421367459253385313006702525506606665513325657107841851439410104118856833250549528309345600994438901026890993789031255524302387252820866177521016309668779564405438776186649089481716393902512049059686799788362854701624870108840227739233423565617147635810535089953364777452765391
2: 869821841437664579273841208702057334636310328092375384661895122870843280009327763942775667106913326453781559554453279690395959752854296081023254897131426262762124201024166059200347755016000992120349762030587256929453375758557293418778029046615012379759165445526510048231152939113590563355048407714266168812177988203610092674509339069396233061882191910214456155546393848777696938097852335164090584476493820466258280954729469300306911409684786367255431603152145902151223834708442564731418184393252984714050882676106734102146980036229108895568198263271905480015122766218776429803994468521463000793677542363327966917714


 encrypt.pyで暗号化された結果がresulet.txtに出力されています.暗号化前の平文がフラグになっているようです.
 暗号アルゴリズムは,平文(フラグ)をM,鍵をB,N,暗号文をRとすると

R = M * (M + B) (mod N)

 となっています.暗号化というよりハッシュっぽいですね.
 この式から単純にMを導くことは不可能ですが,今回はこの暗号化が以下のように2パターンあります.青字が既知の値で,赤字が未知の値です.

R1 = M * (M + B1) (mod N) ・・・①
R2 = M * (M + B2) (mod N) ・・・②

 この2式から未知の値Mを推定します.
 R1>R2かつB1>B2のとき,①式から②式を減算すると

R1 - R2 = M( B1 - B2 ) (mod N)

 が成り立ちます.(証明は省略)
 つまり

M( B1 - B2 ) - ( R1 - R2 ) = kN    (kは自然数)

⇒ M = {kN + ( R1 - R2)} / (B1-B2)

 となりM=の式が出来ます.この式において,Mが整数になるようにkをブルートフォースします.
 作成したスクリプトがこちら.

import struct

tkbctf = struct.unpack('<2I', 'tkbctf4\0')
t=tkbctf[0]-tkbctf[1]

R1=2302864379938375384787522457289953058762346515694717964988017701221632252068998721016780659512789645178039929483162432085291148128875938650563974652974607897108753247761759018159346471819526717450317624121005691577913298753406802656066710989103644069541226797561238591832001786871030959032526532824897381416113004865029523158041337929652792421367459253385313006702525506606665513325657107841851439410104118856833250549528309345600994438901026890993789031255524302387252820866177521016309668779564405438776186649089481716393902512049059686799788362854701624870108840227739233423565617147635810535089953364777452765391
R2=869821841437664579273841208702057334636310328092375384661895122870843280009327763942775667106913326453781559554453279690395959752854296081023254897131426262762124201024166059200347755016000992120349762030587256929453375758557293418778029046615012379759165445526510048231152939113590563355048407714266168812177988203610092674509339069396233061882191910214456155546393848777696938097852335164090584476493820466258280954729469300306911409684786367255431603152145902151223834708442564731418184393252984714050882676106734102146980036229108895568198263271905480015122766218776429803994468521463000793677542363327966917714
x=R1-R2

N=0xcd892c64f1803742a89e68567aea60283066b368d2c2454ad55f740fb0ff8e98e03f70cd9a2c9168294eee89a9f995d2596f2c0be881bfbfa72cccbb18a77287134e2ffc89e7bce766cca9136074a19f6807c3ff51e5cf53ec79838067cbefac809fccbbe2c1c4d170f3be58b34b9c3dfaeb2914341697d6503a826716073f9808b91c747030e2cdeaf20620b269c701d5ce96623c95c983678fd1175243b9eda7f6a310b4ccfd6c69fa836adb82271c56aa7ca267c1d47e64e69a0c3541f63432b4d75b881c1694a07b546eaee036521213967201cd91ff6a4e1e7c89027eb57096f2e3e5cd274d5cd272be44435b21648538f31760b4f8b50cbcd42f0873d5

k=1
while(1):
	if (k*N+x)%t==0:
		print (k*N+x)/t
		break
	else:
		k+=1
	print k
Result
k=569111799
M=8874284088105576854948238238614457044505028639011349374021861498995036023097152049555857239549350287166006347100199983957544356840501980854784901113821946197380611177705292212987004478484951309819182405098846767765333862409425926812573611864910204824928321142829182839508679805462040296761411851272619037561124670207800113160725445151643233355853069817035845548582628939322952705418085312089629008831113802005771457751200233715401850140367028206001736592528758851066145310031045563613680644635600425736132514865977264503188118025685603053077990165729300195014737832785024786331903041102248425142311946913492026078519

 計算に1時間以上かかりました.(途中何度諦めかけたことか・・・)
 きっともっと効率のいい解法があると思います.

 あとは得られたMをASCII文字に変換するだけ.

$ python
>>>a=8874284088105576854948238238614457044505028639011349374021861498995036023097152049555857239549350287166006347100199983957544356840501980854784901113821946197380611177705292212987004478484951309819182405098846767765333862409425926812573611864910204824928321142829182839508679805462040296761411851272619037561124670207800113160725445151643233355853069817035845548582628939322952705418085312089629008831113802005771457751200233715401850140367028206001736592528758851066145310031045563613680644635600425736132514865977264503188118025685603053077990165729300195014737832785024786331903041102248425142311946913492026078519
>>> s = hex(a)[2:]
>>> z=''
>>> for i in range(0, len(s), 2):
...   z += chr(int( (s[i]+s[i+1]), 16))
...
>>> z
'FLAG{R4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!}FLAG{R4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!}FLAG{R4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!}FLAG{R4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!}FLAG{R4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!}FLAG{R4b1n cryp705y57'

 ということで,フラグはR4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!でした.
 
 ほぼ数学の問題でした.

Pythonでnslookup(ドメインの正引きと逆引き)する

ドメインの情報が知りたければdignslookupコマンドを叩けば良いっていう話ですが,複数ドメインもしくはIPアドレスをまとめて処理したいってこと,あるあるですよね.

Python Script

lookup.py

# -*- coding: utf-8 -*-
import socket
import sys

# 正引き
def foward_lookup(domain):
	try:
		return socket.gethostbyname(domain)
	except:
		return False

# 逆引き
def reverse_lookup(ip):
	try:
		return socket.gethostbyaddr(ip)[0]
	except:
		return False

if __name__ == "__main__":
	f = open(sys.argv[1]) # ファイルの読み込み
	for host in f:
		host = host.replace("\n","")
                # 正引き
		ip = foward_lookup(host)
		print ip
                # 逆引き
		#domain = reverse_lookup(host)
		#print domain
	f.close()


正引き

Input Data

data1.txt

twitter.com
google.com
sonickun.tokyo
yahoo.jp

Output

# python lookup.py data1.txt 
199.59.148.82
173.194.117.174
False
183.79.227.111


逆引き

Input Data

data2.txt

199.59.148.82
173.194.117.174
8.8.8.8
183.79.227.111

Output

# python lookup.py data2.txt 
r-199-59-148-82.twttr.com
nrt04s10-in-f14.1e100.net
google-public-dns-a.google.com
yjpn110.mobile.vip.ogk.yahoo.co.jp


スマブラ for 3DSの通信を解析してみた

 2014年9月13日発売の「大乱闘スマッシュブラザーズ for Nintendo 3DS」のネットワーク対戦について,どのような通信を行っているのか解析してみました.

スマブラ for 3DS

 みんな大好きスマブラです.今作では,ゲームをインターネットに接続して遠隔地にいる人とも対戦できるようになっています.また,対戦形式には様々な種類があり,エンジョイ部屋,ガチ部屋,大観戦などがあります.

大乱闘スマッシュブラザーズ for Nintendo 3DS
 
 

パケットキャプチャについて

 今回スマブラの通信について調べるために,対戦時に送信されるパケットをキャプチャして解析しました.そもそも3DSのパケットなんてどうやって拾うんだと思うかもしれませんが,少しテクニカルな方法でキャプチャが可能です.
 ここでキャプチャの方法は具体的には書きませんが,ヒントとなる記事を以前書いたので紹介しておきます.

【実験】ARPスプーフィングによる中間者攻撃 - sonickun.log
 
 上記の記事でも述べていますが,この方法は通信の盗聴にも使えてしまうので,取り扱いには要注意です.今回はあくまで調査,学習目的です.くれぐれもゲームの不正行為やその他の悪用目的でパケットキャプチャはしないでくださいね.
 
 

TCP/UDPについておさらい

 みなさんにとって常識かもしれませんが,この先TCPUDPの話が出てくるので少しおさらいしておきます.
 TCP,UDPOSI参照モデルトランスポート層で動作する通信プロトコルで,以下のような違いがあります.

プロトコル TCP UDP
通信方式 コネクション型 コネクションレス
信頼性 高い 低い
転送速度 低速 高速
主な用途 WEBの閲覧,メール送受信,ファイル転送 音声通話,ビデオストリーミング

 このようにTCPUDPは用途によって使い分けられています.この先この表を頭に入れながら読むとわかりやすいかと思います.
 
 

対戦時(エンジョイ部屋)の通信

 「エンジョイ部屋」とはランダムにマッチングされた4人で同時対戦する部屋です.
 

トラフィックについて

 まずは対戦時のトラフィックを見てみます.
f:id:sonickun:20141019231914p:plain
 グラフを見ると,対戦時はUDPを用いて通信していることがわかります.対戦中はきれーいにUDPトラフィックが発生しています.やはりリアルタイム性を重視する対戦ゲームですから,TCPよりも高速なUDPが用いられています.また最初のインターネット接続時にはTCPでどこかしらのサーバ(アメリカの任天堂社とかがあった)に接続しているようです.
 他にも,エンジョイ部屋に入ってマッチングが行われている時などにはスパイクが観測されました.引き分けサドンデスに突入する直前には一瞬グラフが下がっているのもしっかり見えますね.
 UDPポートは50000番台~60000番台が使われていて,プレイヤーごとに違うようです.
 

通信方式について

 次に対戦時の通信方式について見てみます.結論から言うと,スマブラではP2P接続により対戦をしているようです.つまり,対戦時に中継のサーバを介すことなくプレイヤー同士を直接つなぐので,対戦者間でより高速にパケットのやりとりが出来ます.
 P2P接続であると判断した根拠は以下のとおりです.

  • 4人対戦時には3つのホストと通信をしている
  • 3人に対して同じパケットを送信している
  • 接続先のIPアドレスのほとんどはISPのもの
  • 対戦相手が変わるごとに接続先のIPも変化する

 つまりはパケットキャプチャにより通信相手のIPアドレスがわかってしまうわけですねぇ...そういえば以前,IPアドレスから位置情報を推定する記事を書いたような...

GeoIPでIPアドレスから位置情報を取得する[Python] - sonickun.log
 

パケットの中身について

 次にUDPパケットのペイロード部分について調べてみます.といっても意味不明なバイト列が並んでいるだけで全く読めません.おそらくキー操作の情報やキャラクターの座標の情報をエンコードないしは暗号化して送信していると思われます.キー操作とパケットを照らしあわせてエンコード方式を推測するといった気力は僕にはありません.以上です.
 

パケットの転送頻度について

 スマブラ3DSでは60fpsでキャラクターが動くようになっています(1秒間に60フレーム切り替わるという意味).そこで僕は「通信対戦時には1秒間に60パケット送信している」という仮説を立てました.その検証結果がこちら.
f:id:sonickun:20141020002212p:plain
 仮説通り,1秒間で1人の対戦相手につき60パケット弱(多少ドロップしている)を3人に対して送信していることがわかりました.1パケットのデータ量は平均およそ300byteほどです.通信対戦でもfpsを落とさず,かつ遅延も殆ど無く対戦できるようにした任天堂はえらいですねぇ~ 
 

ガチ部屋(1on1)での通信トラフィック

 「ガチ部屋(1on1)」とはその名の通り1体1でガチンコ勝負する対戦形式です.ガチ部屋でのトラフィックを解析した結果がこちら.
f:id:sonickun:20141019235015p:plain
 グラフの形はエンジョイ部屋のものとほぼ同じです.注目すべきはエンジョイ部屋が4人対戦に対してガチ部屋が2人対戦になっているので,トラフィック量がおよそ半分になっている点です.なんとなく半分より少ない気がするのはガチ部屋ではアイテム無しなのでその分の情報量が少なくなっているから...?

大観戦(リプレイチャンネル)の通信

 最後に大観戦(リプレイチャンネル)の通信について調べてみます.リプレイチャンネルでは過去に誰かがプレイした動画を見ることができます.その際のトラフィックを解析した結果がこちら.
f:id:sonickun:20141020002318p:plain
 リプレイチャンネルでは,過去のプレイ情報を最初のロード時間に一括ダウンロードしているようです.その際にはTCPプロトコルが利用されています.これをWiresharkのFollow TCP Streamでつなげてみると,約127MBのデータ量がありました(対戦時間は2分).リプレイチャンネルでの通信はリアルタイム性よりも信頼性を重視するためにUDPではなくTCPを使っているということになりますね.
 また気になるダウンロード元ですが,Amazon EC2サーバと思われるIPでした.自社のサーバを使っていなかったのは少し意外でした.
 
 

まとめ

 スマブラ for 3DSのインターネット対戦ではP2PによるUDP通信が行われており,できるだけ遅延を減らすような工夫が施されていました.また,サービスの性質によって適切にプロトコルを使い分けています.
 さらに詳しく解析すればもっと面白いことがわかると思います.個人的にはセキュリティの見地からもみてみたいと思っています.「パケットを偽造して不正行為は可能か,あるいは不正は検知されるか」というのもやってみたいと思いましたが,3DSを2台用意して自分だけの環境でやる必要があるでしょうし,なんとなくグレーな感じがするのでやめておきます(笑).その他いろいろな解析方法については,まわりのひとにアイデアを貰いたいところです.
 スマブラを通してネットワークの勉強ができるなんて嬉しい限りですね!

ハニーポットはじめました。

 サーバ初心者がついに念願のハニーポット運用を始めました.
 新しいサーバも同時に立ち上げたので環境構築でアホみたいにつまづいています.

ハニーポット(Dionaea)について

 ハニーポットには分類がありまして,低対話型/高対話型とサーバ型/クライアント型があります.
 

  • 低対話型・・・本物の「ような」環境や、その反応をエミュレートすることで侵入、攻撃を行う者(または、プログラム)を観察し、情報を得るもの
  • 高対話型・・・脆弱性が存在する本物のOSやアプリケーションを用いて構築されるもの

 

  • サーバ型・・・攻撃を待ち受ける
  • クライアント型・・・わざと脆弱性のある環境でアクセスし、感染の有無でウイルス攻撃を検出する

 
 今回選んだ"Dionaea"というハニーポットは,「低対話型サーバ型ハニーポット」に分類されます.(読み方は「ディオナエア」で合ってる?笑)
 IPAの資料によると,Dioneaaは「最も推奨されるハニーポットツールFTP、WINS、TFTP、MS Windows RPC、SMB、HTTPSMSSQLMySQLVoIP など幅広くサポートし、動作のエミュレーションレベルも高い」とあります.

 

今後ハニーポットでやりたいこと

  • パケットキャプチャ&Dionaeaのログをひたすら眺める
  • ログを元に色々統計をとってみる
  • マルウェアを拾ったら解析してみる
  • WEB周りをごにょごにょしたい,PHPを書きたい
  • 各種解析ツール,便利ツールなどを入れる(WinScpとか)
  • ドメイン取得した方がいいというお話を聞いたので検討する

 
 

パケットキャプチャについて

 パケットキャプチャにはtcpdumpを使っています.pcapデータの解析にはtsharkなどの各種ツールを使っていこうと思います.
 
 ず-っと同じファイルに書き出していくと,サイズが大きくなって解析もしにくくなるので,ファイルを1日毎に分割する工夫をしました.

tcpdump.sh
#!/bin/sh
#86400秒(1日)毎にファイルを分割して出力する
sudo tcpdump -G 86400 -np -Z root -w /home/***/pcap/tcpdump_%Y%m%d%H%M%S.pcap 

どうせなら深夜0時0分にキャプチャを始めたいということで,atコマンドでスケジューリングをします.

$ at "00:00 03.10.14" -f tcpdump.sh

 
 

【おまけ】さっそくやらかした話

 sudoの設定ファイルのアクセス権限をいじったせいでsudoの実行ができなくなって最終的にサーバのインスタンスそのものを潰すことになった話をします.

 とある事情で,sudoの設定ファイル"/etc/sudoers"を編集する必要があり,単純に書き換えようとしたら,書き込み権限ないためにできませんでした.
 そこでアクセス権限の情報を見てみると

-r--r-----  1 root root    4004 Oct  3 13:11 sudoers

 とあり,「あーなるほど,rootでも書き込み権限がないんだな,ならchomdで権限を与えてやろう」

 と,chmodで権限を与える.
 
 /etc/sudoersにアクセスすると「書き込みできるようになっちゃってるよ!危ないから開けないようにしておくよ!」と言われ,開けなくなる.
 
 sudoを実行しようとすると「設定ファイルが書き込みできるようになっちゃってるよ!危ないから実行できないようにしておくよ!」と言われ,実行できなくなる.

 chmodで権限を元に直そうにもsudoが使えないので実行できない.


 詰む.


 こんな感じで何もできなくなり,インスタンスを新しく作りなおすことになりました(笑)

 事の顛末は,実はLinuxでは,上記のような権限情報になっていてもrootでは書き込みができるようになっているとのこと.
 ですから,権限をいじらなくてもsudo viとかsudo visudoでよかったんですねぇ...パソコン初心者なのでつらい…
 
 
 良い教訓になりました.