Project Lethe is a lightweight CLI for authorized WAF resilience testing. It can profile an in-scope URL, ask an AI provider for a safety-first test plan, and generate deterministic payload variants using allowlisted transformations.
Use this only on systems you own, administer, or have explicit permission to test.
Kali usually includes Python, but install the basics first:
sudo apt update
sudo apt install -y git python3 python3-venv python3-pipCheck Python:
python3 --versionProject Lethe requires Python 3.10 or newer.
git clone https://github.com/styxabhor/Project-Lethe.git
cd Project-LetheIf you already have the project folder on Kali, just enter it:
cd project-lethepython3 -m venv .venv
source .venv/bin/activateYour shell should now show (.venv).
Install it in editable mode:
pip install -e .Confirm it works:
lethe --version
lethe --helpExpected version at the time this guide was written:
Project Lethe 0.11.0
Copy the example config:
cp .lethe.toml.example .lethe.tomlEdit it:
nano .lethe.tomlBasic config:
[lethe]
profile = "xss"
limit = 25
max_depth = 2
format = "table"
table_width = 96
include_baseline = false
banner = true
profile_timeout = 10Check resolved config:
lethe configIgnore config for one command:
lethe --no-config generate --payload '<test>'Basic usage:
lethe generate --payload '<waf-test data-id="123">' --profile xss --limit 12JSON output:
lethe generate \
--payload '<waf-test data-id="123">' \
--profile xss \
--limit 12 \
--format jsonMarkdown report output:
lethe generate \
--payload '<waf-test data-id="123">' \
--profile xss \
--limit 12 \
--format markdownUse only one technique tag:
lethe generate --payload 'abc def' --profile generic --tag unicodeList profiles:
lethe profilesList tags:
lethe tagsList techniques:
lethe techniques
lethe techniques --tag unicodeThis mode is for simulating unseen WAF normalization cases in authorized labs. It does not generate real zero-day exploits. It combines allowlisted local transformations and bounded randomized padding.
Stack multiple mutation layers:
lethe generate \
--payload '<waf-test data-id="123">' \
--profile xss \
--stack 3 \
--limit 75Default stack depth is capped at 4 to avoid runaway output. For local labs, you can allow depths up to 6:
lethe generate \
--payload '<test>' \
--stack 5 \
--allow-deep-stack \
--limit 100Explore novelty-oriented parser, Unicode, entity, recursive, and canonicalization techniques:
lethe generate \
--payload '<waf-test>' \
--explore \
--format jsonPreview the resolved plan without generating variants:
lethe generate \
--payload '<waf-test>' \
--explore \
--stack 3 \
--dry-run \
--format jsonAdd bounded polymorphic padding variants:
lethe generate \
--payload '<waf-test>' \
--profile xss \
--limit 40 \
--polymorphic 10 \
--poly-seed report-001 \
--format markdownUse a specific polymorphic style:
lethe generate --payload '<test>' --polymorphic 5 --poly-style zero-width
lethe generate --payload '/search?q=test' --polymorphic 5 --poly-style query-padUseful styles:
mixed: rotates between safe padding styles.html-comment: wraps with HTML comment canaries.block-comment: wraps with block comment canaries.zero-width: inserts invisible Unicode separators.query-pad: appends a harmlesslethe_pad_*query parameter.
Use --poly-seed when you need reproducible output for a report.
Use explain when you want report-friendly proof of how a transformation
changed your test string:
lethe explain --payload '<waf-test>' --technique html_hex_entitiesMarkdown:
lethe explain \
--payload '<waf-test>' \
--technique html_hex_entities \
--format markdownExplain a stacked transformation chain:
lethe explain \
--payload '<waf-test>' \
--technique html_hex_entities \
--technique url_encode_reservedUse analyze when you need triage-ready language explaining why a transformed
variant matters. It outputs normalization hypotheses, evidence notes, safe
validation guidance, remediation, and a character-level diff.
Analyze a known Lethe technique chain:
lethe analyze \
--payload '<waf-test>' \
--technique html_hex_entities \
--technique url_encode_reserved \
--profile xss \
--format markdownAnalyze a copied variant from a previous run:
lethe analyze \
--payload '<waf-test>' \
--mutated '%3Cwaf-test%3E' \
--format markdownAdd an AI narrative using a local Ollama model:
lethe analyze \
--payload '<waf-test>' \
--technique html_hex_entities \
--ai-provider ollama \
--ai-model qwen3:8b \
--format markdownDemo the AI flow without an API key:
lethe analyze \
--payload '<waf-test>' \
--technique html_hex_entities \
--ai-command 'python examples/mock_ai_analysis.py' \
--format markdownThe AI analysis mode is explanation-only. It is instructed not to invent new payloads, bypasses, exploit code, scanning steps, or mutation ideas.
Profile mode makes one request to an in-scope URL and returns response context. It helps Lethe choose better profiles and technique tags.
lethe profile https://example.com/account --format json --output site-profile.jsonUse the site profile during generation:
lethe generate \
--payload '<waf-test>' \
--site-profile site-profile.json \
--limit 12Optional reflection check with a harmless canary:
lethe profile https://example.com/search --reflectPrefer environment variables so secrets do not land in shell history.
Bearer token example:
export LETHE_TOKEN='redacted-token-here'
lethe profile https://example.com/account \
--bearer-token-env LETHE_TOKEN \
--format json \
--output site-profile.jsonCookie example:
export LETHE_COOKIE='session=redacted'
lethe profile https://example.com/account \
--cookie-env LETHE_COOKIE \
--format json \
--output site-profile.jsonCustom header example:
lethe profile https://example.com/account \
--header 'X-Program-Scope: authorized' \
--format json \
--output site-profile.jsonIf you are profiling multiple authorized pages, save auth material temporarily:
export LETHE_TOKEN='redacted-token-here'
export LETHE_COOKIE='session=redacted'
lethe auth save hackerone-prod \
--bearer-token-env LETHE_TOKEN \
--cookie-env LETHE_COOKIE \
--header 'X-Program-Scope: authorized' \
--ttl 30mReuse it:
lethe profile https://example.com/account --auth-ref hackerone-prod
lethe profile https://example.com/settings --auth-ref hackerone-prodManage saved auth:
lethe auth list
lethe auth clear hackerone-prod
lethe auth purgeThe cache is local, temporary, and redacted in output. Keep TTLs short.
Lethe can use AI for planning, but the AI does not generate final payload strings. The AI only returns a safety verdict and strategy:
allow: normal authorized testing context.review: scope or intent should be confirmed.block: unsafe or abusive request.
Lethe still generates variants using deterministic local transformations.
Set the API key in your shell:
export GEMINI_API_KEY='your-gemini-key-here'Add this to .lethe.toml:
[ai]
provider = "gemini"
model = "gemini-2.5-flash"
api_key_env = "GEMINI_API_KEY"Run a plan:
lethe plan \
--payload '<waf-test>' \
--site-profile site-profile.jsonUse AI planning during generation:
lethe generate \
--payload '<waf-test>' \
--site-profile site-profile.json \
--aiexport OPENAI_API_KEY='your-openai-key-here'.lethe.toml:
[ai]
provider = "openai"
model = "gpt-4.1-mini"
api_key_env = "OPENAI_API_KEY"Run:
lethe plan --payload '<waf-test>'export ANTHROPIC_API_KEY='your-anthropic-key-here'.lethe.toml:
[ai]
provider = "claude"
model = "claude-sonnet-4-6"
api_key_env = "ANTHROPIC_API_KEY"You can also use provider = "anthropic" as an alias.
Run:
lethe plan --payload '<waf-test>'Ollama is the easiest free/local option on Kali.
Install Ollama:
curl -fsSL https://ollama.com/install.sh | shPull a model:
ollama pull llama3.2.lethe.toml:
[ai]
provider = "ollama"
model = "llama3.2"Run:
lethe plan --payload '<waf-test>'
lethe generate --payload '<waf-test>' --aiNo real API key is required for Ollama. Lethe uses the local OpenAI-compatible
endpoint at http://localhost:11434/v1.
Start LM Studio's local server, load a model, then use:
[ai]
provider = "lmstudio"
model = "local-model"Default endpoint:
http://localhost:1234/v1
Aliases:
provider = "lm-studio"
provider = "lm_studio"Start LocalAI, make sure a model is available, then use:
[ai]
provider = "localai"
model = "llama-3.2-1b-instruct:q4_k_m"Default endpoint:
http://localhost:8080/v1
Aliases:
provider = "local-ai"
provider = "local_ai"Use this for local model servers or compatible gateways:
[ai]
provider = "openai-compatible"
model = "local-json-planner"
api_key_env = "OPENAI_API_KEY"
base_url = "http://localhost:11434/v1"Important: openai-compatible requires base_url so Lethe does not
accidentally send requests to the wrong endpoint.
For local testing or demos, use the included mock planner:
lethe plan \
--payload 'a b' \
--ai-command 'python examples/mock_ai_plan.py'Use the mock plan during generation:
lethe generate \
--payload 'a b' \
--ai-plan-command 'python examples/mock_ai_plan.py'Lethe can print safe request-shape templates for WAF/backend parser comparison. It does not send these requests.
lethe http-plan \
--host example.com \
--path /search \
--param q=safe \
--format rawInclude bounded local-lab stress templates:
lethe http-plan \
--host example.com \
--path /search \
--include-stress \
--max-padding 1024 \
--regex-length 64 \
--format markdownInclude HTTP/2 downgrade discrepancy templates for authorized proxy/WAF translation testing:
lethe http-plan \
--host example.com \
--path /search \
--param q=safe \
--include-http2 \
--format rawThese are text templates only. They cover :path versus path, :authority
versus host, duplicate pseudo-header rejection, lowercase-header enforcement,
and TE/content-length downgrade consistency with harmless canaries.
Use stress templates only in owned labs or explicitly authorized environments.
Project Lethe has a custom terminal signature:
STYX://PROJECT-LETHE [trace online]
canon.map=armed | mode=authorized-lab | output=clean
sig=Avinash K A//styxabhor
Force it:
lethe generate --payload '<test>' --bannerDisable it:
lethe generate --payload '<test>' --no-bannerThe animated frames appear only in an interactive terminal:
STYX://PROJECT-LETHE [seed ] <lethe.......................> canary indexed
STYX://PROJECT-LETHE [wash ] <..lethe.....................> syntax rinsed
STYX://PROJECT-LETHE [morph ] <....lethe...................> layers braided
STYX://PROJECT-LETHE [trace ] <......lethe.................> canonical path
STYX://PROJECT-LETHE [online] <........lethe...............> trace online
STYX://PROJECT-LETHE [trace online]
From inside the repo:
git pull
source .venv/bin/activate
pip install -e .Run tests after updates:
PYTHONPATH=src python -m unittest discover -s testsIf lethe is not found:
source .venv/bin/activate
pip install -e .If Python is too old:
python3 --version
sudo apt update
sudo apt install -y python3 python3-venv python3-pipIf AI planning says the API key was not found:
echo "$GEMINI_API_KEY"
echo "$OPENAI_API_KEY"
lethe configIf openai-compatible fails:
lethe configConfirm ai_base_url is set.
If a site profile fails:
- Confirm the URL starts with
http://orhttps://. - Confirm the route is in scope.
- Confirm auth tokens or cookies are valid.
- Try a higher timeout in
.lethe.tomlwithprofile_timeout = 20.
# 1. Profile an authorized page
lethe profile https://example.com/search --format json --output site-profile.json
# 2. Ask AI for a safety/context plan
lethe plan --payload '<waf-test>' --site-profile site-profile.json
# 3. Generate deterministic variants using that context
lethe generate --payload '<waf-test>' --site-profile site-profile.json --ai --limit 25
# 4. Explain one interesting transformation
lethe explain --payload '<waf-test>' --technique html_hex_entities --format markdown
# 5. Turn the transformation into report-ready analysis
lethe analyze --payload '<waf-test>' --technique html_hex_entities --format markdownThat is the clean Kali flow: profile, plan, generate, explain, analyze, report.