SECCON Beginners CTF 2020の監視・オペレーションを支える技術
LifeMemoryTeamの@atponsです。今回のSECCON Beginners CTF 2020はお楽しみいただけたでしょうか。自分は運営やインフラ整備をしておりました。 今回は、自分が担当していた監視、オペレーション部分の構築回りについて書いておきます。
続きを読むGrafanaのグラフを画像にしてSlackでシュッと取得&共有したい
UniFi Security GatewayでDS-Lite
実売2万6000円のIPS/IDS付きエッジルーター、Ubiquiti「UniFi Security Gateway(USG)」【イニシャルB】 - INTERNET Watchを見て、UniFi Security Gatewayの中身はEdgeOSなので簡単にDS-Liteできるなと思って買ったのですが、色々大変だったので、メモ。
続きを読むGolangでMemcached(を喋るサーバ)を書こうと思った話
某Advent Calendar 8日目の記事です。
途中で止まってしまったので、そのうち再開するかも...ということでここで一回供養する話です。
Memcacheプロトコルを喋るサーバを勉強と検証ということで作ろうと思い、色々とやっておりました。
モチベーションとしてはMemcacheプロトコルをしゃべるが、データを永続化しながら色々できるようにするという目的です。決してMemcachedのクローンを作る話ではなありません。(速い要素を実装する力がないため)
一応こちらに置いてありますが、作りかけのまま終わってしまいました。
できること
- Get
- Set
- Delete
- Incr/Decr
- Flush
ちなみに若干バグがありますが、そこは🙈
大変だったこと
- 変なクライアントに当たるとつらい
- コネクションを持ったままのクライアントだとたまに落ちるということがあったので、そのパケットを飛ばすと言うところを実装するのが結構つらかった
- CAS対応
- まだちゃんと実装しないまま終わってしまいました
実装について
今回はテキストプロトコルではなくバイナリプロトコルで実装を行いました。バイナリプロトコルは BinaryProtocolRevamped · memcached/memcached Wiki · GitHubが参考になりました。あとはひたすらパースする感じです。今回はストレージにSQLite/LRUなインメモリストレージをが使えるようにしてあります。
基本的な実装はserverでパケットを受け取り、handlerで各タイプを処理していきます。storageパッケージでストレージは抽象化していて、そこからSQLiteやLRUなストレージを操作するという感じです。Memcachedのバイナリプロトコルは結構わかりやすく、Opcodeを見てそれぞれ処理していく、あとはQuietlyに気をつければなんとかなります。たまに本物のMemcachedにリクエストを投げながらつじつまを合わせたりしました。(本当はソースを理解できればよかったけど)
一応、ベンチマークを取ってみましょう。ストレージをSQLiteにした結果はこちらです。とりあえずベンチが回るところまで作れて良かったなあという感想です。
ALL STATS ========================================================================= Type Ops/sec Hits/sec Misses/sec Latency KB/sec ------------------------------------------------------------------------- Sets 1330.79 --- --- 25.04900 98.88 Gets 13293.28 4505.96 8787.32 13.13200 466.84 Waits 0.00 --- --- 0.00000 --- Totals 14624.07 4505.96 8787.32 14.21600 565.73
遅すぎますが、一応使えることが分かります。LRUにすると、こうなります。
ALL STATS ========================================================================= Type Ops/sec Hits/sec Misses/sec Latency KB/sec ------------------------------------------------------------------------- Sets 1524.89 --- --- 12.94800 113.30 Gets 15232.17 15230.34 1.83 12.65700 534.94 Waits 0.00 --- --- 0.00000 --- Totals 16757.06 15230.34 1.83 12.68400 648.24
実際のmemcachedはこちら。比べものにならないことがわかります...。(早いモノを作るのが目的じゃなかったので良いのですが)
ALL STATS ========================================================================= Type Ops/sec Hits/sec Misses/sec Latency KB/sec ------------------------------------------------------------------------- Sets 12417.96 --- --- 1.45500 922.69 Gets 124043.18 123961.91 81.26 1.46400 4356.26 Waits 0.00 --- --- 0.00000 --- Totals 136461.14 123961.91 81.26 1.46300 5278.94
ちなみに、RubyのMemcacheクライアントのDalliから使って見ましょう。
irb(main):003:0> d = Dalli::Client.new("localhost:11211") => #<Dalli::Client:0x00007f92d2046d90 @servers=["localhost:11211"], @options={}, @ring=nil> irb(main):004:0> d.set("test",1) => 15988849166174051443 irb(main):006:1" ^C irb(main):006:0> d.get("test") => 1 irb(main):007:0> d.version => {"localhost:11211"=>"0.0.1"}
きちんと値がセットされていることが分かります。
今回のこれで具体的なサーバの書き方が若干分かったり(今見ると直したいところが一杯ありますが)、バイナリと仲良くなれたのは良い経験だったなと思いました。ちゃんと書き直すのはいつになるか分かりませんが、とりあえずこれで供養をしておきます。
はじめてのThinkPad
某アドベントカレンダー1日目は、最近Macをやめて、ThinkPadを購入したという話をすることにします。技術っぽい話はそのうちすると思います。
続きを読むpcapをパースする(その3・IP/ICMP編)
これまで以下の記事でEthernetフレームやARPのパケットをpcapファイルからパースするために必要なフォーマットについて解説していきました.
pcapをパースする(その2・ARP編) - ポン酢ブログ(β)
pcapをパースする(その1・ヘッダ編) - ポン酢ブログ(β)
引き続き,IPやICMPをパースしてみたいと思います.
IPパケット
IPパケットはRFC 791で策定されています.
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live | Protocol | Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
今回のパースは,Golangの準標準パッケージであるnetを使ってみる練習がてら,パースをそれに任せてみました.
実際にパースをする場合は,Parseを呼び,そこに[]byte
でパケットを入れてあげれば大丈夫です.
気づきとしては,socket.NativeEndian
には動作するOSのbinary.ByteOrder
が入っており,それを使って処理していました.
実装はこちらになっていて,パースしたあとにその後ろの[]byte
を返すようになっています.
ICMPパケット
IPパケットまでパースできたので,関連するICMPパケットについてもパースしていきたいと思います.ICMPはRFC 792で策定されています.普段pingコマンド等で馴染みのある人もいるのではないでしょうか.
ICMPのメッセージには多くの種類があり,こちらが参考になります.
今回のパースも,Golangの準標準パッケージであるnetを使ってみる練習がてら,パースをそれに任せてみました.実際にパースと言っても,structに値をマッピングしていく形になります.パッケージでの実装を参考にしながら使っていきますが,internalでメッセージのタイプが隠れていたため,仕方なく同じものを自分て定義しそれを使っています.
実際の実装部分です.
自作のものとつなげていく
自作しているpcapのパーサーでは,Packet
インターフェースを作り,それを満たすように各データを格納しています.そして,実際にに読み込んでいる部分はこちらになります.
実際にファイルを読み込ませてみると,パースできていることがわかります.
実はこの他にも追加した機能があるのですが,それはまた別の機会に説明します.