副題: MITM 攻撃による秘匿化された SIP 接続情報の取得について
はじめに
最近サービスインとなった、関西電力傘下の K-opti.com が提供するIP電話サービスである LaLaCall を契約してみました。
この分野は NTT Communications の 050 plus や Fusion Communications の FUSION IP-Phone SMART といったサービスが有名ですが、これらのサービスの基本であるIP電話と LINE のメッセージングサービスのいいとこ取りを図って第3極としての地位を狙う、という感じのようです。
現時点では Android 向けのアプリのみが提供されており、 iOS 端末向けのものは近日公開予定だそうです。
IP電話といえば、古くからSIPが標準プロトコルとしての地位を確立しており、最近スマートフォン向けに提供されている、いわゆる050番号サービスのほとんどが、SIPをベースプロトコルとしていかに3G網のネットワーク制限を突破するか、という点に力を置いてアレンジが加えられたものになっています。
特に、LTE世代に突入し、端末にはプライベートアドレスが割り振られてNATが標準になったこと、また spmode などのISP側でプロトコル制限が掛かるようになり、SIP標準である udp:5060 が通じなくなっていること、が大きな壁となっています。さらに、SIPのみならず音声通信に用いられるRTP側の手当ても必要であり、なおかつ wifi 環境まで視野に入れると盗聴防止という観点からもTLSの選択は必須となります。
ということで、同じくSIPの派生であろうという推測のもと、 LaLaCall の純正アプリを導入しパケットキャプチャを行ってみました。
パケットキャプチャの結果
すると、アプリの初期設定をHTTPSで行っていることが確認できました。
よく知られているように、iOS 端末ではOSで設定したプロクシの設定が全てのアプリに反映されるため、例えば burpproxy のようなソフトウェアを介在させることでHTTP通信を傍受することが可能です。また、偽造証明書を導入すればアプリの実装によってはHTTPS通信をも傍受することが可能です。
一方、Android 端末ではプロクシの設定は通常ブラウザ閲覧にのみ反映され、アプリが行う通信内容を傍受することは比較的困難であり、それこそ透過プロクシなどネットワーク側からの攻撃テクニックを用いる必要がありました。
そこで、rooted な端末に透過プロクシ(SandroProxy)を導入して観察を続けると、SIPパケットについても Port 443 にTLS接続をしていることがわかりました。いわゆる SSL-VPN と同様の回避手段ですね。一般的な NAT/FW ではSSL通信の中身までは確認できないため、ほぼ無条件で通過させてしまうことを逆手に取った方法です。
で、端末に導入した SandroProxy は HTTPS のみ対応だったので、練習がてら本格的な中間者攻撃を仕掛けるべく、今回は有名どころの mitmproxy よりもプロトコル汎用性の高い SSLsplit を導入した環境を構築し、通信内容を解析しました。
SSLsplit によるMITM攻撃の実際
用いた環境は Ubuntu Linux 13.10 です。
- eth0 に通常の外部アクセスが可能な LAN を接続
- eth1 を eth0 とは異なるアドレス空間に設定。 eth1 のIPアドレスを固定し、これをデフォルトゲートウェイとした DHCP パケットを発行するように uhttpd を設定し、稼働
- eth1 に無線LANのアクセスポイントを接続
これでAPに接続したスマホには、外部アクセスが可能な LAN とは別空間のアドレスが割り当てられます。デフォルトゲートウェイは eth1 のアドレスが指定されているはずですので、通信内容は全て eth1 を経由することになります。その上で、 eth0 と eth1 を iptables でNATします。ここまでが下ごしらえ。
次に、 SSLsplit を導入します。パッケージレポジトリにもあるのかもしれませんが、今回はふつーに make しました。詳細な使い方はこの辺を見て頂くかググってもらうとして、さらに iptables を書き換えて SSLsplit が待ち受けるポートに通信内容をリダイレクトします。
で、この状態でスマホから HTTPS 通信を行うと、本来なら SSLsplit 導入時に OpenSSL で作成したオレオレ証明書がスマホ側に提示されるはずなのですが、ブラウザ以外で証明書関連のエラーを吐いたのは twitter 純正クライアントと twicca くらいでした。それ以外は何もエラー出さずに通信内容丸見え。いやはや。
ここまでで準備が終わったので、後は LaLaCall で登録手続きを行います。通信内容を見てもらえばわかりますが、今となっては珍しい SOAP で登録が行われています。API は userLogin → startCallBack → getCallBackResult という3段階を踏んでいて、 startCallBack を叩くと PIN コードが発行され、確認の電話がかかってきます。
どうでもいいんですけど、LaLaCall は携帯番号のみだったと思うので難しいでしょうが、こういう電話確認型の宛先に例えばマージン取得型国際電話番号とかQ2とか指定できるんですかね?Q2ってもう無くなったんでしたっけ?
LaLaCall におけるSIP通信
閑話休題。
全ての情報が間違いなく登録されると、サーバー情報などの各種情報が一気に提示されます。メッセージングアプリのプロトコルは XMPP なのかーとか。
こうして得られたSIP周りの情報を、今度は microsip を使って登録してみます。SIPの REGISTER 等々各種操作も TCP:443 を通じて行われるので、このまま傍受を続けて情報を収集してみましょう。
SIP パケットを見ていて最初に気づくのは、先の操作で取得した user_id ってダミーじゃね…?ということでしょうか*1。まさか電話番号がユーザーIDだとは…。あとは 407 が発行されているので Proxy Authentication が必須と思われます。さらに電話をかけてみると、 INVITE で RTP オプションとして
m=audio 48888 RTP/SAVP 18 0 101
a=crypto:1 AES_CM_128_HMAC_SHA1_32 inline:******************************
a=rtpmap:18 G729/8000
a=rtpmap:0 PCMU/8000
が指定されていることがわかります。これより、
- SAVP なのでセキュア必須。アルゴリズムは 128 bit AES/HMAC(SHA1)(SDP における暗号化アルゴリズムの詳細は RFC 4568 に記載があります)
- 対応コーデックは ulaw(PCMU) および G.729
ということだそうで、microsip はこれら要求条件を全て満たしています。
ということでレジストしてみたのですが、すんなり通ってしまいました。ついでに発着信とも可能でした。 ごめんなさい、発信はOKだけど着信がどうやらダメになっているようです。エラーログを見ていると、
**:**:**.*** pjsua_media.c ....pjmedia_transport_media_start() failed for call_id 1 media 0: SRTP crypto-suite name not match the offerer tag (PJMEDIA_SRTP_ECRYPTONOTMATCH) **:**:**.*** pjsua_call.c ...Unable to create media session: No active media stream after negotiation (PJMEDIA_SDPNEG_ENOMEDIA) [status=220048] **:**:**.*** endpoint ....Request msg BYE/cseq=31116 (tdta0520C500) created.
という感じになっていて、クライアント側から提示した暗号化アルゴリズムのシーケンスIDと、それを受けてサーバーから返ってくる指示のシーケンスIDが食い違っている?ようなのが原因ぽい。ソース見たけどちょっと手入れるのは厄介そう…。
両面待ちについて
PCとスマホの両面待ちも可能といえば可能なのですが、不安定なので現実的には同時使用は困難でしょうね。あと、原則的に両面待ちを考慮していない*2根拠として、050番号を端末のデバイスIDと紐付けていることと、所有者確認コールバックが通るとそのたびにパスワードが振り出され、更新されることが挙げられます。パスワードだけを叩き出すAPIが用意されていないのか、スマホのDB上にあるパスワードで接続に失敗する場合、スマホ側ではアプリ初期化のうえ再登録を行うように指示されてしまいます。
なお、スマホ側の DB って sqlite なのですが、格納されている値は BASE64 なものの、デコードしても何か操作された値が出てきて使い物になりませんでしたことをお知らせしておきます。
あとがき
実際には割と試行錯誤してるのですが、文章に起こすと全体的にあっさり通ったかのような印象がありますね…。結局各種情報を取れてもパズルみたいなもので、解けた瞬間に飽きちゃってエントリ起こした後はおとなしく純正アプリで使おうと思います。