A lightweight honeypot that wastes WordPress scanners' time by slowly dripping an endless fake database dump.
Instead of returning a fast 404 to bots probing for /wp-login.php, /wp-admin/, /xmlrpc.php, etc., wp-tarpit responds with HTTP 200 and streams a never-ending fake SQL backup full of convincing-looking (but completely fake) credentials. Each response takes 30+ seconds and ties up the scanner's connection, making your server an expensive target to probe.
WARNING: Do not install this on sites that actually run WordPress. wp-tarpit intercepts all requests to
/wp-admin,/wp-login.php,/xmlrpc.php, etc. and traps them. This is designed for non-WordPress sites where those paths would normally return 404. Installing it on a real WordPress site will lock out admins and break the application.
Scanner ──► nginx/Apache ──► wp-tarpit (Unix socket)
│ │
│ /wp-login.php │ 200 OK
│ ────────────► │ Content-Type: application/sql
│ │
│ │ -- WordPress Database Backup
│ │ INSERT INTO wp_users ...
│ │ 'sk_live_a3f8...' ← (fake)
│ │ ... (drip, drip, drip)
│ │
│ /about (normal) │
│ ────────────► │ (not intercepted — passes to your real backend)
- Runs as a hardened systemd service under a dedicated no-login user
- Communicates via Unix socket (no TCP port exposure)
- Zero dependencies — Python 3.7+ standard library only
- Systemd security score: 1.2/10 (lower is better)
- Works with any backend — your real application is unaffected
git clone https://github.com/lakeforestcomputer-com/wp-tarpit.git
cd wp-tarpit
sudo bash scripts/install.shThe installer will:
- Create a
wp-tarpitsystem user (no home, no shell) - Deploy the script to
/opt/wp-tarpit/ - Scan the host for
.envfiles and automatically build a SHA-256 blacklist of your real secrets - Install and start the systemd service
- Detect Apache or nginx and install the appropriate snippet
- Add the web server user to the
wp-tarpitgroup for socket access
Then add the snippet to your virtual hosts:
Apache — add before any ProxyPass / directive:
<VirtualHost *:443>
Include /etc/apache2/snippets/wp-tarpit.conf
ProxyPass / http://your-backend:8000/
ProxyPassReverse / http://your-backend:8000/
</VirtualHost>nginx — add inside each server {} block:
server {
include snippets/wp-tarpit.conf;
location / {
proxy_pass http://your-backend:8000;
}
}Restart your web server and you're done.
# Direct socket test (should drip slowly and return fake SQL)
curl --unix-socket /run/wp-tarpit/wp-tarpit.sock --max-time 5 http://localhost/test
# Through your web server
curl -sk --max-time 10 https://yoursite.com/wp-login.php
# Check logs
journalctl -u wp-tarpit -fwp-tarpit generates random hex and alphanumeric strings. The odds of accidentally generating one of your real API keys are astronomically small (1 in 16^64 for a 64-character hex string), but you can eliminate even that possibility.
The blacklist file stores SHA-256 hashes of your real secrets — never the plaintext values. The tarpit checks each generated string against the blacklist before sending it.
During installation, the installer automatically scans /etc, /opt, /srv, /home, /var/www, and /root for .env files and builds the initial blacklist. To update it later or add additional sources:
Scan directories for .env files:
sudo bash scripts/generate-blacklist.sh -d /etc -d /opt -d /srvScan specific files:
sudo bash scripts/generate-blacklist.sh \
-f /etc/myapp/.env \
-f /opt/app/config.envAdd individual secrets:
sudo bash scripts/generate-blacklist.sh \
-s "sk_live_abc123..." \
-s "whsec_xyz789..."Combine all approaches:
sudo bash scripts/generate-blacklist.sh \
-d /etc/myapp \
-f /home/deploy/.env \
-s "my-specific-api-key"Then restart the service:
sudo systemctl restart wp-tarpitThe blacklist file at /opt/wp-tarpit/blacklist.sha256 looks like this:
a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1
b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5
...
Each line is a SHA-256 hash. An attacker who obtains this file gains nothing — the hashes cannot be reversed to recover your secrets.
The service accepts command-line arguments. Edit the systemd unit's ExecStart line to customize:
| Option | Default | Description |
|---|---|---|
--socket |
/run/wp-tarpit/wp-tarpit.sock |
Unix socket path |
--blacklist |
/opt/wp-tarpit/blacklist.sha256 |
Blacklist file path |
--delay-min |
0.5 |
Minimum delay between chunks (seconds) |
--delay-max |
2.0 |
Maximum delay between chunks (seconds) |
--max-connections |
50 |
Maximum concurrent tarpit connections |
--max-session |
300 |
Maximum session duration in seconds |
Example — slow the drip to 2-5 seconds per chunk:
ExecStart=/usr/bin/python3 /opt/wp-tarpit/wp-tarpit.py --delay-min 2 --delay-max 5By default, these WordPress paths are routed to the tarpit:
| Path | Why |
|---|---|
/wp-admin/* |
Admin panel — #1 scanner target |
/wp-login.php |
Login page — brute force target |
/wp-config.php |
Config file — contains DB creds |
/xmlrpc.php |
XML-RPC — amplification attacks |
/wp-content/* |
Plugins/themes — vulnerability scanning |
/wp-includes/* |
Core files — version fingerprinting |
/wordpress/* |
Common path guess by scanners |
Edit the web server snippet to add or remove paths.
| Layer | Protection |
|---|---|
| User | Dedicated wp-tarpit system user — no home, no shell, own group |
| Filesystem | Script: 500, blacklist: 400, directory: 500 — all owned by wp-tarpit |
| Network | Unix socket only (RestrictAddressFamilies=AF_UNIX) — no TCP/UDP |
| Kernel | NoNewPrivileges, ProtectSystem=strict, PrivateDevices, MemoryDenyWriteExecute, ProtectKernel* |
| Syscalls | Allowlist via SystemCallFilter, no privileged/mount/resource calls |
| Capabilities | CapabilityBoundingSet= (empty — zero capabilities) |
| Blacklist | SHA-256 hashes only — no plaintext secrets stored |
Run systemd-analyze security wp-tarpit to audit the sandbox yourself.
The tarpit is designed to waste attacker resources, not yours. Multiple layers prevent it from being turned against you:
| Layer | What it does | Default |
|---|---|---|
| Web server rate limit | nginx limit_req / Apache mod_ratelimit throttles requests per IP before they reach the tarpit |
2 req/s (nginx), 512 bytes/s (Apache) |
| Connection cap | Tarpit drops new connections beyond the limit immediately | 50 concurrent |
| Session timeout | Each connection is terminated after a maximum duration | 300 seconds |
| systemd memory limit | OOM-kills the tarpit if it exceeds the cap | 128 MB |
| systemd CPU quota | Prevents the tarpit from starving other services | 25% of one core |
| systemd file descriptors | Hard cap on open sockets | 256 |
| systemd task limit | Hard cap on threads | 128 |
A bot hitting /wp-login.php receives:
-- WordPress Database Backup
-- Generated by WP-CLI v2.11.0
-- Host: localhost
-- Database: wordpress_production
INSERT INTO `wp_stripe_customers` (`sk_live_key`, `option_name`, `api_secret`, `id`, `meta_key`) VALUES
('b6a52583d962375585a061466839ef952c20f5f4ad913d19a9c32382b98154a2', ...),
('95f0293919519e996df77449c03a15633e55ea11d532e2446fc005af10aab217', ...),
...The response never ends. New INSERT statements keep appearing every 0.5-2 seconds, each packed with fake hex strings that look like API keys and credentials. The scanner's connection stays pinned, wasting its time and resources.
sudo bash scripts/uninstall.sh
# Then restart your web server
sudo systemctl restart apache2 # or nginx- Python 3.7+
- systemd
- Apache (with
mod_rewrite,mod_proxy,mod_proxy_http) or nginx - Linux
MIT — see LICENSE.