eBPFでゲストのCPUIDをトレースする
これはMCC Advent Caleandar 20日目の記事です.
前日はUm6ra1さんによるMediBang Paintのmdpファイルを解析してみたでした.
はじめに
eBPFのチュートリアルとして,KVMゲストによるCPUID Instructionの実行をホスト側でeBPF(とTracepoints)を使ってトレースしてみる.ここでは,eBPFはBCCを使って書く.BCCのサンプルソースには kvm_hypercall.py
というのがあり,このサンプルの動きは以下のようになっている.
kvm_exit
,kvm_entry
,kvm_hypercall
をフックする.kvm_exit
のフックにおいて,VMExit ReasonがVMCALLによるものであったら,eBPF Mapにフラグを立ててprintする.kvm_hypercall
のフックにおいて,eBPF Mapにフラグが立っていれば,args->nr
(VCPU_REGS_RAX
) をprintする.kvm_entry
のフックにおいて,eBPF Mapのフラグを0にし,args->vcpi_id
をprintする.
例えば,ゲストで適当にVMCALL Instructionを実行すると以下のようなトレースが取れる.
$ sudo python kvm_hypercall.py TIME(s) COMM PID EVENT 15163.410112000 qemu-system-x86 9819 KVM_EXIT exit_reason : 18 15163.410130000 qemu-system-x86 9819 HYPERCALL nr : 114514 15163.410132000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0
今回はこのサンプルをVMExit ReasonがCPUIDによるものかどうかのチェックに改変し,さらに kvm_cpuid
をフックする.args
で取れる値については /sys/kernel/debug/tracing/events/kvm/kvm_xxx(イベント名)/format
(TracepointsかつKVMイベントの場合) 辺りを見るとわかる.
環境
ホスト
- OS : Ubuntu 18.04.1 LTS
- Linux Kernel : 4.15.0-39-generic
- CPU : Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
- Memory : 32GB
- QEMU-KVM : 2.11.1
- Python : 2.7.15rc1
- libbcc : 0.7.0-1
- bcc-tools : 0.7.0-1
ゲスト
コード
ホスト
ゲスト
CPUID Instructionのコードを適当に書いた. gist.github.com
結果
$ sudo python kvm_cpuid.py TIME(s) COMM PID EVENT 20422.473108000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473128000 qemu-system-x86 9819 KVM_CPUID rax : 0xd, rbx : 0x756e6547, rcx : 0x6c65746e 20422.473132000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473142000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473144000 qemu-system-x86 9819 KVM_CPUID rax : 0x663, rbx : 0x800, rcx : 0x80202021 20422.473146000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473149000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473151000 qemu-system-x86 9819 KVM_CPUID rax : 0x0, rbx : 0x0, rcx : 0x0 20422.473153000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473688000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473702000 qemu-system-x86 9819 KVM_CPUID rax : 0x1, rbx : 0x0, rcx : 0x4d 20422.473704000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473714000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473718000 qemu-system-x86 9819 KVM_CPUID rax : 0x1, rbx : 0x0, rcx : 0x4d 20422.473719000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473722000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473725000 qemu-system-x86 9819 KVM_CPUID rax : 0x121, rbx : 0x1c0003f, rcx : 0x3f 20422.473726000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473729000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473731000 qemu-system-x86 9819 KVM_CPUID rax : 0x122, rbx : 0x1c0003f, rcx : 0x3f 20422.473733000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473735000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473738000 qemu-system-x86 9819 KVM_CPUID rax : 0x143, rbx : 0x3c0003f, rcx : 0xfff 20422.473742000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473747000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473750000 qemu-system-x86 9819 KVM_CPUID rax : 0x163, rbx : 0x3c0003f, rcx : 0x3fff 20422.473751000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.473760000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.473763000 qemu-system-x86 9819 KVM_CPUID rax : 0xd, rbx : 0x756e6547, rcx : 0x6c65746e 20422.473764000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.979395000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.979412000 qemu-system-x86 9819 KVM_CPUID rax : 0x663, rbx : 0x800, rcx : 0x80202021 20422.979414000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0 20422.979453000 qemu-system-x86 9819 KVM_EXIT exit_reason : 0xa 20422.979460000 qemu-system-x86 9819 KVM_CPUID rax : 0x663, rbx : 0x800, rcx : 0x80202021 20422.979462000 qemu-system-x86 9819 KVM_ENTRY vcpu_id : 0x0
参考
DNS over Tor
はじめに
DNS over Torが持つメリットとリスクについて書きます。大体ポエムです。
読むのが面倒な方向けに先に結論(持論)を書いておくと、DNS over TorはDNS Leakに対しては有効ですが、名前解決自体は出口ノード依存です。つまり、出口ノードの参照先フルサービスリゾルバがDNSブロッキングを実施していたり、出口ノード自身がMaliciousな場合は本来とは異なる名前解決結果を得る可能性があります。現実でそのようなケースを引き当てることは多くはないですが(全出口ノードのおよそ40%はGoogle Public DNSを参照している)*1、リスクとして把握しておく必要はあります。
DNS over Tor
DNS over TorはTorネットワークを介して名前解決を得るための仕組みです。0.2.0.1-alpha以前*2のTorでは、Polipo(Privoxy)のようなサードパーティのプロキシを用いて実現していましたが、0.2.0.1-alpha以降はTor本体にリゾルバ(TorDNS)が実装されました。そのため、現在では下記のいずれかの方法でTorDNSを使用するのが一般的のようです。
dnsmasqのようなローカルキャッシュDNSサーバとTorDNSを併用する
TorDNS単体で使う
- torrcのDNSPortを53にしてTorを起動
また、FirefoxやChromeのような一般的なブラウザでDNS over Tor および、Tor経由の通信を実現するためには、ブラウザの設定でSOCKS5プロキシをTorのSocks Port(9050)に向け、SOCKS5のリモートDNS機能を有効にするだけです(この場合、DNS over SOCKS5 over Torというような構成になります。DNS over SOCKS5というと語弊がありますが、これについては後述します)。Tor Browser Bundleは既に初めからそのような設定・構成がされているので、DNS Leakを気にする必要はありません。しかし、名前解決が出口ノード依存という点に変わりはありません。ちなみに、tor-resolveコマンドではTor Browser Bundle同様SOCKSを用いた名前解決を行います。
なお、TorはUDPに対応していないため、TorDNS(DNS over Tor)であっても、SOCKSを用いた名前解決(DNS over SOCKS5 over Tor)であっても、実際のDNSのUDPパケットを投げているわけではありません。TCPを用いた擬似的な名前解決を行っています。
DNS over TorはA/AAAAレコードにのみ対応しており、TXTやMX、SOAといったレコードには対応していないようです。Tor 0.3.2.10の src/or/dnsserv.c
96行目付近は下記のようになっています。
[tor/src/or/dnsserv.c]
for (i = 0; i < req->nquestions; ++i) { if (req->questions[i]->dns_question_class != EVDNS_CLASS_INET) continue; switch (req->questions[i]->type) { case EVDNS_TYPE_A: case EVDNS_TYPE_AAAA: case EVDNS_TYPE_PTR: /* We always pick the first one of these questions, if there is one. */ if (! supported_q) supported_q = req->questions[i]; break; default: break; } }
TorDNSを使ってみる
ここではTorDNS単体で使う方法で設定を行い、実際にTorDNSを用いて名前解決が行われることを確認してみます。
$ sudo lsof -i:53 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME tor 3956 debian-tor 7u IPv4 39295 0t0 UDP localhost:domain
$ dig torproject.org | grep -e "torproject.org." -e " Query time" ;torproject.org. IN A torproject.org. 300 IN A 154.35.132.71 ;; Query time: 531 msec
名前解決時間(Query time)は531ミリ秒でした。当たり前ではありますが、Torを介しているため通常の名前解決より遅いです。この時のlocalhost:53の様子を確認してみます。 (GuardノードのIPアドレスが含まれる部分を *
で隠しています)
$ sudo tcpdump -i lo port 53 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes 00:28:58.356075 IP localhost.32932 > localhost.domain: 33784+ [1au] A? torproject.org. (43) 00:28:58.357530 IP localhost.59343 > localhost.domain: 32736+ PTR? *.*.*.*.in-addr.arpa. (45) 00:28:58.357684 IP localhost.domain > localhost.59343: 32736 1/0/0 PTR *.ip-*-*-*.eu. (115) 00:28:58.358021 IP localhost.47104 > localhost.domain: 19011+ PTR? 10.0.168.192.in-addr.arpa. (43) 00:28:58.358144 IP localhost.domain > localhost.47104: 19011 NXDomain 0/0/0 (43) 00:28:58.887745 IP localhost.domain > localhost.32932: 33784 1/0/0 A 154.35.132.71 (48)
次に、この名前解決要求を受けてTorが実際にGuradノードのORPort(443)に接続している様子を確認してみます。 (GuardノードのIPアドレスが含まれる部分を *
で隠しています)
$ sudo tcpdump -i wlp3s0 port 443 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on wlp3s0, link-type EN10MB (Ethernet), capture size 262144 bytes 00:28:58.356395 IP 192.168.0.10.38530 > *.*.*.*.https: Flags [P.], seq 1633253232:1633253775, ack 1193388446, win 1444, options [nop,nop,TS val 3583398 ecr 3185613847], length 543 00:28:58.641147 IP *.*.*.*.https > 192.168.0.10.38530: Flags [.], ack 543, win 1452, options [nop,nop,TS val 3185634151 ecr 3583398], length 0 00:28:58.887383 IP *.*.*.*.https > 192.168.0.10.38530: Flags [P.], seq 1:544, ack 543, win 1452, options [nop,nop,TS val 3185634398 ecr 3583398], length 543 00:28:58.887498 IP 192.168.0.10.38530 > *.*.*.*.https: Flags [.], ack 544, win 1440, options [nop,nop,TS val 3583531 ecr 3185634398], length 0
DNSブロッキングの検証
ここでは実際にDNSブロッキングを行う出口ノードを構築して検証します。とはいえ、実際のTorネットワークで検証するのはtor projectのポリシーに反するので*3、ここではprivateなTorネットワークを構築し、その中で検証します。
今回は合計7ノードからなるTorネットワークを構築しました。うち5ノードは出口ノードの役割を果たします。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 80f4bf39f34e antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 80/tcp, 9001/tcp, 9030/tcp, 9051/tcp private-tor-network_hs_1 9b8680f38efb antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 7000/tcp, 9001/tcp, 9030/tcp, 9051/tcp private-tor-network_exit_1 0b0b242adf15 antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 7000/tcp, 9001/tcp, 9030/tcp, 9051/tcp private-tor-network_relay_1 2bed3aa7b897 antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 9001/tcp, 9030/tcp, 0.0.0.0:9050-9051->9050-9051/tcp private-tor-network_client_1 ff78de4d068d nginx "nginx -g 'daemon ..." 2 hours ago Up 2 hours 80/tcp private-tor-network_web_1 8910c7f6a786 antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 7000/tcp, 9001/tcp, 9030/tcp, 9051/tcp private-tor-network_da2_1 01fd20bbf316 antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 7000/tcp, 9001/tcp, 9030/tcp, 9051/tcp private-tor-network_da1_1 5e9709373409 antitree/private-tor "docker-entrypoint..." 2 hours ago Up 2 hours 7000/tcp, 9001/tcp, 9030/tcp, 9051/tcp private-tor-network_da3_1
これらの全ての出口ノードはブロッキングを行うフルサービスリゾルバ(dockerコンテナとは別に構築したunbound)を参照します。ブロッキングの内容についてはtorproject.org
のAレコードに対して0.0.0.0
を返す仕様とします。
[unbound.conf]
server: verbosity: 1 interface: 0.0.0.0 access-control: 172.18.0.0/24 allow access-control: 192.168.122.0/24 allow access-control: 127.0.0.1/32 allow do-daemonize: no chroot: "" username: "" directory: "" logfile: "" pidfile: "unbound.pid" local-data: "torproject.org. IN A 0.0.0.0"
クライアントからtor-resolveコマンドを実行し、torproject.org
の名前解決を行うと0.0.0.0
が表示されることが確認できます。
$ sudo lsof -i:9050 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME docker-pr 29174 root 4u IPv6 382567 0t0 TCP *:9050 (LISTEN) $ tor-resolve torproject.org 0.0.0.0
実験用Torネットワークを構築する方法として、他にもネットワークシミュレータを使用する方法があり、例えばShadowがあります。Shadowは離散事象(discrete-event)ネットワークシミュレータの一種です。TorやBitocinといった実際のアプリケーションのシミュレーションに適しており、単一のマシン上で、実際のTorネットワークに近い構成で実験・検証を行うことができます。ただし、Shadowはアプリケーションごとにプラグインを作成する必要があり、Torに関してもshadow-plugin-torを導入する必要があります。Tor自体の開発においてネットワークの動作検証がしたい、という場合はこちらを使うと良いと思われます。
Bad Exit
不正なフルサービスリゾルバを参照している出口ノードや出口ノード自身がMaliciousな場合はBad Exitフラグが付加されるのでサーキットのパス選択時に弾かれるのではないか*4、と考えた方もいるかと思います。確かにその通りで、実際OpenDNSをフルサービスリゾルバにしている出口ノードがBad Exitとして弾かれるケースが多いようです*5。ただし、Bad Exitのラベル付加はDirectory Authorityのオペレータや彼らにボランティアとして協力する人々によって運営されているので、ネットワーク上で自動検出が行われるわけではありません。ちなみにBad Exitを自動検出するresearch projectとして、spoiled onions*6があります。このresearch projectで開発されたプロダクトであるexitmapについては以前記事を書きました。
リスクに対する対策
「名前解決に必ずTorを用いる」と前提した上でのリスクに対する対策を考えてみました。
torrcのExcludeNodesとExcludeExitNodesを適当に調整する。
torrcのExitNodesで信頼できる出口ノードを指定する。
- あまりやらない方が良さそう。
3つ目の方法については未検証ですが、redsocksのようなプロキシと組み合わせることで実現できるものと思われます。未検証なのでまた別の機会に検証します。
参考
*1:We also find that a set of exit relays, at times comprising 40% of Tor’s exit bandwidth, uses Google’s public DNS servers - https://nymity.ch/tor-dns
*2:0.2.0.1-alphaは2012年頃のリリースバージョンであり、0.2.0.xのTorは現在のTorネットワーク上では動作しない https://blog.torproject.org/end-life-tor-020x-branch
*3:Should I snoop on the plaintext traffic that exits through my Tor relay? - No - https://www.torproject.org/eff/tor-legal-faq.html.en
*4:Bad Exitは全Directory Authorityのうち、半分以上が承認する必要がある [2.2.1. Choosing an exit] - https://gitweb.torproject.org/torspec.git/tree/path-spec.txt?id=426e9ac1069ee843000aaeed9260ba4c9733af00#n184
*5:OpenDNSはユーザ保護の目的で異なるDNSレコードを返す場合があるらしい - [What specifically causes an Exit to get a Bad Exit flag?] https://tor.stackexchange.com/questions/253/what-specifically-causes-an-exit-to-get-a-bad-exit-flag/254?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
*6:spoiled onions - https://www.cs.kau.se/philwint/spoiled_onions
Torのサーキット生成ルール
はじめに
Torがサーキットを生成する際にどのようなルールで各ノードを選択するのか気になったので調べた.torspecの2.2項(Path selection and constraints)に書いてあった.
選択のルール
1.同じパス上で同じノードを2度選択することはない.
2.同じファミリの中で複数のノードを選択することはない.
3./16のサブネットの中で複数のノードを選択することはない.
4.有効でないノード(Validフラグを持たないノード)または、実行されていないノード(Runningフラグを持たないノード)を選択することはない.
5.Torの接続に使用する入口ノードはGuardノードでなければならない.
2について、各ノードは"family"というエントリを持っていて、例えば2つのTorノードがfamilyエントリにお互いのノードを持っている場合、それらのノードは同一のfamilyといえる.
4について、これは設定(torrc)によって変更が可能.デフォルトの設定では、non-validなノードに関してはrendezvousノードとmiddleノードの選択において許可されている.
5について、Guardノードとは長期間運用の実績と適切な帯域を持つノードのこと(Guardフラグを持つ).入口ノードはTor利用者のグローバルIPアドレスを直接見ることができるため、信頼性の高いノード(=Guardノード)が求められる.
Guard・Exitノードに関しては帯域幅の割合と利用者が選択している位置(torrcのEntryNodes項目およびExitNodes項目)に応じて重み付けがなされ、決定される.さらに、次のルールのうち1つ以上を考慮してサーキットを生成する場合がある.
1.全てのIntroductionサーキットとRendezvousサーキットの生成には安定したノード(Stableフラグを持つノード)が選択されるべきである.
2.長期間の接続が予想される場合には安定したノードが選択されるべきである.
3.DNS(DNS over Tor)を使用する場合には、torrcのExitPolicyが"reject *:*"でないExitノードが必要である.
4.DNSの逆引きにはeventdnsに対応したバージョンのTorが必要である(Tor 0.1.2.1-alpha-dev以降)
5.全ての接続には、接続先アドレスとポートをポリシーによってrejectしていないExitノードが必要である.
1について、IntroductionサーキットとRendezvousサーキットはHidden Serviceにアクセスする際に生成されるサーキットでRendezvousノードは利用者とHidden Serviceのサーバの中間地点にあたる.詳細に関してはこちら(Tor : Onion Service Protocol)を参照のこと.
2について、利用者が長期間接続するかどうかは接続時のリクエストポートを見ることによって判断していて、以下のポートの場合は安定ノードが選択されるようである.
21, 22, 706, 1863, 5050,5190, 5222, 5223, 6667, 6697, 8300
おわりに
他にもBad Exitが有効なExitノードは選択されないようになっている.Torの実際のソースコードを読んで確認したわけではないので今後時間があれば確認・検証していきたい所存.
参考
DLLインジェクションの検証
これはMCC Advent Calendar10日目の記事です.
前回はUm6ra1(@v1ru5) さんによる,LinuxのUSBドライバを書いてみよう ~RTL2832uでLチカ~でした.
はじめに
本記事では,DLLインジェクションを実際に実装・検証していきます.DLLインジェクションとは,Windowsにおいて,プロセスに意図しないDLL(Dynamic Link Library)を埋め込む攻撃手法です.この手法はUser Mode Rootkit等のマルウェアでよく用いられるテクニックでもあります.
環境
以下の環境で検証を行いました.実装はC++を使用しました.
OS Windows10 Pro x86-64 Visual Studio Community 2015 Version 14.025431.01 Update 3
手順
基本方針としては,DLLパスを攻撃対象プロセスの仮想アドレス空間のメモリ領域に書き込み,リモートスレッドを生成する形でDLLコードを実行します.具体的には以下の手順で攻撃対象へDLLインジェクションを行います.
1. プロセスへのアタッチ -> OpenProcess()
HANDLE OpenProcess( DWORD dwDesiredAccess, // アクセスフラグ BOOL bInheritHandle, // ハンドルの継承オプション DWORD dwProcessId // プロセス識別子 );
https://msdn.microsoft.com/ja-jp/library/cc429278.aspx
2. プロセスの仮想アドレス空間内のメモリ領域確保 -> VirtualAllocEx()
LPVOID VirtualAllocEx( HANDLE hProcess, // 割り当てたいメモリを保持するプロセス LPVOID lpAddress, // 割り当てたい開始アドレス DWORD dwSize, // 割り当てたい領域のバイト単位のサイズ DWORD flAllocationType, // 割り当てのタイプ DWORD flProtect // アクセス保護のタイプ );
https://msdn.microsoft.com/ja-jp/library/cc430225.aspx
3. 確保済みメモリ領域へDLLパスのコピー -> WriteProcessMemory()
BOOL WriteProcessMemory( HANDLE hProcess, // プロセスのハンドル LPVOID lpBaseAddress, // 書き込み開始アドレス LPVOID lpBuffer, // データバッファ DWORD nSize, // 書き込みたいバイト数 LPDWORD lpNumberOfBytesWritten // 実際に書き込まれたバイト数 );
https://msdn.microsoft.com/ja-jp/library/cc429067.aspx
4. リモートスレッドの生成(DLLコードの実行) -> CreateRemoteThread()
HANDLE CreateRemoteThread( HANDLE hProcess, // 新しいスレッドを稼働させるプロセスを識別するハンドル LPSECURITY_ATTRIBUTES lpThreadAttributes, // スレッドのセキュリティ属性へのポインタ DWORD dwStackSize, // 初期のスタックサイズ (バイト数) LPTHREAD_START_ROUTINE lpStartAddress, // スレッド関数へのポインタ LPVOID lpParameter, // 新しいスレッドの引数へのポインタ DWORD dwCreationFlags, // 作成フラグ LPDWORD lpThreadId // 取得したスレッド識別子へのポインタ );
https://msdn.microsoft.com/ja-jp/library/cc429075.aspx
CreateRemoteThread関数の実行において,lpStartAddressにLoadLibraryのアドレス,lpParameterにプロセスの仮想アドレス空間のメモリ領域に書き込まれたDLLパスのアドレスを指定するようにします.こうすることでリモートスレッドのエントリポイントがLoadLibraryによってロードされたDLLのDllMain関数となります.
HMODULE LoadLibrary( LPCTSTR lpFileName // モジュールのファイル名 );
https://msdn.microsoft.com/ja-jp/library/cc429241.aspx
今回は検証用DLL(dllexample.dll)をメモ帳(notepad.exe)にインジェクションします.検証用DLLのDllMain関数部分は以下の通りです. gist.github.com また,インジェクション実行のプログラム(injector.cpp)は以下の通りです. gist.github.com 以上のプログラムを実行すると以下のような結果になり,メモ帳に対してDLLインジェクションが成功したことが確認できました.
参考
- Open Security Research: Windows DLL Injection Basics
- 64ビット対応のDLLインジェクション - SIN@SAPPOROWORKSの覚書
- CreateRemoteThread関数によるDLLインジェクションをやってみる - ももいろテクノロジー
おわりに
DLLインジェクションの攻撃手法自体は以前から知っていましたが,手を動かして検証したことがなかったのでやってみました.
次回はsasamijpさんによる,街中謎解きゲームの話です.
今年は某先輩専用のAdvent Calendarが開催されなかったことが残念でなりません.
セキュリティ・ミニキャンプ in やまなし 2017 に参加してきた
9月24日に開催されたセキュリティ・ミニキャンプ in やまなし の専門講座にチューターとして参加してきた.ミニキャンプは23日に一般講座も開催していたがその時は専門講座の会場設営などを行っていた(実際は電車を乗り過ごして一番遅く着いたのでほとんど設営は終わっていた).今回は専門講座で行われた技術的な内容について書いていく.
1. 手作りパケットでWebサーバと通信しよう
Scapyでhttp通信を行うプログラムを書いた.事前課題ではコネクション確立,コネクション確立後の通信,コネクションの切断の際のシーケンス番号(seq),応答確認番号(ack)の変化について調べる課題とNetcat,PythonのSocketに関して基本的な操作方法を覚えてくる課題が出された.講義では最終的にコネクション確立,確立後のHTTPリクエスト,コネクション切断までの一連の流れを書き,tcpdumpやwiresharkを使用しながら実際にパケットの流れの観測と検証を行った.
(www.example.comというのは今回記事として書く上での適当なホスト名であり講義で実際に使用したわけではない)
TCPのシーケンス番号,応答確認番号については,個人的にはこの辺りの説明がわかりやすいと思う.TCPの機能は正直あまり理解していない部分が多いのでRFCを読もうと思う.ちなみに知り合いのとあるイケメンパケリストはRFCの文書->疑似言語->プログラミング言語みたいな感じで翻訳していけばTCPのプロトコルスタックが実装しやすいと言っていた気がする.
OSがRSTパケット(RST/ACK)を送信してしまう
これはカーネルのTCPスタックがSYNを認知していない状態でサーバからのSYN/ACKを受けるため.TCPスタックがはじめのSYNを認知できないのはScapyがRawSocket経由でパケットの送信を行っているためである.ということでScapyで3ウェイハンドシェイクを行うためにはiptablesでRSTをDROPする必要がある.
$ sudo iptables -I OUTPUT -p tcp --tcp-flags RST RST -j DROP
2. ローカルプロキシで遊ぼう
講師の先生が自作したローカルプロキシ(Nodejs製)を使って与えられた課題を解決するようにプロキシのプログラムを改変した.課題は以下の通り.
必須課題
1. レスポンスがHTMLだった場合,"Bad"という文字列を"Good"に置換して返却する. 2. Basic認証を常に突破できるようにする.(userid/passwordは知っている状態)
1はヘッダのContent-Type
行がtext/html
だった場合にボディ中の"Bad"を"Good"に書き換える.2は通常時のBasic認証のHTTPを観察し,Authorization
行を常にヘッダに追加するようにすればよい. これはわかっていてもJavaScriptがわからず沼にはまっている人が多く私もJavaScriptの経験が浅いので結構沼にはまった. JavaScriptやっていきたい.
ちなみにこれらは必須課題で任意課題(発展課題)もある.そちらの方はまだ検証中なので追々追記していく.ジャバスクリプト何もわからん
— e_no (@epcnt19) 2017年9月24日
感想
初めてチュータを経験したが参加者とは違った側面の面白さや学びがあった.セキュリティ・キャンプは全国大会・地方大会含め参加者として何度かお世話になったので今後は新たな参加者の方をサポートしていく立場に回れればと思う.(一般 | 専門)講座参加者・講師・チュータの皆様,開催にあたり支援して下さった皆様,お疲れ様でした.
exitmap
はじめに
TorがOnionRoutingという仕組みで秘匿性を保っていることは既に皆さんご存知かと思います.そしてOnionRoutingにおいて,Torネットワークからクリアネット(非Torネットワーク)への架け橋となる出口ノード(ExitNode)の存在は非常に重要で,エンドtoエンドでの暗号化通信を行っていない場合,ExitNodeにはクライアントの通信内容が筒抜けになります.Torネットワークの各ノード(EntryNode,BridgeNode,RelayNode,ExitNode)は誰でもなることができるため,悪意をもって盗聴や改ざんを行うExitNodeの存在が非常に脅威となります.そこで,今回は悪意のあるExitNodeや,そういった疑いのあるExitNodeを探索するツールであるexitmapについて紹介します.
exitmap
exitmapはスウェーデンのカールスタード大学 PriSecグループとオーストリアのSBA Researchの共同研究によって2014年に開発されたもののようです. Pythonベースのプログラムになっており,内部的にStemを使用しているため,Tor本体をインストールしておく必要があります.
https://www.cs.kau.se/philwint/spoiled_onions/ github.com
イメージとしては,exitmap内でモジュール(例えば,ファイルのダウンロード・Webサーバへの接続等を行うモジュール)が評価対象のExitNodeを通じて実行され,その実行結果からNegative/Positiveを判定する感じです.モジュールは各自で開発することもできます.
使ってみる
環境
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.10 DISTRIB_CODENAME=yakkety DISTRIB_DESCRIPTION="Ubuntu 16.10"
$ uname -mr 4.8.0-52-generic x86_64
インストール
$ git clone https://github.com/NullHypothesis/exitmap $ cd exitmap $ pip install -r requirements.txt $ cd ..
使い方
$ ./exitmap/bin/exitmap (モジュール名)
のように指定します.デフォルトで入っているモジュールには以下のようなものがあります.
checktest
- https://check.torproject.org/に接続し,Torと判定されるかチェックする.(ExitNodeがプロキシを刺しているかチェックする)
testfds
- 単純なWebページの内容を取得できるかチェックする.(ExitNodeのファイルディスクリプタが正しく機能しているかチェックする)
dnspoison
- 名前解決を行い,受信したAレコードと予想されるAレコードの比較を行う.(ExitNodeがDNSスプーフィングを行っているかチェックする)
dnssec
- ExitNodeがDNSSECを検証するかどうかチェックする.
patchingCheck
- ファイルのダウンロードを行い,改ざんをチェックする.
cloudflared
- https://www.cloudflare.com/に接続し,CAPTCHAが受信できるかチェックする.
rtt
- RTTをチェックする.
正直なところ,各モジュールの役割についてはソースコードを読んだ方が早いです.モジュールは./exitmap/src/modules/
以下に配置されています.
また,exitmap本体にもいくつかのオプションがあります.詳細はhelpオプションで確認できるので,ここではいくつかの重要なオプションについて紹介します.
--country [Country Code]
- ExitNodeの国を指定するオプションです.日本であれば
JP
のように指定します.
- ExitNodeの国を指定するオプションです.日本であれば
--first-hop [FingerPrint]
- EntryNode(first hop)を20バイトのフィンガープリントで指定します.
--exit [FingerPrint]
- ExitNodeを20バイトのフィンガープリントで指定します.
--build-delay [Seconds]
- Torのサーキットを生成する際に任意の秒数間休止します.
以下の例は全ての出口ノードに対してdnspoisonモジュールを実行した例です.今回はDNSスプーフィングの疑いのあるExitNodeは発見できませんでした.
https://gist.github.com/epcnt19/84d753545ac715a569e31cc44fed883f#file-exitmap
おわりに
悪意のあるExitNodeを発見するためには,exitmapによる継続的なスキャンや,モジュールの拡張・自作を行う必要がありそうです.ただし,継続的なスキャンという点に関してはスキャンがTorネットワークへ負荷を与えているという点に注意する必要があります.
独り言
全出口ノード数は800〜900程度だそうですが,案外少なくないですか(調べてみたら,ここ数年で結構減少してるっぽい)
https://metrics.torproject.org/relayflags.html
Torの全出口ノード数800って意外に少ないな
— e_no (@epcnt19) 2017年6月3日
全体の10%程度であれば月10〜20万くらいで運用可能では
DECAFのセットアップ
DECAFの環境を構築したので,再度構築する時のための自分用メモ.
github.com
環境
- ホストOS Ubuntu16.10(LTS) 64bit
- ゲストOS① Ubuntu14.04(LTS) 64bit
- ゲストOS② Windows XP 32bit
ホストOSの上でゲストOS①を動作させ(Virtualbox),その上でゲストOS②を動作させる(DECAF(QEMU)). ホストOS上で直接DECAFを動作させなかったのは、Ubuntu16.10上でのビルドが上手く行かなかったためで,現在原因調査中.
パッケージのインストール
$ sudo apt-get update $ sudo apt-get install qemu binutils-dev libboost-all-dev $ sudo apt-get build-dep qemu $ sudo apt-get -y install build-essential linux-headers-$(uname -r) $ sudo apt-get install zlib1g-dev libssl-dev libncurses5-dev $ sudo apt-get install libcurl4-openssl-dev libexpat1-dev libreadline-gplv2-dev $ sudo apt-get install uuid-dev libfuse-dev bzip2 libbz2-dev git $ sudo apt-get -y install automake1.9 autoconf libtool $ git clone https://github.com/sycurelab/DECAF $ cd DECAF/decaf $ ./configure $ make $ cd ..
イメージの作成
- WindowsXP(modernIE)のVMware用イメージをダウンロードした.
- VMWare用イメージ(vmdk)をqcow2イメージに変換したものでDECAFを起動しようとしたところ,
No bootable device
と出てしまい,ブートできなかった.原因調査中. - qcow2イメージではなく,rawイメージに変換したものを使用したところ,問題なく起動できた.
DECAFでqcow2(vmdk->qcow2のconvert)だとNo bootable deviceになるけどrawを直接指定したらとりあえず動いた
— e_no (@epcnt19) 2017年4月30日
$ unzip IE8.XP.For.Windows.VMware.zip $ qemu-img convert -f vmdk -O raw IE8_-_WinXP-disk1.vmdk winxp.img $ ./decaf/i386-softmmu/qemu-system-i386 -monitor stdio -drive file=./winxp.img
さいごに
今後は以下を参考にプラグイン開発をやっていく予定.
追記
https://groups.google.com/forum/#!topic/decaf-platform-discuss/iLw8E7p559w
参考
- poppycompass.hatenablog.jp
- tsunpoko.hatenablog.com
- GitHub - ntddk/geteip: tiny DECAF plugin
- GitHub - ntddk/blue: Some anti QEMU trick used by in-the-wild malware.
- DECAFによるマルウェア自動解析 | 一生あとで読んでろ
- セキュリティキャンプ講義「仮想化技術を用いたマルウェア解析」にチャレンジしてみた(プラグイン開発編) - 拾い物のコンパス
- ミニキャンプ沖縄・仮想化技術を用いたマルウェア解析-Revenge(プラグイン開発編) - /var/log/tsun
記念撮影