sniff!/Documentation
Dashboard
v1.0Last updated: March 2025

sniff! Documentation

Complete reference for setting up, using, and extending sniff! — the Android HTTPS traffic interception toolkit by x-lock.

Overview

sniff! automates the full Android HTTPS interception pipeline — ADB device management, mitmproxy setup, and Frida-based SSL pinning bypass — from a single interface. Select an app, pick a bypass script, and start capturing decrypted traffic in seconds.

It provides both a terminal UI (TUI) built on BubbleTea and a modern web dashboard built on Next.js. The Go backend orchestrates all subprocesses and streams captured flows in real-time via Server-Sent Events.

Traffic Capture
SSL Bypass
Frida Scripts
Device Mgmt
HAR Export
App Modes

Prerequisites

RequirementVersionPurpose
Go1.21+Build the backend
Bun1.0+Build the frontend
ADBAnyAndroid device communication
Frida16+Runtime instrumentation
frida-serverMatch Frida versionRuns on device (arm64)
mitmproxy10+HTTPS proxy (mitmdump)
Rooted AndroidAndroid 7+Target device
mitmproxy CAInstalled as system CADevice trusts proxy

Device Setup

Your Android device needs to be rooted (Magisk recommended) with USB debugging enabled. The mitmproxy CA certificate must be installed as a system certificate, and frida-server must be pushed to the device.

bash
# Push frida-server to device
adb push frida-server-16.x.x-android-arm64 /data/local/tmp/fs-helper-64
adb shell "chmod 755 /data/local/tmp/fs-helper-64"

# Install mitmproxy CA as system cert (requires root)
adb push ~/.mitmproxy/mitmproxy-ca-cert.cer /sdcard/
# Then: Settings > Security > Install from storage

Installation

Development

bash
git clone https://github.com/x-lock/sniff.git
cd sniff

# Install frontend dependencies
cd site && bun install && cd ..

# Start both backend (:9090) and frontend (:3000)
./dev

The dev script starts the Go backend on port 9090 and the Next.js dev server on port 3000, then opens the browser automatically.

Production Build

bash
# Build frontend + Go binary
./build-sniff!

# Run in web mode
./sniff! --web

# Run in TUI mode
./sniff!

# Run with a preset target package
./sniff! com.example.app

Usage

Web UI

Launch with ./sniff! --web or ./dev for development. The dashboard is organized into these pages:

PagePathDescription
Overview/dashboardStats cards and quick action menu
Capture/dashboard/captureLive flow table, filters, detail panel, terminal
App Modes/dashboard/modesApp-specific capture presets
HAR Inspector/dashboard/harLoad, search, and analyze HAR files
Terminal/dashboard/terminalFull-page Frida output and backend logs
Device/dashboard/deviceADB connection, Frida, proxy status
Apps/dashboard/appsBrowse and select target app
Scripts/dashboard/scriptsManage Frida bypass scripts
Settings/dashboard/settingsConfigure all settings

TUI Mode

Launch with ./sniff! for a full terminal interface.

KeyAction
cStart standard capture
mStart MITM-only capture (no Frida)
nApp-specific capture modes
sSettings
fFrida script selection
dDevice info & management
aBrowse installed apps
lFull log viewer
eExport captured flows
qQuit

During capture:

KeyAction
t / TCycle resource type filter
fToggle text filter
xClear flows
eExport to file
rRestart target activity
TabToggle focus between flows and logs
EnterView flow detail
EscBack

Capture Workflow

When you start a standard capture, sniff! executes this sequence automatically:

1. SELinux → Permissive
2. Force stop target app + kill stale processes
3. Start frida-server on device
4. Start mitmdump on configured port
5. Set device system proxy
6. Verify device ↔ proxy connectivity
7. Launch app, wait for PID
8. Wait for ART JIT warmup (configurable delay)
9. Attach Frida with SSL bypass script
10. Restart activity → capture flows in real-time

On stop: proxy is cleared, Frida detaches, SELinux is restored to Enforcing.

Capture Modes

Beyond standard capture, sniff! includes specialized modes that encode app-specific bypass knowledge.

ModeWhen to Use
StandardGeneral apps. Full pipeline: Frida + mitmproxy + system proxy.
MITM OnlyApps where CA is already trusted. Skips Frida entirely.
Signup HandoffApps with external browser auth (PingOne/DaVinci). Lets signup flow run direct, enables proxy after callback.
LinkedIn CronetLinkedIn app. Spawns with Cronet patch to disable QUIC and DNS bypass.
DailyPayDailyPay app. Standard bypass + APEX cert injection into isolated mount namespace.
SpeedwaySpeedway/7-Eleven. Extended ART settle time + Distil/Imperva bot protection stub.
Papa JohnsPapa Johns (Flutter). Uses iptables REDIRECT instead of system proxy + BoringSSL native hooks.

Frida Scripts

Built-in Scripts

IDNameLabelTarget
universalUniversal SSL UnpinBESTTrustManager, OkHttp, Conscrypt, ProxySelector, HostnameVerifier, WebView, TrustKit, Netty
trustmanagerTrustManager OnlyLIGHTWEIGHTAndroid platform TLS only — lowest crash risk
okhttpOkHttp Pinner + ProxyOKHTTP APPSOkHttp CertificatePinner + ProxySelector
proxy_onlyProxy Redirect OnlyDIAGNOSTICForces traffic through proxy without SSL bypass
webviewWebView + HttpsURLHYBRID APPSWebView auto-proceed + HttpsURLConnection bypass
react_nativeReact NativeRN APPSOkHttpClientProvider, TrustKit, NetworkingModule
flutterFlutter / DartFLUTTERNative BoringSSL hooks in libflutter.so (needs iptables)

