GNU httptunnelの改造

キャンプのチューターの応募前に去年のネットワーク組の講義内容を見直していました。*1その中で偽装通信(CovertChannel)のツールは沢山公開されていますが、ソースコードを読んだことがなかったので、いい機会だと思って、GNU httptunnel のソースを追いながら、軽く手を加えてみました。実装が1日だったので、結構荒いです。GNU GLOBALvimに大変お世話になりました。

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
クライアントからの接続
yasulib@client% telnet localhost 10023
取れるパケット


とりあえず、パケットを見たら、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を使っているので、これを修正してできました。

結果

0xffで暗号化した結果、滅茶苦茶怪しいになりました。

0x7fだと次のような感じです。

怪しすぎるので、base64エンコードしてascii文字列にしてから、適当なサイトのhtml取得して埋め込んだりとかすれば、パッと見わからなくなるのかな。とか思います。

余談

以下、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

でした。

*1:結果的にネットワーククラスのチューターではなかったのですが。

*2:httpプロキシを経由する場合は -P proxy:8080 とオプションで指定する