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 まとめ
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(ドメインの正引きと逆引き)する
ドメインの情報が知りたければdig
やnslookup
コマンドを叩けば良いっていう話ですが,複数のドメインもしくは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についておさらい
みなさんにとって常識かもしれませんが,この先TCPとUDPの話が出てくるので少しおさらいしておきます.
TCP,UDPはOSI参照モデルのトランスポート層で動作する通信プロトコルで,以下のような違いがあります.
プロトコル | TCP | UDP |
---|---|---|
通信方式 | コネクション型 | コネクションレス型 |
信頼性 | 高い | 低い |
転送速度 | 低速 | 高速 |
主な用途 | WEBの閲覧,メール送受信,ファイル転送 | 音声通話,ビデオストリーミング |
このようにTCPとUDPは用途によって使い分けられています.この先この表を頭に入れながら読むとわかりやすいかと思います.
対戦時(エンジョイ部屋)の通信
「エンジョイ部屋」とはランダムにマッチングされた4人で同時対戦する部屋です.
トラフィックについて
まずは対戦時のトラフィックを見てみます.
グラフを見ると,対戦時はUDPを用いて通信していることがわかります.対戦中はきれーいにUDPのトラフィックが発生しています.やはりリアルタイム性を重視する対戦ゲームですから,TCPよりも高速なUDPが用いられています.また最初のインターネット接続時にはTCPでどこかしらのサーバ(アメリカの任天堂社とかがあった)に接続しているようです.
他にも,エンジョイ部屋に入ってマッチングが行われている時などにはスパイクが観測されました.引き分けサドンデスに突入する直前には一瞬グラフが下がっているのもしっかり見えますね.
UDPポートは50000番台~60000番台が使われていて,プレイヤーごとに違うようです.
通信方式について
次に対戦時の通信方式について見てみます.結論から言うと,スマブラではP2P接続により対戦をしているようです.つまり,対戦時に中継のサーバを介すことなくプレイヤー同士を直接つなぐので,対戦者間でより高速にパケットのやりとりが出来ます.
P2P接続であると判断した根拠は以下のとおりです.
つまりはパケットキャプチャにより通信相手のIPアドレスがわかってしまうわけですねぇ...そういえば以前,IPアドレスから位置情報を推定する記事を書いたような...
GeoIPでIPアドレスから位置情報を取得する[Python] - sonickun.log
パケットの中身について
次にUDPパケットのペイロード部分について調べてみます.といっても意味不明なバイト列が並んでいるだけで全く読めません.おそらくキー操作の情報やキャラクターの座標の情報をエンコードないしは暗号化して送信していると思われます.キー操作とパケットを照らしあわせてエンコード方式を推測するといった気力は僕にはありません.以上です.
大観戦(リプレイチャンネル)の通信
最後に大観戦(リプレイチャンネル)の通信について調べてみます.リプレイチャンネルでは過去に誰かがプレイした動画を見ることができます.その際のトラフィックを解析した結果がこちら.
リプレイチャンネルでは,過去のプレイ情報を最初のロード時間に一括ダウンロードしているようです.その際には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、HTTPS、MSSQL、MySQL、VoIP など幅広くサポートし、動作のエミュレーションレベルも高い」とあります.
今後ハニーポットでやりたいこと
- パケットキャプチャ&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
でよかったんですねぇ...パソコン初心者なのでつらい…
良い教訓になりました.
【実験】ARPスプーフィングによる中間者攻撃
ARPスプーフィングとは、ARPプロトコルの応答を偽装することにより、LAN上で通信機器のなりすましを行なう技法です.これを用いて中間者攻撃すると大変なことになってしまうというお話です.
10月のSECCON札幌大会がARP Spoofing Challengeということで,予行演習がてらいろいろ手を動かしてみました.
実際に自分で実験してみて,改めて凶悪なハッキング手法だと知りました.まさに現代の黒魔術...
スライドでも述べていますが,パケットの盗聴,なりすまし,通信の乗っ取りなどは犯罪につながるので絶対にやめましょう.(実験はあくまで自分の環境で)
以前,ARP関連で記事を書いたので紹介しておきます.こちらはARPのとあるバグ(?)を利用してプロミスキャスモードの盗聴ホストを特定するという話です.ARPスプーフィングの話も少し出てきます.
ARPを利用してプロミスキャスモードの盗聴ホストを特定してみた - sonickun.log
家庭用プリンタがポートスキャンの踏み台になった話
もりたこさん(@mrtc0)の記事を読んで,「Idle Scan」という送信元を隠して行うポートスキャンについて興味をもったので,自宅にあるネットワーク機器にも踏み台になりうるものはないかなーと探していたら,ありました.
プリンタが.
完全にもりたこさんの記事の二番煎じになりますが,書きます.
Idle Scan の概要
あえて英語で書くと,こう.
- Probe the zombie's IP ID and record it.
- Forge a SYN packet from the zombie and send it to the desired port on the target. Depending on the port state, the target's reaction may or may not cause the zombie's IP ID to be incremented.
- Probe the zombie's IP ID again. The target port state is then determined by comparing this new IP ID with the one recorded in step 1.
( 引用:TCP Idle Scan (-sI) )
なるほど.
※概要についてはもりたこさんがすごく丁寧にまとめてくれています.
手動でパケットを送ってIdle Scanを行うことも可能ですが,Nmapの-sI
オプションでIdle Scanを行うことができるそうです.
実験
用意したマシンがこちら
- 攻撃者: 192.168.11.9
- ターゲット: 192.168.11.1
- 踏み台(Brother DCP-J515N Printer): 192.168.11.2
まずは踏み台となるプリンタの調査から.
攻撃者側からプリンタに向けてhpingでSYNを送ってみる.
IPフラグメントIDに注目すると,数が(ほぼ1ずつ)単調増加していることがわかります.つまり,次に来るIDが何かを容易に推測でき,Idle Scanが成立します.(ほんとはIDはランダムになっているのが理想)
そしてNmapのIdle Scanを使い,ターゲットのオープンになっている80番ポートのスキャンを実行します.
できた\(^o^)/
これにより,攻撃者はターゲットに全く気付かれずにポートスキャンを行うことに成功しました.
Nmap の挙動
せっかくなので,Nmapの-packet-trace
オプションで挙動を追ってみました.
最初に攻撃側からプリンタに向けて6パケットほど連続でSYNを送りIDのパターンを調査しています.そして
Idle scan using zombie 192.168.11.2 (192.168.11.2:80); Class: Incremental
とあり,IDの変化パターンが"Incremental"であると判断しています.
この時点でプリンタのIPフラグメントIDは"44219"です.
次に攻撃側は送信元をプリンタのIPのに偽造したパケットをターゲットに向けて5パケット送信します.すると,下記のようにターゲットからプリンタに向けてパケットが返ってきました.
SENT (0.3325s) TCP 192.168.11.1:43401 > 192.168.11.2:80 SA ttl=38 id=38333 iplen=44 seq=3299246808 win=1024 <mss 1460> SENT (0.3830s) TCP 192.168.11.1:43401 > 192.168.11.2:80 SA ttl=46 id=29500 iplen=44 seq=3299246809 win=1024 <mss 1460> SENT (0.4341s) TCP 192.168.11.1:43401 > 192.168.11.2:80 SA ttl=50 id=16813 iplen=44 seq=3299246810 win=1024 <mss 1460> SENT (0.4852s) TCP 192.168.11.1:43401 > 192.168.11.2:80 SA ttl=59 id=12389 iplen=44 seq=3299246811 win=1024 <mss 1460> SENT (0.7861s) TCP 192.168.11.9:43641 > 192.168.11.2:80 SA ttl=50 id=11090 iplen=44 seq=1178307903 win=1024 <mss 1460>
この直後,攻撃者がプリンタに向けて1パケットを送信したところ以下のパケットが返ってきています.
RCVD (0.7943s) TCP 192.168.11.2:80 > 192.168.11.9:43641 R ttl=64 id=44224 iplen=40 seq=136725895 win=0
フラグメントIDが"44224",つまり,ターゲットに送った5パケット分IDの数が増加しているため,ターゲットのポートが開いていることがわかりました.