metatron-pentest-assistant
AI-powered penetration testing assistant using local LLM (metatron-qwen via Ollama) on Parrot OS Linux
Best use case
metatron-pentest-assistant is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
AI-powered penetration testing assistant using local LLM (metatron-qwen via Ollama) on Parrot OS Linux
Teams using metatron-pentest-assistant should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/metatron-pentest-assistant/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How metatron-pentest-assistant Compares
| Feature / Agent | metatron-pentest-assistant | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
AI-powered penetration testing assistant using local LLM (metatron-qwen via Ollama) on Parrot OS Linux
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# METATRON Penetration Testing Assistant
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
METATRON is a CLI-based AI penetration testing assistant that runs entirely locally — no cloud, no API keys. It orchestrates recon tools (nmap, whois, whatweb, curl, dig, nikto), feeds results to a locally running fine-tuned LLM (`metatron-qwen` via Ollama), and stores all findings in MariaDB with full scan history, vulnerability tracking, and PDF/HTML export.
---
## Architecture Overview
```
metatron.py ← CLI entry point, main menu, scan orchestration
db.py ← MariaDB CRUD (history, vulns, fixes, exploits, summary)
tools.py ← Recon tool runners (nmap, whois, whatweb, curl, dig, nikto)
llm.py ← Ollama interface, agentic loop, AI tool dispatch
search.py ← DuckDuckGo search + CVE lookup (no API key)
Modelfile ← Custom metatron-qwen model config
```
**Database spine:** every scan creates a `sl_no` in `history`; all other tables link via `sl_no`.
---
## Installation
### 1. Clone and set up Python environment
```bash
git clone https://github.com/sooryathejas/METATRON.git
cd METATRON
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
### 2. Install system recon tools
```bash
sudo apt install nmap whois whatweb curl dnsutils nikto
```
### 3. Install Ollama and pull base model
```bash
curl -fsSL https://ollama.com/install.sh | sh
# 8GB+ RAM:
ollama pull huihui_ai/qwen3.5-abliterated:9b
# <8GB RAM — use 4b and edit Modelfile FROM line accordingly:
ollama pull huihui_ai/qwen3.5-abliterated:4b
```
### 4. Build the custom metatron-qwen model
```bash
ollama create metatron-qwen -f Modelfile
ollama list # verify metatron-qwen appears
```
**Modelfile** (the repo ships this — key parameters):
```
FROM huihui_ai/qwen3.5-abliterated:9b
PARAMETER num_ctx 16384
PARAMETER temperature 0.7
PARAMETER top_k 10
PARAMETER top_p 0.9
```
To use 4b instead, edit `Modelfile`:
```
FROM huihui_ai/qwen3.5-abliterated:4b
```
Then rebuild: `ollama create metatron-qwen -f Modelfile`
### 5. Set up MariaDB
```bash
sudo systemctl start mariadb
sudo systemctl enable mariadb
mysql -u root
```
```sql
CREATE DATABASE metatron;
CREATE USER 'metatron'@'localhost' IDENTIFIED BY '123';
GRANT ALL PRIVILEGES ON metatron.* TO 'metatron'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```
Create all tables:
```bash
mysql -u metatron -p123 metatron < schema.sql
```
Or manually (paste from README schema block). The 5 tables:
- `history` — one row per scan session (spine)
- `vulnerabilities` — findings per session
- `fixes` — remediation per vulnerability
- `exploits_attempted` — exploit attempts per session
- `summary` — raw scan + full AI analysis dump
---
## Running METATRON
METATRON requires **two terminals**:
**Terminal 1 — Load model into memory:**
```bash
ollama run metatron-qwen
# Wait for >>> prompt before proceeding
```
**Terminal 2 — Launch the assistant:**
```bash
cd ~/METATRON
source venv/bin/activate
python metatron.py
```
### Main Menu Flow
```
[1] New Scan → enter target IP/domain → select tools → AI analyzes → saved to DB
[2] View History → browse past scans → view/edit/delete/export
[3] Exit
```
### New Scan — Tool Selection
```
[1] nmap
[2] whois
[3] whatweb
[4] curl headers
[5] dig DNS
[6] nikto
[a] Run all (except nikto)
[n] Run all + nikto (slow, thorough)
```
### Exporting Reports
From **View History → select scan → export**:
- `PDF` — professional vulnerability report
- `HTML` — browser-viewable report
---
## Code Examples
### Programmatically run a scan and save to DB (`db.py` patterns)
```python
import mysql.connector
def get_db_connection():
return mysql.connector.connect(
host="localhost",
user="metatron",
password="123",
database="metatron"
)
def create_scan_session(target: str) -> int:
"""Create a new history entry, return sl_no."""
from datetime import datetime
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"INSERT INTO history (target, scan_date, status) VALUES (%s, %s, %s)",
(target, datetime.now(), "active")
)
conn.commit()
sl_no = cursor.lastrowid
cursor.close()
conn.close()
return sl_no
def save_vulnerability(sl_no: int, vuln_name: str, severity: str,
port: str, service: str, description: str) -> int:
"""Save a vulnerability finding, return vuln id."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"""INSERT INTO vulnerabilities
(sl_no, vuln_name, severity, port, service, description)
VALUES (%s, %s, %s, %s, %s, %s)""",
(sl_no, vuln_name, severity, port, service, description)
)
conn.commit()
vuln_id = cursor.lastrowid
cursor.close()
conn.close()
return vuln_id
def save_fix(sl_no: int, vuln_id: int, fix_text: str, source: str = "AI"):
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"INSERT INTO fixes (sl_no, vuln_id, fix_text, source) VALUES (%s, %s, %s, %s)",
(sl_no, vuln_id, fix_text, source)
)
conn.commit()
cursor.close()
conn.close()
def save_summary(sl_no: int, raw_scan: str, ai_analysis: str, risk_level: str):
from datetime import datetime
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"""INSERT INTO summary (sl_no, raw_scan, ai_analysis, risk_level, generated_at)
VALUES (%s, %s, %s, %s, %s)""",
(sl_no, raw_scan, ai_analysis, risk_level, datetime.now())
)
conn.commit()
cursor.close()
conn.close()
def get_scan_history():
"""Retrieve all scan sessions."""
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM history ORDER BY scan_date DESC")
rows = cursor.fetchall()
cursor.close()
conn.close()
return rows
def get_vulnerabilities_for_scan(sl_no: int):
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute(
"SELECT * FROM vulnerabilities WHERE sl_no = %s", (sl_no,)
)
rows = cursor.fetchall()
cursor.close()
conn.close()
return rows
```
### Running recon tools (`tools.py` patterns)
```python
import subprocess
def run_nmap(target: str) -> str:
"""Run nmap service/version scan."""
result = subprocess.run(
["nmap", "-sV", "-sC", "-T4", target],
capture_output=True, text=True, timeout=120
)
return result.stdout + result.stderr
def run_whois(target: str) -> str:
result = subprocess.run(
["whois", target],
capture_output=True, text=True, timeout=30
)
return result.stdout
def run_whatweb(target: str) -> str:
result = subprocess.run(
["whatweb", "-a", "3", target],
capture_output=True, text=True, timeout=60
)
return result.stdout
def run_curl_headers(target: str) -> str:
result = subprocess.run(
["curl", "-I", "-L", "--max-time", "15", target],
capture_output=True, text=True, timeout=20
)
return result.stdout
def run_dig(target: str) -> str:
result = subprocess.run(
["dig", target, "ANY"],
capture_output=True, text=True, timeout=15
)
return result.stdout
def run_nikto(target: str) -> str:
"""Slow but thorough web scanner."""
result = subprocess.run(
["nikto", "-h", target],
capture_output=True, text=True, timeout=300
)
return result.stdout
def run_selected_tools(target: str, selections: list) -> dict:
"""
selections: list of tool names, e.g. ['nmap', 'whois', 'dig']
Returns dict of {tool_name: output}
"""
tool_map = {
'nmap': run_nmap,
'whois': run_whois,
'whatweb': run_whatweb,
'curl': run_curl_headers,
'dig': run_dig,
'nikto': run_nikto,
}
results = {}
for tool in selections:
if tool in tool_map:
print(f"[*] Running {tool} on {target}...")
try:
results[tool] = tool_map[tool](target)
except subprocess.TimeoutExpired:
results[tool] = f"[TIMEOUT] {tool} timed out"
except Exception as e:
results[tool] = f"[ERROR] {tool}: {e}"
return results
```
### Querying Ollama LLM (`llm.py` patterns)
```python
import requests
import json
OLLAMA_URL = "http://localhost:11434/api/generate"
MODEL_NAME = "metatron-qwen"
def query_llm(prompt: str, stream: bool = True) -> str:
"""Send prompt to metatron-qwen, return full response."""
payload = {
"model": MODEL_NAME,
"prompt": prompt,
"stream": stream
}
response = requests.post(OLLAMA_URL, json=payload, stream=stream)
if not stream:
return response.json().get("response", "")
full_response = ""
for line in response.iter_lines():
if line:
chunk = json.loads(line)
token = chunk.get("response", "")
print(token, end="", flush=True)
full_response += token
if chunk.get("done"):
break
print()
return full_response
def build_pentest_prompt(target: str, scan_results: dict) -> str:
"""Build the analysis prompt from scan results."""
combined = "\n\n".join(
f"=== {tool.upper()} ===\n{output}"
for tool, output in scan_results.items()
)
return f"""You are an expert penetration tester analyzing scan results for: {target}
SCAN RESULTS:
{combined}
Provide a structured analysis covering:
1. VULNERABILITIES FOUND — name, severity (Critical/High/Medium/Low), port, service, description
2. EXPLOIT SUGGESTIONS — specific tools or techniques for each vulnerability
3. RECOMMENDED FIXES — actionable remediation steps
4. OVERALL RISK LEVEL — Critical / High / Medium / Low
Format vulnerabilities as:
VULN: <name> | SEVERITY: <level> | PORT: <port> | SERVICE: <service>
DESC: <description>
FIX: <remediation>
"""
def analyze_target(target: str, scan_results: dict) -> str:
prompt = build_pentest_prompt(target, scan_results)
print("\n[🤖] metatron-qwen analyzing...\n")
return query_llm(prompt)
```
### DuckDuckGo search and CVE lookup (`search.py` patterns)
```python
from duckduckgo_search import DDGS
def search_exploits(query: str, max_results: int = 5) -> list:
"""Search DuckDuckGo for exploit info — no API key needed."""
with DDGS() as ddgs:
results = list(ddgs.text(query, max_results=max_results))
return results
def lookup_cve(cve_id: str) -> list:
"""Look up a CVE identifier."""
query = f"{cve_id} vulnerability exploit details"
return search_exploits(query)
def search_service_vulns(service: str, version: str) -> list:
query = f"{service} {version} known vulnerabilities CVE exploit"
return search_exploits(query)
# Usage example:
# results = lookup_cve("CVE-2021-44228")
# results = search_service_vulns("Apache", "2.4.49")
```
### Full scan pipeline (end-to-end)
```python
from tools import run_selected_tools
from llm import analyze_target
from db import (create_scan_session, save_vulnerability,
save_fix, save_summary)
def run_full_scan(target: str, tools: list = None):
if tools is None:
tools = ['nmap', 'whois', 'whatweb', 'curl', 'dig']
# 1. Create DB session
sl_no = create_scan_session(target)
print(f"[+] Scan session #{sl_no} created for {target}")
# 2. Run recon
scan_results = run_selected_tools(target, tools)
raw_scan = "\n\n".join(f"{k}:\n{v}" for k, v in scan_results.items())
# 3. AI analysis
ai_output = analyze_target(target, scan_results)
# 4. Parse and save (simplified — real parser in llm.py)
# Save summary
save_summary(sl_no, raw_scan, ai_output, risk_level="High")
print(f"\n[✓] Results saved to database (sl_no={sl_no})")
return sl_no, ai_output
# Run it:
# sl_no, analysis = run_full_scan("192.168.1.1", ['nmap', 'whois'])
```
---
## Common Patterns
### Check if Ollama model is running before scan
```python
import requests
def check_ollama_ready(model: str = "metatron-qwen") -> bool:
try:
resp = requests.get("http://localhost:11434/api/tags", timeout=5)
models = [m["name"] for m in resp.json().get("models", [])]
return any(model in m for m in models)
except Exception:
return False
if not check_ollama_ready():
print("[!] metatron-qwen not found. Run: ollama run metatron-qwen")
exit(1)
```
### Query scan history
```python
from db import get_db_connection
def get_full_scan_report(sl_no: int) -> dict:
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM history WHERE sl_no = %s", (sl_no,))
history = cursor.fetchone()
cursor.execute("SELECT * FROM vulnerabilities WHERE sl_no = %s", (sl_no,))
vulns = cursor.fetchall()
cursor.execute("SELECT * FROM summary WHERE sl_no = %s", (sl_no,))
summary = cursor.fetchone()
cursor.close()
conn.close()
return {"history": history, "vulnerabilities": vulns, "summary": summary}
```
### Add a custom recon tool
```python
# In tools.py — add your tool function:
def run_gobuster(target: str, wordlist: str = "/usr/share/wordlists/dirb/common.txt") -> str:
result = subprocess.run(
["gobuster", "dir", "-u", f"http://{target}", "-w", wordlist],
capture_output=True, text=True, timeout=180
)
return result.stdout
# Register it in the tool_map in run_selected_tools():
tool_map['gobuster'] = run_gobuster
```
---
## Troubleshooting
### `metatron-qwen` not found / connection refused
```bash
# Terminal 1: ensure model is loaded
ollama run metatron-qwen
# Should show >>> prompt
# Verify Ollama API is reachable
curl http://localhost:11434/api/tags
```
### Out of memory when running 9b model
```bash
# Switch to 4b: edit Modelfile first line:
# FROM huihui_ai/qwen3.5-abliterated:4b
ollama create metatron-qwen -f Modelfile
```
### MariaDB connection error
```bash
sudo systemctl status mariadb
sudo systemctl start mariadb
# Verify credentials work:
mysql -u metatron -p123 metatron -e "SHOW TABLES;"
```
### `mysql.connector` not found
```bash
source venv/bin/activate
pip install mysql-connector-python
```
### nmap requires root for SYN scan
```bash
sudo nmap -sV -sC -T4 <target>
# Or use TCP connect scan (no root needed):
nmap -sT -sV <target>
```
### Nikto timeout
Nikto is slow by design. Either use `[a]` (all without nikto) or increase the subprocess timeout in `tools.py`:
```python
result = subprocess.run(["nikto", "-h", target],
capture_output=True, text=True,
timeout=600) # 10 minutes
```
### Slow AI responses
The 9b model needs time to load. If response is slow after the first query, it's still loading. Subsequent queries will be faster. Ensure no other GPU/memory-heavy processes are running.
---
## Configuration Reference
| Setting | Location | Default | Notes |
|---|---|---|---|
| DB host | `db.py` | `localhost` | Change for remote DB |
| DB user | `db.py` | `metatron` | Match SQL user created |
| DB password | `db.py` | `123` | Change in production |
| DB name | `db.py` | `metatron` | |
| Ollama URL | `llm.py` | `http://localhost:11434` | |
| Model name | `llm.py` | `metatron-qwen` | Must match `ollama list` |
| Context window | `Modelfile` | `16384` | Increase for large scans |
| Temperature | `Modelfile` | `0.7` | Lower = more deterministic |
> **Security note:** For production use, replace the hardcoded DB password with an environment variable: `os.environ.get("METATRON_DB_PASSWORD", "123")`
---
## Legal Disclaimer
METATRON is for **educational purposes and authorized penetration testing only**. Only scan systems you own or have explicit written permission to test. Unauthorized scanning is illegal.Related Skills
shannon-ai-pentester
Autonomous white-box AI pentester for web applications and APIs using source code analysis and live exploit execution
picoclaw-ai-assistant
Ultra-lightweight AI assistant in Go that runs on $10 hardware with <10MB RAM, supporting multiple LLM providers, tools, and single-binary deployment across RISC-V, ARM, MIPS, and x86.
gstack-workflow-assistant
Team of specialist AI workflows for Claude Code with CEO review, engineering planning, code review, shipping, QA testing, and browser automation
copaw-ai-assistant
Personal AI assistant framework supporting multiple chat channels (DingTalk, Feishu, QQ, Discord, etc.) with extensible skills, local/cloud deployment, and cron scheduling.
cairn-ai-pentest
AI-automated penetration testing and general problem-solving system that achieved unique AK (All Killed) in Tencent Cloud Hackathon intelligent penetration challenge
```markdown
---
zeroboot-vm-sandbox
Sub-millisecond VM sandboxes for AI agents using copy-on-write KVM forking via Zeroboot
yourvpndead-vpn-detection
Android app that detects VPN/proxy servers (VLESS/xray/sing-box) via local SOCKS5 vulnerability, exposing exit IPs and server configs without root
xata-postgres-platform
Expert skill for Xata open-source cloud-native Postgres platform with copy-on-write branching, scale-to-zero, and Kubernetes deployment
x-mentor-skill-nuwa
AI-powered X (Twitter) content strategy skill that distills methodologies from 6 top creators + open-source algorithm data into actionable writing, growth, and monetization guidance.
wx-favorites-report
End-to-end pipeline to extract, decrypt, and visualize WeChat Mac favorites from encrypted SQLite DB into an interactive HTML report.
wterm-web-terminal
Web terminal emulator with Zig/WASM core, DOM rendering, and React/vanilla JS bindings