GNU httptunnelの改造
キャンプのチューターの応募前に去年のネットワーク組の講義内容を見直していました。*1その中で偽装通信(CovertChannel)のツールは沢山公開されていますが、ソースコードを読んだことがなかったので、いい機会だと思って、GNU httptunnel のソースを追いながら、軽く手を加えてみました。実装が1日だったので、結構荒いです。GNU GLOBALとvimに大変お世話になりました。
httptunnelの概要
軽くhttptunnelについて説明します。httptunnelはその名の通り、httpトンネリングツールです。プロキシなどで外部との通信にhttpのみなどの制限がかかっている場合に、サーバとhttpトンネルを通じてやり取りすることで、sshなどのプロトコルが使用できます。ソースを落として、makeすることで、hts(サーバ側)とhtc(クライアント側)ができます。やり取りは外から見ると一見、http通信をしているように見えますが(といっても普通に見てるだけでも、かなり怪しい)、そのHTTPのボディ部分でデータをやり取りすることで、Covert(隠れた) Channel(通信路)を張っています。
図はtelnetを使った時の概要です。x,y,zは適当な任意のポートです。telnetを使った理由はtelnetの脆弱な感じが好きだからです。特に意味はありません。以下、図に沿った場合の設定について書いていきます。
サーバ側(192.168.88.189)の設定
デフォルトの8888ポートを開いて待っていて、そこに届いたhttpっぽいプロトコルの箱から出した元のデータを23番ポートに転送する設定。
yasulib@server% hts -F localhost:23
クライアント側の設定
localhost:10023ポートを開いて待っていて、そこに届いたプロトコルをhttpっぽいプロトコルに箱詰めして server の -F オプションで指定されたポートに向かって投げます。
root@client# htc -F 10023 192.168.88.189 *2
取れるパケット
とりあえず、パケットを見たら、HTTPじゃないことはわかりますよね。telnet over httpなので、通信は暗号化してないのでユーザ名、パスワードも丸見えです。
変更
htc - hts 間のやり取りでのデータを暗号化することを目的とします。パケットを見てもわかるのですが、HTTPのボディにデータが詰められているので、それぞれボディの処理時のみ暗号化、復号を行うように書き替えます。暗号アルゴリズムは実装が楽な共通鍵によるXORで実装しました。
yasulib@ubuntu% diff src_crypt/tunnel.c src_orig/tunnel.c 481,487d480 < unsigned char *d = (unsigned char*)data; < size_t i; < for(i = 0; i < length; ++i){ < d[i] = d[i] ^ '\xff'; < } < data = (void*)d; < 818,820d810 < unsigned char* b; < unsigned short l = 0xffff; < size_t i; 824,826d813 < < req = req ^ '\xff'; < 861,862d847 < len = len ^ l; < 879,884d863 < b = buf; < for(i = 0; i < len; ++i){ < b[i] = b[i] ^ '\xff'; < } < buf = b; <
トンネル部分の実装はhts, htc共にtunnel.cを使っているので、これを修正してできました。
余談
以下、userのパスワードを探してくださいと言って、nao_pcapさんに0xffで暗号化したパケットを渡した時の反応です。
nao_pcap: 怪しさ満点なんだけど、どこから手をつけたら・・
nao_pcap: @yasulib httptunnelあたりをいじったとかかなぁ。
nao_pcap: @yasulib そのツール知らなかったですw tcpヘッダに違和感があるのはそのせいか。
tcpヘッダに違和感を感じるとか、怖いです (-_-;;)
tcpストリームを cipher.bin として保存すると、
> python >>> cipher = open('cipher.bin', 'rb').read() >>> bin = cipher[cipher.find('\r\n\r\n'): ] >>> plain = '' >>> key = 0xff >>> for ch in bin: ... plain += chr( ord(ch) ^ key ) ... >>> plain "\xf2\xf5\xf2\xf5\x01\x00\x01*\x02\x00\x0c\xff\xfb\x18\xff\xfb \xff\xfb#\xff\xfb '\x02\x00E\xff\xfa \x0038400,38400\xff\xf0\xff\xfa#\x00ubuntu:0.0\xff\xf0\xff\xf a'\x00\x00DISPLAY\x01ubuntu:0.0\xff\xf0\xff\xfa\x18\x00xterm\xff\xf0\x02\x00\x18 \xff\xfd\x03\xff\xfc\x01\xff\xfb\x1f\xff\xfa\x1f\x00\xee\x009\xff\xf0\xff\xfd\x0 5\xff\xfb!\x02\x00\x03\xff\xfd\x01\x02\x00\x01u\x02\x00\x01s\x02\x00\x01e\x02\x0 0\x01r\x02\x00\x02\r\x00\x02\x00\x01r\x02\x00\x01e\x02\x00\x01s\x02\x00\x01u\x02 \x00\x02\r\x00EEE"
ということで
id: user pw: resu
でした。