Few security tools announce their intent as plainly as Trape. The subtitle on the repository reads “People tracker on the Internet: Learn to track the world, to avoid being traced,” and the project — created by Jose Pino, presented at BlackHat Arsenal in Singapore in 2018 — describes itself as an OSINT analysis and research tool for tracking and executing social-engineering attacks in real time, built to show how large Internet companies could obtain confidential information such as session status and browser control without a user’s knowledge. The codebase studied here is the stable 2.0 release (the version file says 2.0 while the source banner says 2.1): roughly a dozen Python files built on Flask, flask-socketio, eventlet, and SQLite, plus a small set of JavaScript files that do the actual in-browser collection.
What makes Trape genuinely distinctive is not any single capability but the completeness of the loop. One command clones a target page on the fly, rewrites its asset links, injects a collector, and serves it under a randomized URL. The collector reports back over both HTTP and WebSockets: device profile, GPU renderer, battery state, incognito and Do-Not-Track flags, local IP addresses harvested through WebRTC, detected social-network sessions, continuously refreshed coordinates, and every form field the visitor types. A password-protected dashboard renders the stream, and an optional ngrok tunnel plus a REST API script extend it to third-party websites. Few open-source projects wire all of those stages together, which is why the source rewards reading.
This article reads the repository strictly as a software engineering study. Trape’s own disclaimer states it was published for educational purposes and that the author is not responsible for how it is used, and that boundary matters: deploying lures against real people is illegal surveillance and impersonation in most jurisdictions. The legitimate takeaway is defensive — seeing precisely how a cloned page turns one click into a firehose of personal data is what makes unexpected links, and the permissions they request, worth distrusting.
Trape's end-to-end shape: the boot layer configures randomized paths and tunnels, the lure engine clones and injects, the injected client streams telemetry to the capture API, and a Flask panel reads everything from SQLite in real time.
Reading the overview from left to right:
- Boot entry (
trape.py) — prints the banner, loadstrape.config, generates per-run random paths, parses flags. See trape.py. - Lure route (
core/user.py) — clones the target page and injects the client. See user.py. - Injected client (
static/js/payload.js,static/js/base.js) — the in-browser collectors. See payload.js. - Capture API (
core/user.py) —/register,/lr,/nr,/bs,/nm,/regvand friends. - Realtime layer (
core/sockets.py) — a SocketIO namespace that pushes hook events. See sockets.py. - Panel (
core/stats.py) — login gate, dashboard data routes, inject-script service. See stats.py. - Data layer (
core/db.py) — an eight-table SQLite database. See db.py.
Why You Need This
The first value is understanding modern link-based tracking at the source level. The lure route in core/user.py fetches the target URL with the visitor’s own user-agent, then victim_inject_code in core/user_objects.py rewrites every relative src= and href= back to the original domain so the page looks and behaves exactly like the real one, and finally splices four script tags in front of </head>: a jQuery library bundle, the base collector, a custom hook file, and the payload bootstrap. Two injected globals are API keys — a Google Maps key and an IP geolocation key — set directly on window. Nothing about the mechanism is hidden; it is all in a hundred lines of readable Python, and knowing that pattern makes cloned pages far easier to recognize.
The second value is the browser-reconnaissance catalog. The injected JavaScript demonstrates, in one file, almost every passive fingerprinting surface a browser exposes: WebGL’s WEBGL_debug_renderer_info for the GPU vendor and renderer, navigator.getBattery() for charge status, incognito-mode heuristics built on RequestFileSystem and indexedDB failures, navigator.doNotTrack, and the classic WebRTC trick of creating an RTCPeerConnection with RtpDataChannels to harvest local IP addresses from SDP candidates. The session-detection routine is equally instructive — it loads a small image from each monitored service and treats a successful onload as proof the visitor is logged in. Each technique is a defensive lesson about what a page can learn without any dialog.
The third value is the architecture itself: a real-time observability stack in miniature. Telemetry arrives over plain POST endpoints, while operator-facing events — a new session, a captured form field, a voice message sent — flow through flask-socketio’s /trape namespace into a room per victim ID. Everything lands in an eight-table SQLite schema whose dashboard queries are long, explicit JOINs rather than an ORM. For anyone building a monitoring UI, Trape is a compact reference for the Flask-plus-SocketIO-plus-SQLite pattern, right down to the multiprocessing Process it spawns to ping-sweep the visitor’s subnet.
How It Works
The full anatomy: boot-time randomization, the clone-and-inject lure engine, the injected client's collectors, the capture endpoints and realtime namespace, the panel routes, the SQLite schema, and the ngrok tunnel lifecycle.
Understanding the Architecture
Boot and randomization. trape.py instantiates the Trape class and the Database, and on first run — when database.db does not exist — prints an ASCII banner and a disclaimer, then waits for Enter. The constructor is where Trape’s security-through-unpredictability lives: generateToken shuffles a character set, hashes it with SHA-1, and slices tokens of set lengths for the dashboard home path (18 chars), the logout path (6), the delete path (14), the REST API script name (12 plus .js), and the panel access key (24, unless --accesskey overrides it). A first-run wizard writes trape.config with ngrok, Google Maps, URL-shortener, and IP-geolocation keys; if the Google keys are left empty, hardcoded fallbacks are used. The --update flag simply runs git reset --hard origin/master and git pull.
The lure engine. The decoy path is derived from the --url argument, and the Flask route registered at /<victim_path> is the heart of the clone: it re-fetches the target with the victim’s user-agent (with DNT and keep-alive headers), applies the relative-URL rewrites, injects the scripts, and returns the page. A --local variant renders a local HTML template instead of a remote clone. Because asset filenames are also randomized per run — seven JavaScript files and six CSS files each get a fresh 12-character token, resolved by lookup tables in the route handlers — a returning visitor’s cached copies do not map to any stable URL, and static analysis of the served HTML reveals nothing meaningful.
The injected client. payload.js runs first: it fetches IP geolocation data from api.ipgeolocation.io using the injected key, merges it with a local device profile, serializes the UAParser CPU string plus navigator.hardwareConcurrency, and POSTs everything to /register. On success it stores the victim ID in localStorage and fans out the collectors: queryGPU renders a WebGL canvas to read vendor and renderer strings, detectBattery reports charging state and levels, navigation_mode reports incognito detection and Do-Not-Track, getIPs extracts local addresses from WebRTC candidates, and the location loop POSTs coordinates to /lr and reschedules itself every five seconds — ten on error — so position updates keep flowing while the tab stays open. Every sixty seconds it re-runs the IP and network probes.
Session detection and the capture API. workWithNetworks fetches the public IP from ipinfo.io, then probes each entry in its Services list by loading a favicon-like image from the service; a successful load means the browser fetched an authenticated resource, so the tool POSTs the confirmed session to /nr. The Python side of victim_server registers every signal: /register builds a victim object (IP, platform, browser, version, a 63-port scan of the visitor’s address via utils.portScanner, CPU) and a victim_geo object (city, country, latitude, longitude, ISP, zip, organization), spawns getHostsAlive as a separate process that pings all 254 addresses of the visitor’s subnet and records which respond with latency, and inserts or updates the row depending on whether the ID has been seen before. /regv captures form fields, /rv proxies any URL the operator redirects the victim to and injects the vscript collector, and /tping keeps the online heartbeat current.
Realtime and panel. core/sockets.py wraps the Flask app in a SocketIO server. Clients join a room named after their victim ID; the my_room_event handler translates event types through attacks_hook_message — network, url, redirect, alert, execute, talk, jscode, jsscript — into console labels such as “Sending voice message” or “Injecting Script”, then re-emits the payload to the room, and disconnect_request marks the victim offline when the tab closes. The panel in core/stats.py gates on the 24-character access key at /login and returns the full path map; dashboard routes like /get_data and /get_preview run the heavy JOIN queries against victims, geo, clicks, battery, and hosts-alive tables, while /get_socialimpact aggregates sessions, locations, and interactions per detected network.
Tunnel and REST API. With an ngrok token configured, core/ngrok.py downloads the right binary for the platform from equinox.io if missing, registers the authtoken, and starts the tunnel as a multiprocessing.Process. The header routine then polls http://127.0.0.1:4040/api/tunnels, extracts the public ngrok.io URL, and shortens the public lure link through the Google shortener key. The REST API script served at the randomized inject URL lets the same collectors be embedded into third-party websites.
End to end. The operator starts Trape with a URL and port, the tool randomizes its paths and optionally opens a tunnel; a visitor opens the lure and their browser reports geolocation, device profile, WebRTC addresses, and detected sessions; each signal lands in SQLite and pushes over SocketIO to the dashboard, where every victim appears as a live card with location, network, and interaction history.
Advantages
- Complete working loop — cloning, injection, collection, storage, realtime push, and dashboard are all implemented and wired together, so the code demonstrates every stage of the technique.
- Per-run randomization — paths, asset names, and the access key are regenerated each launch through SHA-1 tokens, a study in unguessable URL schemes.
- Rich fingerprint catalog — GPU, battery, incognito heuristics, Do-Not-Track, WebRTC IP harvest, and image-based session detection in one auditable file.
- Hybrid transport design — plain POST endpoints for telemetry plus SocketIO rooms for operator events in one Flask app.
- Clean data schema — eight normalized SQLite tables with explicit dashboard JOINs make the state fully inspectable.
- Deployment extras — automatic ngrok binary download, a first-run config wizard, and a
--locallure mode reduce setup to a single command.
Benefits
- Security awareness material — the source is the most concrete way to show a non-technical audience why random links are dangerous: every claim in a training deck maps to a readable function here.
- Reference architecture — the Flask-plus-SocketIO-plus-SQLite stack, the Process-based subnet sweep, and the token-mapped static routes are reusable patterns for legitimate monitoring tools.
- Fingerprinting education — developers can see exactly which browser surfaces leak data and therefore what to harden, from WebRTC IP exposure to WebGL renderer strings.
- Historical value — presented at BlackHat Arsenal Singapore 2018, the project documents how tracking tooling of that era was engineered.
- Compact codebase — a dozen Python files with clear naming make the whole system traceable in an afternoon of reading.
- Extensibility study — the REST API inject script and the hook-message type map show how the author designed for operator-side automation from the start.
Usage
Install and launch:
git clone https://github.com/jofpin/trape.git
cd trape
pip3 install -r requirements.txt
python3 trape.py -h
Start a lure against a page on a chosen port:
python3 trape.py --url http://example.com --port 8080
Customize the panel key and REST API script name, and enable the ngrok tunnel:
python3 trape.py --url http://example.com --port 8080 --accesskey MYKEY123 --injectcode api.js --ngrok
Update to the latest version:
python3 trape.py --update
Conclusion
Trape is best understood as a mirror held up to the web: everything it does, it does with the browser’s own cooperation, one POST at a time. Its engineering is straightforward — Flask routes, a SocketIO namespace, a SQLite schema, and a handful of JavaScript collectors — yet the composite is a sobering demonstration of how much a single click can disclose: where you are, refreshed every few seconds; what devices share your network; which accounts you stay logged into; even how much battery your phone has left. The project’s disclaimer frames it as education, and that is the right way to receive it: read the source, recognize the patterns, close the doors it shows you — block third-party scripts on pages that ask for credentials, limit WebRTC leakage, and treat any link that wants location or login state with deep suspicion.
Links:
Enjoyed this post? Never miss out on future posts by following us