Plus 7 app-specific scripts for Pilot Flying J, LinkedIn, DailyPay, Speedway, and Papa Johns.

Custom Scripts

Create custom scripts from the web UI (Scripts page → New Script) or upload .js files. Custom scripts are stored in frida_scripts/custom/ with a META header:

javascript
// META: {"label":"CUSTOM","desc":"My bypass script"}
Java.perform(function() {
  var TrustManager = Java.use("javax.net.ssl.X509TrustManager");
  // Your hooks here
});

All scripts — built-in and custom — can be edited from the web UI. Changes are saved directly to the script file on disk.

API Reference

The Go backend serves a REST API on port 9090 (configurable). All endpoints are prefixed with /api/.

State & Events

MethodEndpointDescription
GET/api/stateCurrent capture state, mode, flow/log counts, settings
GET/api/eventsSSE stream — real-time flows, logs, state changes

SSE event types:

EventDataWhen
state{ capturing, captureMode, captureName }Capture started/stopped
flowFull flow objectNew HTTP flow captured
log{ Time, Msg, Style }Backend log entry
clear{}Flows cleared

Capture Control

MethodEndpointBodyDescription
POST/api/capture/start{ mode, package }Start capture. Returns 409 if already capturing.
POST/api/capture/stop{}Stop active capture
POST/api/capture/clear{}Clear captured flows

Available modes: standard, mitm_only, signup_handoff, linkedin_cronet, linkedin_replay, dailypay, speedway, papajohns

Flows

MethodEndpointDescription
GET/api/flowsAll captured flows as JSON array
GET/api/flows?id=NSingle flow by index

Flow object shape:

json
{
  "ts": 1711584000.123,
  "method": "POST",
  "url": "https://api.example.com/v1/auth",
  "host": "api.example.com",
  "path": "/v1/auth",
  "status": 200,
  "req_size": 256,
  "resp_size": 1024,
  "content_type": "application/json",
  "req_headers": { "Authorization": "Bearer ..." },
  "resp_headers": { "Set-Cookie": "..." },
  "req_body": "{\"username\":\"...\"}",
  "resp_body": "{\"token\":\"...\"}"
}

Bodies are truncated to 5,000 characters.

Device

MethodEndpointDescription
GET/api/deviceDevice model, Android version, SDK, SELinux, Frida status, proxy, host IP
POST/api/device/frida/startStart frida-server on device
POST/api/device/proxy/clearClear system proxy setting

Scripts

MethodEndpointDescription
GET/api/scriptsAll scripts (built-in + custom) with metadata
GET/api/scripts/content?id=XFull source code of a script
POST/api/scripts/customCreate: { name, content, label, desc }
PUT/api/scripts/customUpdate: { id, name, content, label, desc }
DELETE/api/scripts/custom?id=XDelete custom script

Settings

MethodEndpointDescription
GET/api/settingsAll settings as key-value array
PUT/api/settingsUpdate setting: { key, value }
POST/api/exportExport flows: { format } (json or har)

Configuration

Settings are stored in settings.json and editable via the TUI or web UI.

KeyDefaultDescription
package""Target Android package name
port8080mitmproxy listen port
attach_delay10Seconds to wait for ART JIT before Frida attach
ignore_hostsPerimeterX regexHosts to pass through without interception
frida_script_id"universal"Active Frida script ID
captures_dir./capturesDirectory for exported captures
frida_server/data/local/tmp/fs-helper-64Path to frida-server on device
host_ipAuto-detectHost machine IP for proxy config
export_format"json"Export format: "json" or "har"
ui_mode"tui""tui" or "web"
web_port9090Web UI port

Architecture

text
sniff!/
├── main.go              # TUI, capture sequences, ADB, Frida, mitmdump
├── web.go               # HTTP API, SSE streaming, REST handlers
├── go.mod               # Go dependencies (BubbleTea + stdlib)
├── frida_scripts/       # 14+ built-in .js bypass scripts
│   └── custom/          # User-created scripts
├── site/                # Next.js web dashboard
│   ├── src/app/         # App Router pages
│   ├── src/components/  # React components
│   └── src/lib/         # API client, state, filters, types
├── dev                  # Dev script (backend + frontend)
└── build-sniff!      # Production build script

The Go backend manages all subprocesses (Frida, mitmdump, ADB) via os/exec. Captured flows are written to a JSONL file by a mitmdump Python addon, then tailed by the Go process at 300ms intervals and streamed to connected clients via SSE.

Data flow:
Android App → System Proxy → mitmdump (terminates TLS)
/tmp/sniff_flows.jsonl (one JSON per flow)
Go backend (tails JSONL, parses flows)
SSE /api/events → Next.js Dashboard

The Frida injection works by hooking Java-level SSL methods (TrustManager, OkHttp CertificatePinner, etc.) or native functions (BoringSSL for Flutter) at runtime, making the app accept the mitmproxy CA certificate.

An x-lock open source project

Open Dashboard