Warning
- This tool is for authorised penetration testing and stress-testing only.
- Running it against systems you do not own or have explicit written permission to test is illegal in most jurisdictions.
- The authors accept no liability for misuse.
Proof of concept denial of service over TOR stress test tool. Is multi-threaded and supports multiple attack vectors.
Requires Python 3.10 or higher.
Python requirement Requires Python 3.10 or higher.
Linux installation
$ git clone https://github.com/skizap/dos-over-tor.git
$ cd dos-over-tor
$ pip install -e .Tor setup
- Install Tor:
$ sudo apt install tor # Debian/Ubuntu $ sudo dnf install tor # Fedora/RHEL
- Start the Tor service:
$ sudo systemctl enable --now tor - Verify the two required ports are listening:
Port 9050 is the SOCKS5 proxy (used by
$ ss -tlnp | grep -E '9050|9051'
--tor-proxy-port) and port 9051 is the control port (used by--tor-ctrl-port).
General syntax
$ ./main.py <mode> <target> [options]Common options table
| Flag | Default | Description |
|---|---|---|
--tor-address |
127.0.0.1 |
Tor service address |
--tor-proxy-port |
9050 |
Tor SOCKS5 proxy port |
--tor-ctrl-port |
9051 |
Tor control port |
--num-threads |
10 |
Number of soldier threads (1β100) |
--http-method |
GET |
HTTP method (GET, POST, PUT, DELETE) |
--cache-buster |
off | Append random query string to every request |
--identity-rotation-interval |
disabled | Rotate Tor identity every N seconds |
singleshot mode
Hits a single URL repeatedly across all threads. Example:
$ ./main.py singleshot https://example.com --num-threads=20 --identity-rotation-interval=60fullauto mode
Crawls the target domain for links (using BeautifulSoup) and hits as many pages as possible. Mode-specific flags:
| Flag | Default | Description |
|---|---|---|
--max-urls |
500 |
Max URLs to discover per thread |
--max-time |
180 |
Max crawl time in seconds per thread |
Example:
$ ./main.py fullauto https://example.com --num-threads=50 --max-urls=200 --max-time=120 --http-method=POST --cache-buster --identity-rotation-interval=60slowloris mode
Opens many partial HTTP connections and keeps them alive with slow keep-alive headers, exhausting the server's connection pool. Mode-specific flag:
| Flag | Default | Description |
|---|---|---|
--num-sockets |
100 |
Sockets to open per thread |
Note that bytes_received is always 0 for this mode because no response body is ever read. Example:
$ ./main.py slowloris https://example.com --num-threads=25 --num-sockets=200 --cache-buster --identity-rotation-interval=60Interactive mode
The ./main.py interactive command acts as a guided wizard. The five wizard steps:
- Mode selection β choose
1(singleshot),2(fullauto), or3(slowloris). - Target URL β must include
http://orhttps://scheme and a domain; re-prompted on invalid input. - Common options β threads, HTTP method, cache buster, Tor address/ports, identity rotation interval (enter
0to disable). - Mode-specific options β sockets count for slowloris; max URLs and max time for fullauto.
- Execution β wizard builds
AttackConfigand delegates toAttackRunner.
Example:
$ ./main.py interactivesequenceDiagram
participant CLI as main.py / InteractiveWizard
participant Runner as AttackRunner
participant Preflight as PreFlightValidator
participant Tor as TorClient
participant Platoon as Platoon
participant Soldier as SoldierThread
participant Weapon as Weapon
participant Monitor as Monitor
participant Reporter as SummaryReporter
CLI->>Runner: run(AttackConfig)
Runner->>Tor: connect(address, proxy_port, ctrl_port)
Runner->>Preflight: validate(tor_client, config)
Preflight-->>Runner: True / False
Runner->>Platoon: attack(target_url, weapon_factory)
loop each SoldierThread
Platoon->>Soldier: attack(target_url, weapon)
Soldier->>Weapon: attack()
Weapon-->>Soldier: AttackResult
Soldier->>Monitor: report_attack_result(result)
end
Platoon->>Monitor: get_summary()
Monitor-->>Runner: AttackSummary
Runner->>Reporter: display(summary)
AttackConfig(app/models.py) β dataclass holding all configuration: mode, target, thread count, Tor ports, and mode-specific parameters.AttackRunner(app/runner.py) β orchestrates the full lifecycle: Tor connection, pre-flight, proxy scope, weapon factory creation, platoon execution, and summary display.PreFlightValidator(app/preflight.py) β checks Tor control connection, retrieves the current exit IP via the proxy, and prints the configuration summary before the attack starts.Platoon(app/command.py) β spawnsSoldierThreadinstances (with a staggered 1β2 s ramp-up), optionally starts anIdentityRotator, and drives the live status display loop.SoldierThread(app/command.py) β athreading.Threadthat loops callingWeapon.attack()and forwarding eachAttackResultto theMonitor.Weapon(app/weapons/__init__.py) β abstract base; concrete implementations areSingleShotWeapon,FullAutoWeapon, andSlowLorisWeapon.Monitor(app/command.py) β thread-safe collector ofAttackResultobjects; provides live metrics (hits/sec via rolling buckets) and a finalAttackSummary.SummaryReporter(app/reporter.py) β formats and prints the end-of-runAttackSummarywith human-readable byte sizes, durations, and HTTP status distribution.
singleshot/fullauto:NetworkClient.request()inapp/net.pycomputesbytes_sentfrom the request-line length + header lengths.bytes_receiveduses theContent-Lengthresponse header when present; otherwise falls back toresponse.lengthhint or a fixed 200-byte header estimate. These are best-effort estimates β actual wire bytes may differ.slowloris:SlowLorisWeaponinapp/weapons/slowloris.pycounts the exact bytes passed to eachsock.send()call (HTTP request line + headers + keep-alive headers), sobytes_sentis accurate.bytes_receivedis always0because no response body is ever read.
Tor not running Symptom:
[ERROR] Failed to connect to Tor: failed to connect to control port; ...
or:
[ERROR] Tor control connection failed - not connected to Tor
[ERROR] Tor proxy connection failed - ...
Fix: ensure Tor is running (sudo systemctl start tor) and that ports 9050 and 9051 are open. Verify with ss -tlnp | grep -E '9050|9051'.
Invalid target URL Symptom (interactive mode):
Invalid URL. Must include scheme (http/https) and domain.
Fix: always prefix the target with http:// or https://. The CLI singleshot/fullauto/slowloris commands call url_ensure_valid() from app/net.py which defaults to https:// if no scheme is given, but the interactive wizard enforces the scheme explicitly.
Network failures during a run Symptom:
[ERROR] Network request error during attack: ...
[ERROR] Unexpected error during attack: ...
Individual thread errors are also logged per-request:
[ERROR] <RequestException message>
Fix: check that the Tor proxy is still reachable (curl --socks5-hostname 127.0.0.1:9050 https://icanhazip.com). Transient errors are counted in the total_errors field of the summary and do not stop the attack.
- Dev install:
$ pip install -e .[dev]
- Run tests:
$ pytest
- Type checking:
$ mypy app/
- Lint:
$ ruff check . - Format:
$ ruff format .
