断片

です・ます調が記事によって違ったりするブログ

eBPFでゲストのCPUIDをトレースする

これはMCC Advent Caleandar 20日目の記事です.

adventar.org

前日はUm6ra1さんによるMediBang Paintのmdpファイルを解析してみたでした.

はじめに

eBPFのチュートリアルとして,KVMゲストによるCPUID Instructionの実行をホスト側でeBPF(とTracepoints)を使ってトレースしてみる.ここでは,eBPFはBCCを使って書く.BCCサンプルソースには kvm_hypercall.py というのがあり,このサンプルの動きは以下のようになっている.

  • kvm_exitkvm_entrykvm_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

github.com

今回はこのサンプルを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

ゲスト

  • OS : Ubuntu 16.04.5 LTS
  • Linux Kernel : 4.4.0-131-generic

コード

ホスト

gist.github.com

ゲスト

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を併用する

    • torrcのDNSPortを9053にしてTorを起動
    • ローカルキャッシュDNSサーバの上流DNSをTorDNS(localhost:9053)に向ける
  • TorDNS単体で使う

    • torrcのDNSPortを53にしてTorを起動

また、FirefoxChromeのような一般的なブラウザで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)であっても、実際のDNSUDPパケットを投げているわけではありません。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ネットワークを構築し、その中で検証します。

github.com

今回は合計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自体の開発においてネットワークの動作検証がしたい、という場合はこちらを使うと良いと思われます。

shadow.github.io

github.com

Bad Exit

不正なフルサービスリゾルバを参照している出口ノードや出口ノード自身がMaliciousな場合はBad Exitフラグが付加されるのでサーキットのパス選択時に弾かれるのではないか*4、と考えた方もいるかと思います。確かにその通りで、実際OpenDNSをフルサービスリゾルバにしている出口ノードがBad Exitとして弾かれるケースが多いようです*5。ただし、Bad Exitのラベル付加はDirectory Authorityのオペレータや彼らにボランティアとして協力する人々によって運営されているので、ネットワーク上で自動検出が行われるわけではありません。ちなみにBad Exitを自動検出するresearch projectとして、spoiled onions*6があります。このresearch projectで開発されたプロダクトであるexitmapについては以前記事を書きました。

epcnt19.hatenablog.com

リスクに対する対策

「名前解決に必ずTorを用いる」と前提した上でのリスクに対する対策を考えてみました。

  • torrcのExcludeNodesとExcludeExitNodesを適当に調整する。

  • torrcのExitNodesで信頼できる出口ノードを指定する。

    • あまりやらない方が良さそう。
  • DNS over TLSを使う

    • 出口ノードに名前解決を依存させないかつ、UDPでない(=TCP)な方法がDNS over TLS over SOCKS5 over Torとなる。TLSにするのは同時にMaliciousな出口ノードによるDNSレコード改竄と盗聴を防げるため。TCPに対応したローカルキャッシュサーバとSOCKS5にリダイレクトするProxyを手元に設置すれば良さそう(未検証)

3つ目の方法については未検証ですが、redsocksのようなプロキシと組み合わせることで実現できるものと思われます。未検証なのでまた別の機会に検証します。

github.com

参考

tor.stackexchange.com

tor.stackexchange.com

*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の実際のソースコードを読んで確認したわけではないので今後時間があれば確認・検証していきたい所存.

参考

tor.stackexchange.com

tor.stackexchange.com

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インジェクションが成功したことが確認できました. f:id:epcnt19:20171209221922p:plain

参考

おわりに

DLLインジェクションの攻撃手法自体は以前から知っていましたが,手を動かして検証したことがなかったのでやってみました.

次回はsasamijpさんによる,街中謎解きゲームの話です.
今年は某先輩専用のAdvent Calendarが開催されなかったことが残念でなりません.

セキュリティ・ミニキャンプ in やまなし 2017 に参加してきた

9月24日に開催されたセキュリティ・ミニキャンプ in やまなし の専門講座にチューターとして参加してきた.ミニキャンプは23日に一般講座も開催していたがその時は専門講座の会場設営などを行っていた(実際は電車を乗り過ごして一番遅く着いたのでほとんど設営は終わっていた).今回は専門講座で行われた技術的な内容について書いていく.

1. 手作りパケットでWebサーバと通信しよう

Scapyでhttp通信を行うプログラムを書いた.事前課題ではコネクション確立,コネクション確立後の通信,コネクションの切断の際のシーケンス番号(seq),応答確認番号(ack)の変化について調べる課題とNetcat,PythonのSocketに関して基本的な操作方法を覚えてくる課題が出された.講義では最終的にコネクション確立,確立後のHTTPリクエスト,コネクション切断までの一連の流れを書き,tcpdumpwiresharkを使用しながら実際にパケットの流れの観測と検証を行った.

gist.github.com

(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やっていきたい.

ちなみにこれらは必須課題で任意課題(発展課題)もある.そちらの方はまだ検証中なので追々追記していく.

感想

初めてチュータを経験したが参加者とは違った側面の面白さや学びがあった.セキュリティ・キャンプは全国大会・地方大会含め参加者として何度かお世話になったので今後は新たな参加者の方をサポートしていく立場に回れればと思う.(一般 | 専門)講座参加者・講師・チュータの皆様,開催にあたり支援して下さった皆様,お疲れ様でした.

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
  • rtt
    • RTTをチェックする.

正直なところ,各モジュールの役割についてはソースコードを読んだ方が早いです.モジュールは./exitmap/src/modules/以下に配置されています. また,exitmap本体にもいくつかのオプションがあります.詳細はhelpオプションで確認できるので,ここではいくつかの重要なオプションについて紹介します.

  • --country [Country Code]
    • ExitNodeの国を指定するオプションです.日本であればJPのように指定します.
  • --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

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イメージに変換したものを使用したところ,問題なく起動できた.

$ 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

参考

記念撮影

f:id:epcnt19:20170501140856p:plain