Skip to content

Repository files navigation

monowire

monowire is a tool for finding TLS keys in process memory on Linux.

Demo using monowire

Rationale

There are several different methods out there for decrypting TLS traffic, including (non-exhaustively)

  • using the SSLKEYLOGFILE environment variable;
  • using a proxy like mitmproxy;
  • and using a uprobe-based tool like eCapture.

These are all respectable techniques that can fail in more adversarial scenarios, e.g. against obfuscated binaries.

monowire is an attempt to fix these shortcomings by identifying TLS secrets for traffic decryption against a larger set of binaries. The tradeoff is that it's a little less reliable in cases where one of the aforementioned approaches works. It also requires significantly more computational overhead than these other approaches.

Architecture

monowire is a Linux tool for TLS decryption that works by using brute-force search to find traffic secrets in a process's memory. It consists of a userspace component and a kernelspace component.

Userspace component

The userspace component (monowire/) drives most of the action. This includes writing captured packets to disk, dumping process memory, searching for TLS secrets, and decrypting packets. It also does the handling for all monowire subcommands.

Kernelspace component

The kernelspace component consists of a set of eBPF programs (monowire-ebpf/), and an (optional) kernel module (monowire-module/). The eBPF programs are used to send data back to the userspace component, including captured packets and TLS connection information.

The kernel module is an optional but recommended component that allows monowire to create on-demand process forks. This is a relatively cheap method for creating memory snapshots that can be searched for key material. When the kernel module is unavailable, the fallback mechanism for creating memory snapshots involves moving target processes into a frozen cgroup, which is prone to latency issues (and may interfere with the operation of the original process).

Data flow

The loose process for finding TLS secrets works as follows:

  1. On startup, monowire attach loads several eBPF programs for capturing packets and monitoring TLS connections.
  2. When a new TLS connection is established, an eBPF program monitors the connection for the first packet of encrypted application data. It sends an event back to the userspace component when this first packet is encountered.
  3. When the userspace component sees this event, it makes a best-effort attempt to create an instantaneous snapshot of process memory at that point in time.
    • If /dev/monowire is available (indicating that the monowire kernel module has been loaded), monowire will use it to fork the target process so that we can search the fork's memory for key material.
    • Otherwise, monowire will asynchronously move the target process into a frozen cgroup while immediately starting its memory dump (with the hopes of grabbing key material before it's wiped from memory.
  4. monowire then searches the dumped memory for the client and server traffic secrets.
  5. If found, these secrets will be written as a DSB record to the .pcapng file containing captured packets from the process. This can then be used to decrypt the PCAP using monowire decrypt.

Monowire operational flow

Usage

# Capture all TLS connections established by curl
monowire attach -c curl -O output

# Read pcap generaged by monowire and decrypt its contents
monowire decrypt ./output/$pid/$timestamp.pcapng

Building monowire

Setup

# Install nightly toolchain and rust-src
rustup toolchain install nightly
rustup component add rust-src --toolchain nightly

# Install bpf-linker
cargo +nightly install bpf-linker

Build

cargo build

# Build kernel module (optional)
cd monowire-module && make

This compiles the eBPF programs (via aya-build in monowire/build.rs) and the userspace binary in a single cargo build invocation.

Static build (musl)

To produce a fully static, portable binary using musl libc:

# Via Docker (no local musl toolchain needed)
bash scripts/build-static.sh

# Or locally (requires musl-tools installed)
bash scripts/build-static.sh --local

The Docker mode outputs the binary to target/static/monowire. The local mode outputs to target/x86_64-unknown-linux-musl/release/monowire.

Benchmarking

sudo sysctl -w kernel.perf_event_paranoid=-1
FLAGS="-g --call-graph dwarf"

# Memory scanning benchmarks
# * use "fast" for the fast keyscan path
# * use "full" for the full keyscan path
variant="fast"
perf record $FLAGS \
    cargo bench --bench keyscan -- --profile-time 10 $variant

# perf archive / perf archive --unpack can be used to export perf
# results and read them on another system

Licensing

All components of this project with the exception of ./monowire-module (the Linux kernel module companion) is licensed under MIT. The ./monowire-module code is licensed under GPLv3.

About

Transparent TLS decryption tool

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages