
LLVMベースの計装ツールであり、汎用テイントトラッキング、データフロー解析、トレーシングを実現します。
PolyTracker は、もともと Automated Lexical Annotation and Navigation of Parsers のために作られたツールであり、The ALAN Parsers Project と呼ぶためだけに考案されたバックロニムです。しかし、現在ではプログラムのデータフロー解析と制御フロー解析を効率的に実行するための汎用ツールへと進化しました。PolyTracker は LLVM パスであり、入力ファイルのどのバイトがどの関数によって操作されるかを追跡するようにプログラムを計測します。データフロー情報を含むデータベースと、ランタイムトレースを出力します。また、PolyTracker は、その出力を操作・分析するための Python ライブラリと、対話型の Python REPL も提供します。
PolyTracker は PolyFile と組み合わせて使用することで、パーサー内の関数の意味論的な目的を自動的に特定できます。また、パーサーが受理する言語を表す文脈自由文法を生成できる実験的機能もあります。
動的計測の代替手段である Taintgrind などとは異なり、PolyTracker はほぼすべての入力に対して無視できるほどのパフォーマンスオーバーヘッドしか課さず、入力のすべてのバイトを一度に追跡できます。PolyTracker は LLVM DataFlowSanitizer のフォークとして始まり、Angora Fuzzer から多くの着想を得ています。ただし、Angora システムとは異なり、PolyTracker はテイントの 来歴(プロビナンス) 全体を追跡できます。2021年2月、LLVM DataFlowSanitizer に origin tracking と呼ばれるテイントの来歴を追跡する新機能が追加されました。しかし、同時に追跡できるのは最大16テイントまでであり、PolyTracker は最大 2-1 まで追跡できます。
この README は、PolyTracker のインストールとバイナリのコンパイル/計測に関する一般的な使用ガイドです。Python API を介して PolyTracker をプログラム的に操作・拡張する方法や、計測されたコードから生成されたランタイムトレースを操作する方法については、Python ドキュメントを参照してください。
PolyTracker は polytracker という Python スクリプトを介して制御されます。次のコマンドでインストールできます。
pip3 install polytracker
PolyTracker の実行には非常に特殊なシステム環境が必要なため、ほぼすべてのユーザーはコンテナ環境で実行することになるでしょう。幸い、polytracker を使えば簡単です。docker をインストールして、次のコマンドを実行するだけです。
polytracker docker pull
および
polytracker docker run
後者のコマンドは、現在の作業ディレクトリを PolyTracker Docker コンテナにマウントし、計測されたプログラムのビルドと実行を可能にします。
polytracker 制御スクリプトは、ホストシステムまたは Docker コンテナ内のどちらからでも実行でき、プログラムの計測と生成された成果物の分析の両方のためのさまざまなコマンドを備えています。たとえば、実行中のデータフローを調査したり、計測されたプログラムの制御フローグラフを再構築したり、プログラムが受理する入力に一致する文脈自由文法を抽出したりすることもできます。これらのコマンドは、次のコマンドを実行して確認できます。
polytracker --help
polytracker スクリプトは、コマンドライン引数を指定せずに実行すると REPL としても機能します。
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> commands
PolyTracker には build コマンドも用意されています。このコマンドを使用すると、Blight で計測された環境で任意のビルドコマンドを実行できます。これにより、ビルド中に実行されたすべてのコマンドを記録した blight_journal.jsonl ファイルが生成されます。C/C++ ターゲットがある場合は、polytracker build を呼び出してビルドコマンドを渡すことで計測できます。
polytracker build gcc -g -o my_binary my_source.c
ビルドターゲットを計測するには、instrument-targets コマンドを使用します。デフォルトでは、このコマンドは現在の作業ディレクトリにある blight_journal.jsonl を使用して、ビルドターゲットの計測版をビルドします。計測されたビルドターゲットは、元のビルドターゲットと同じフラグを使用してビルドされます。
polytracker instrument-targets my_binary
build は、autotools や CMake などのビルドシステムを使用する、より複雑なプログラムもサポートしています。
polytracker build cmake .. -DCMAKE_BUILD_TYPE=Release
polytracker build ninja
# or
polytracker build ./configure
polytracker build make
次に、ビルドの任意のターゲットに対して instrument-targets を実行します。
polytracker instrument-targets a.bin b.so
すると、a.instrumented.bin と b.instrumented.so が計測されたバージョンになります。実際のプログラムを計測する方法の例については、examples ディレクトリ内の Dockerfile を参照してください。
計測されたソフトウェアは、その出力を POLYDB で指定されたパスに書き込みます。省略した場合は polytracker.tdag に書き込みます。これはバイナリファイルであり、次のように実行して操作できます。
from polytracker import PolyTrackerTrace, taint_dag
trace = PolyTrackerTrace.load("polytracker.tdag")
tdfile = trace.tdfile
first_node = list(tdfile.nodes)[0]
print(f"First node affects control flow: {first_node.affects_control_flow}")
# Operate on all Range nodes
for index, node in enumerate(tdfile.nodes):
if isinstance(node, taint_dag.TDRangeNode):
print(f"Node {index}: first {node.first}, last {node.last}")
# Access taint forest
tdforest = trace.taint_forest
n1 = tdforest.get_node(1)
print(
f"Forest node {n1.label}. Parent labels: {n1.parent_labels}, "
f"source: {n1.source.path if n1.source is not None else None}, "
f"affects control flow: {n1.affected_control_flow}"
)
計測されたバイナリを REPL から直接実行することもできます。
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> trace = run_trace("path_to_binary", "path_to_input_file")
これにより、必要に応じて計測されたバイナリが Docker コンテナ内で自動的に実行されます。
⚠️ Docker または VM 内で PolyTracker を実行する場合: PolyTracker は、 仮想化環境で実行していて、入力ファイル、特に出力データベースがホスト OS から マップまたはマウントされたディレクトリにある場合、非常に遅くなることがあります。 これは、macOS ホスト上の Docker で PolyTracker を実行する場合に特に当てはまります。 解決策は、データベースをコンテナ/VM 内のパスに書き込み、最後にホストシステムにコピーすることです。
Python API ドキュメントは こちら で入手できます。
実行時に、PolyTracker の計測は環境変数を介して指定された多数の設定パラメータを探します。これにより、バイナリを再コンパイルせずに計測パラメータを変更できます。
PolyTracker は、ターゲットプログラムを再コンパイルしないように、環境変数の形式で設定パラメータを受け入れます。PolyTracker が現在サポートしている環境変数は次のとおりです。
POLYDB: A path to which to save the output database (default is polytracker.tdag)
WLLVM_ARTIFACT_STORE: Provides a path to an existing directory to store artifact/manifest for all build targets
POLYTRACKER_TAINT_ARGV: Set to '1' to use argv as a taint source.
POLYTRACKER_STDIN_SOURCE: Set to '1' to use stdin as a taint source.
POLYTRACKER_STDOUT_SINK: Set to '1' to use stdout as a taint sink.
POLYTRACKER_STDERR_SINK: Set to '1' to use stderr as a taint sink.
Polytracker は設定パラメータを次の順序で設定します。
DFSan は ABI リストを使用して、自動的に計測する関数、無視する関数、および存在するカスタム関数ラッパーを決定します。詳細については、dfsan ドキュメント を参照してください。
大規模なソフトウェアプロジェクト、特に古い/サポートされていないプロジェクトをビルドしようとすると、時間がかかることがあります。ビルドシステムを変更して、dfsan や私たちの計測のような変更をサポートしようとすることは、さらに時間がかかります。
polytracker/scripts にスクリプトがあり、任意の ELF ライブラリに対して実行すると、無視する関数のリストを出力します。これは、libpng のような特定のライブラリやプログラムの他のサブコンポーネントを通過する情報を追跡したくない場合に使用します。Dockerfile-listgen.demo は、一般的なオープンソースライブラリをビルドしてこれらのリストを作成できるようにするために存在します。
このスクリプトは、DataFlowSanitizer が持つスクリプトを少し調整したバージョンで、システムライブラリを無視することに焦点を当てています。元のスクリプトは dfsan_rt にあります。
この Git リポジトリをチェックアウトしてください。ルートから、ベースの PolyTracker Docker イメージをビルドするか:
pip3 install -e ".[dev]" && polytracker docker rebuild
または、DockerHub から最新のプリビルド版をプルします:
docker pull trailofbits/polytracker:latest
MuPDF パーサーで PolyTracker を実行するデモについては、次のコマンドを実行してください:
docker build -t trailofbits/polytracker-demo-mupdf -f examples/pdf/Dockerfile-mupdf.demo .
mutool_track は /polytracker/the_klondike/mupdf/build/debug にビルドされます。mutool_track を実行すると、テイント分析によって提供される情報を含む polytracker.tdag が出力されます。
Poppler utils バージョン 0.84.0 で PolyTracker を実行するデモについては、次のコマンドを実行してください:
docker build -t trailofbits/polytracker-demo-poppler -f examples/pdf/Dockerfile-poppler.demo .
すべての poppler utils は /polytracker/the_klondike/poppler-0.84.0/build/utils に配置されます。
cd /polytracker/the_klondike/poppler-0.84.0/build/utils
./pdfinfo_track some_pdf.pdf
PolyTracker コードベースの拡張や TDAG トレースの分析にもう少し深く取り組みたいが、ローカル環境を弄って大幅にカスタマイズされた LLVM バージョンをインストールしたくないとしましょう。
Ubuntu で作業していて、比較的クリーンな 22.04 または 24.04 ベースから始める場合、リンクされた Gist に、動作するパススルーバージョンの PolyTracker ベースコンテナを取得する手順が詳しく説明されています。ベースコンテナは、すべての依存関係を備えた開発環境を提供し、その中で直接作業したり、拡張したりできます(サンプルの Dockerfile で行ったように)。
Python と C++ の両方の単体テストは、PolyTracker Docker コンテナ内で実行する必要があります。
unittests/ 内の Catch2 単体テストは、コンテナ内の /polytracker-build/unittests/src/taintdag/ にあります。Docker コンテナ内でテストバイナリを実行します:
cd /polytracker-build/unittests/src/taintdag/ && ./tests-taintdag
tests/ 内の Python 単体テストは、テストフィクスチャが計測するローカルのテスト用 C++ プログラムを必要とします。Pytest を使用して、作業ディレクトリで実行してください:
pytest tests
または、pytest を使用して単一のテストファイルを実行するには:
pytest tests/test_foo.py
PolyTracker は現在 Linux でのみ動作します。これは、DataFlowSanitizer がサポートしている唯一のシステムが Linux だからです。この制限は、他の OS のシステムコールのセマンティクスのサポートが不足しているためであり、将来追加される可能性があります。ただし、これは非 Linux システムで PolyTracker を実行するには Docker のインストールが必要になることを意味します。
テイントは、動的に読み込まれるライブラリを通過して伝播しません。ただし、それらのライブラリが PolyTracker を使用してソースからコンパイルされている場合、または PolyTracker に実装されているライブラリ呼び出しの特定のサポートがある場合は除きます。現在、計測されていない C 標準ライブラリ呼び出しの大部分を介したテイントの伝播をサポートしています。明確に言うと、計測されていない関数を使用するプログラムは通常どおり動作しますが、サポートされていないライブラリ呼び出しによって実行される操作はテイントを伝播しません。現在、C++ プログラムの堅牢なサポートの追加に取り組んでいますが、現時点では C プログラムで最良の結果が得られます。
Docker で問題が発生した場合は、システムの prune を実行し、PolyTracker と実行しようとしているデモの両方で --no-cache を使用してビルドしてみてください。
PolyTracker の最悪のパフォーマンスは、メモリ内の単一バイトがソースファイルからの多数の入力バイトによって同時にテイントされた場合に発生します。これは、ブロックサイズが大きい圧縮アルゴリズムや暗号化アルゴリズムを計測する場合に最も一般的です。この動作に対するいくつかの緩和策が現在研究・開発されています。
以下は、PolyTracker を使って行った公開されている作業の一部です。ここに掲載してほしい他のものをご存知の場合は、お知らせください!
mapping と cavities)トレース分析機能を使用して CVE を特定し、Trail of Bits ブログに記事を書きました。この研究は、Trail of Bits によって、Galois の下請けとして Defense Advanced Research Projects Agency (DARPA) の SafeDocs プログラムの資金提供を受けて開発されました。Apache 2.0 ライセンス の下でライセンスされています。© 2019, Trail of Bits.
[email protected] を使用してお問い合わせください。