Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions ttm_aggregate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@

import sys
import re
from pathlib import Path
from collections import defaultdict

CATEGORIES = {
"fetch": ["_URLs.txt"],
"subdomains": ["_subdomain.txt"],
"backups": ["_backups.txt"],
"archives": ["_archive.txt"],
"parameters": ["_parameters.txt"],
"jwt": ["_jwt.txt"],
"xss": ["_xss.txt"],
"sqli": ["_sqli.txt"],
"lfi": ["_lfi.txt"],
"redirect": ["_redirect.txt"],
"jira": ["_jira.txt"],
"wp": ["_wp.txt"],
"fuzz": ["_fuzz.txt"],
}

DOMAIN_RE = re.compile(r"^(?=.{1,253}$)(?!-)([a-z0-9-]{1,63}\.)+[a-z]{2,63}$")


def is_under_apex(domain: str, apex: str) -> bool:
return domain == apex or domain.endswith("." + apex)


def main():
if len(sys.argv) != 2:
print("Usage: python ttm_aggregate.py <apex-domain>")
sys.exit(1)

apex = sys.argv[1].lower().strip()
content_dir = Path("content")
output_dir = Path("output") / apex

if not content_dir.exists():
print("[!] content/ directory not found")
sys.exit(1)

output_dir.mkdir(parents=True, exist_ok=True)

buckets = defaultdict(set)

for subdir in content_dir.iterdir():
if not subdir.is_dir():
continue

domain = subdir.name.lower()

if not DOMAIN_RE.match(domain):
continue

if not is_under_apex(domain, apex):
continue

for file in subdir.glob("*.txt"):
for category, suffixes in CATEGORIES.items():
if any(file.name.endswith(sfx) for sfx in suffixes):
for line in file.read_text(errors="ignore").splitlines():
line = line.strip()
if line:
buckets[category].add(f"[{domain}] {line}")


for category, values in buckets.items():
if not values:
continue

out_file = output_dir / f"{category}_all.txt"
with out_file.open("w", encoding="utf-8") as f:
for entry in sorted(values):
f.write(entry + "\n")

print(f"[+] Written {out_file} ({len(values)} lines)")

print(f"\n[✓] Aggregation complete → {output_dir}")

if __name__ == "__main__":
main()

81 changes: 81 additions & 0 deletions ttm_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import subprocess
import sys
import re
from pathlib import Path


PIPELINE = [
["--fetch"],
["--backups"],
["--listings"],
["--parameters"],
["--jwt"],
["--attack", "xss"],
["--attack", "sqli"],
["--attack", "lfi"],
["--attack", "redirect"],
["--attack", "jira"],
["--attack", "wp"],
["--attack", "fuzz"],
["--subdomains"],
]

DOMAIN_RE = re.compile(r"^(?=.{1,253}$)(?!-)([a-z0-9-]{1,63}\.)+[a-z]{2,63}$")


def normalize(domain: str) -> str:
domain = domain.strip().lower()
domain = re.sub(r"^https?://", "", domain)
return domain.split("/", 1)[0].rstrip(".")

def load_subdomains(path: Path, apex: str):
results = set()
for line in path.read_text(errors="ignore").splitlines():
d = normalize(line)
if DOMAIN_RE.match(d) and (d == apex or d.endswith("." + apex)):
results.add(d)
return sorted(results)


def main():
if len(sys.argv) != 3:
print("Usage: python ttm_wrapper.py <apex-domain> <subdomains.txt>")
sys.exit(1)

apex = normalize(sys.argv[1])
sub_file = Path(sys.argv[2])

base_dir = Path(__file__).resolve().parent
ttm_script = base_dir / "thetimemachine.py"

if not ttm_script.exists():
print("[!] thetimemachine.py not found in this directory")
sys.exit(1)

targets = load_subdomains(sub_file, apex)

print(f"[+] Apex : {apex}")
print(f"[+] Targets : {len(targets)}")
print(f"[+] TTM Dir : {base_dir}")

for i, target in enumerate(targets, 1):
print(f"\n==============================")
print(f"[{i}/{len(targets)}] TARGET → {target}")
print(f"==============================")

for step in PIPELINE:
cmd = ["python", "thetimemachine.py", target] + step
print("\n[CMD]", " ".join(cmd))
print("-" * 60)

subprocess.run(
cmd,
cwd=base_dir
)

print("\n[✓] All scans completed")
print("[i] Check ./content/ for raw results")

if __name__ == "__main__":
main()