moriarty-project 0.1.7__py3-none-any.whl → 0.1.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moriarty/__init__.py +1 -2
- moriarty/cli/app.py +98 -2
- moriarty/cli/domain_cmd.py +118 -21
- moriarty/modules/domain_scanner.py +7 -1
- moriarty/modules/port_scanner.py +807 -80
- {moriarty_project-0.1.7.dist-info → moriarty_project-0.1.8.dist-info}/METADATA +4 -1
- {moriarty_project-0.1.7.dist-info → moriarty_project-0.1.8.dist-info}/RECORD +9 -9
- {moriarty_project-0.1.7.dist-info → moriarty_project-0.1.8.dist-info}/WHEEL +0 -0
- {moriarty_project-0.1.7.dist-info → moriarty_project-0.1.8.dist-info}/entry_points.txt +0 -0
moriarty/__init__.py
CHANGED
moriarty/cli/app.py
CHANGED
@@ -24,9 +24,23 @@ app = typer.Typer(
|
|
24
24
|
)
|
25
25
|
|
26
26
|
|
27
|
+
def version_callback(value: bool):
|
28
|
+
if value:
|
29
|
+
from .. import __version__
|
30
|
+
console.print(f"Moriarty OSINT Toolkit v{__version__}")
|
31
|
+
raise typer.Exit()
|
32
|
+
|
27
33
|
@app.callback()
|
28
34
|
def main(
|
29
35
|
ctx: typer.Context,
|
36
|
+
version: bool = typer.Option(
|
37
|
+
None,
|
38
|
+
"--version",
|
39
|
+
"-v",
|
40
|
+
callback=version_callback,
|
41
|
+
is_eager=True,
|
42
|
+
help="Show version and exit.",
|
43
|
+
),
|
30
44
|
concurrency: int = typer.Option(50, min=1, help="Maximum concurrent tasks."),
|
31
45
|
timeout: float = typer.Option(8.0, min=0.1, help="Per-request timeout in seconds."),
|
32
46
|
proxy: str | None = typer.Option(None, help="HTTP/SOCKS proxy URI."),
|
@@ -46,7 +60,7 @@ def main(
|
|
46
60
|
),
|
47
61
|
output: str | None = typer.Option(None, help="Path to export artifacts."),
|
48
62
|
redact: bool = typer.Option(True, "--redact/--no-redact", help="Redact PII in output."),
|
49
|
-
verbose: bool = typer.Option(False, help="Enable verbose logging."),
|
63
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging."),
|
50
64
|
quiet: bool = typer.Option(False, help="Suppress non-critical output."),
|
51
65
|
professional_mode: bool = typer.Option(False, help="Enable professional mode safeguards."),
|
52
66
|
seed: int | None = typer.Option(None, help="Deterministic seed for planners."),
|
@@ -117,4 +131,86 @@ def main() -> None: # Console script compatibility
|
|
117
131
|
main_entry()
|
118
132
|
|
119
133
|
|
120
|
-
|
134
|
+
def check_pipx_installed() -> bool:
|
135
|
+
"""Verifica se o pipx está instalado."""
|
136
|
+
try:
|
137
|
+
import subprocess
|
138
|
+
subprocess.run(["pipx", "--version"], capture_output=True, check=True)
|
139
|
+
return True
|
140
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
141
|
+
return False
|
142
|
+
|
143
|
+
|
144
|
+
def self_update():
|
145
|
+
"""Atualiza o Moriarty para a versão mais recente."""
|
146
|
+
import subprocess
|
147
|
+
import sys
|
148
|
+
from rich.console import Console
|
149
|
+
from rich.panel import Panel
|
150
|
+
from rich import print
|
151
|
+
|
152
|
+
console = Console()
|
153
|
+
|
154
|
+
try:
|
155
|
+
# Verifica se está instalado com pipx
|
156
|
+
if check_pipx_installed():
|
157
|
+
console.print("🔄 Atualizando via pipx...", style="bold blue")
|
158
|
+
result = subprocess.run(
|
159
|
+
["pipx", "install", "--upgrade", "moriarty-project"],
|
160
|
+
capture_output=True,
|
161
|
+
text=True
|
162
|
+
)
|
163
|
+
else:
|
164
|
+
console.print("🔄 Atualizando via pip...", style="bold blue")
|
165
|
+
result = subprocess.run(
|
166
|
+
[sys.executable, "-m", "pip", "install", "--upgrade", "moriarty-project"],
|
167
|
+
capture_output=True,
|
168
|
+
text=True
|
169
|
+
)
|
170
|
+
|
171
|
+
if result.returncode == 0:
|
172
|
+
console.print("✅ [bold green]Atualização concluída com sucesso![/]")
|
173
|
+
if result.stdout:
|
174
|
+
console.print(Panel(
|
175
|
+
result.stdout.strip(),
|
176
|
+
title="[bold]Saída do comando[/]",
|
177
|
+
border_style="green"
|
178
|
+
))
|
179
|
+
console.print("\nReinicie o terminal para aplicar as alterações.", style="yellow")
|
180
|
+
else:
|
181
|
+
console.print("❌ [bold red]Erro durante a atualização:[/]")
|
182
|
+
if result.stderr:
|
183
|
+
console.print(Panel(
|
184
|
+
result.stderr.strip(),
|
185
|
+
title="[bold]Mensagem de erro[/]",
|
186
|
+
border_style="red"
|
187
|
+
))
|
188
|
+
console.print("\nTente executar manualmente:")
|
189
|
+
if check_pipx_installed():
|
190
|
+
console.print(" [cyan]pipx install --upgrade moriarty-project[/]")
|
191
|
+
else:
|
192
|
+
console.print(f" [cyan]{sys.executable} -m pip install --upgrade moriarty-project[/]")
|
193
|
+
except Exception as e:
|
194
|
+
console.print(f"❌ [bold red]Erro inesperado:[/] {str(e)}")
|
195
|
+
console.print("\nTente atualizar manualmente usando:")
|
196
|
+
if check_pipx_installed():
|
197
|
+
console.print(" [cyan]pipx install --upgrade moriarty-project[/]")
|
198
|
+
else:
|
199
|
+
console.print(f" [cyan]{sys.executable} -m pip install --upgrade moriarty-project[/]")
|
200
|
+
|
201
|
+
|
202
|
+
# Adiciona o comando self-update
|
203
|
+
@app.command("self-update", help="Atualiza o Moriarty para a versão mais recente.")
|
204
|
+
def self_update_command():
|
205
|
+
"""Atualiza o Moriarty para a versão mais recente."""
|
206
|
+
self_update()
|
207
|
+
|
208
|
+
|
209
|
+
# Adiciona um alias mais curto
|
210
|
+
@app.command("update", hidden=True)
|
211
|
+
def update_alias():
|
212
|
+
"""Alias para 'self-update'."""
|
213
|
+
self_update()
|
214
|
+
|
215
|
+
|
216
|
+
__all__ = ["app", "main", "self_update"]
|
moriarty/cli/domain_cmd.py
CHANGED
@@ -25,6 +25,7 @@ def scan_full(
|
|
25
25
|
threads: int = typer.Option(10, "--threads", "-t", help="Threads concorrentes"),
|
26
26
|
timeout: int = typer.Option(30, "--timeout", help="Timeout em segundos"),
|
27
27
|
output: str = typer.Option(None, "--output", "-o", help="Arquivo de saída"),
|
28
|
+
verbose: bool = typer.Option(False, "--verbose", help="Ativar saída detalhada"),
|
28
29
|
):
|
29
30
|
"""
|
30
31
|
🔍 Scan completo de domínio/IP com 14 módulos.
|
@@ -49,6 +50,7 @@ def scan_full(
|
|
49
50
|
stealth_level=stealth,
|
50
51
|
threads=threads,
|
51
52
|
timeout=timeout,
|
53
|
+
verbose=verbose,
|
52
54
|
)
|
53
55
|
|
54
56
|
asyncio.run(scanner.run())
|
@@ -284,6 +286,7 @@ def port_scan(
|
|
284
286
|
scan_type: str = typer.Option("syn", "--type", "-t", help="Tipo: syn, tcp, udp"),
|
285
287
|
stealth: int = typer.Option(0, "--stealth", "-s", help="Stealth level (0-4)"),
|
286
288
|
output: str = typer.Option(None, "--output", "-o", help="Arquivo de saída"),
|
289
|
+
verbose: bool = typer.Option(False, "--verbose", help="Ativar saída detalhada"),
|
287
290
|
):
|
288
291
|
"""
|
289
292
|
🔌 Scan avançado de portas.
|
@@ -496,39 +499,133 @@ def passive_recon(
|
|
496
499
|
|
497
500
|
@app.command("ports")
|
498
501
|
def port_scan(
|
499
|
-
|
500
|
-
profile: str = typer.Option(
|
501
|
-
|
502
|
-
|
503
|
-
|
502
|
+
target: str = typer.Argument(..., help="Domínio ou IP para escanear"),
|
503
|
+
profile: str = typer.Option(
|
504
|
+
"quick",
|
505
|
+
"--profile",
|
506
|
+
"-p",
|
507
|
+
help=f"Perfil de varredura: {', '.join(PROFILES.keys())}"
|
508
|
+
),
|
509
|
+
stealth: int = typer.Option(
|
510
|
+
0,
|
511
|
+
"--stealth",
|
512
|
+
"-s",
|
513
|
+
min=0,
|
514
|
+
max=5,
|
515
|
+
help="Nível de stealth (0-5, maior = mais lento e discreto)",
|
516
|
+
),
|
517
|
+
concurrency: int = typer.Option(
|
518
|
+
200,
|
519
|
+
"--concurrency",
|
520
|
+
"-c",
|
521
|
+
help="Número de conexões simultâneas",
|
522
|
+
min=1,
|
523
|
+
max=1000,
|
524
|
+
),
|
525
|
+
timeout: float = typer.Option(
|
526
|
+
2.0,
|
527
|
+
"--timeout",
|
528
|
+
"-t",
|
529
|
+
help="Timeout por porta em segundos",
|
530
|
+
min=0.5,
|
531
|
+
max=30.0,
|
532
|
+
),
|
533
|
+
output: Optional[str] = typer.Option(
|
534
|
+
None,
|
535
|
+
"--output",
|
536
|
+
"-o",
|
537
|
+
help="Arquivo de saída (formato: .json, .txt, .md)"
|
538
|
+
),
|
539
|
+
format: str = typer.Option(
|
540
|
+
"text",
|
541
|
+
"--format",
|
542
|
+
"-f",
|
543
|
+
help="Formato de saída: text, json, markdown",
|
544
|
+
),
|
545
|
+
resolve_services: bool = typer.Option(
|
546
|
+
True,
|
547
|
+
"--resolve/--no-resolve",
|
548
|
+
help="Tentar identificar serviços nas portas abertas",
|
549
|
+
),
|
550
|
+
check_vulns: bool = typer.Option(
|
551
|
+
True,
|
552
|
+
"--vulns/--no-vulns",
|
553
|
+
help="Verificar vulnerabilidades conhecidas",
|
554
|
+
),
|
504
555
|
):
|
505
|
-
"""
|
556
|
+
"""
|
557
|
+
🔍 Varredura avançada de portas com detecção de serviços e vulnerabilidades.
|
558
|
+
|
559
|
+
Exemplos:
|
560
|
+
moriarty domain ports example.com
|
561
|
+
moriarty domain ports 192.168.1.1 --profile full --stealth 3
|
562
|
+
moriarty domain ports target.com -o resultado.json --format json
|
563
|
+
"""
|
506
564
|
import asyncio
|
507
565
|
import json
|
566
|
+
from pathlib import Path
|
567
|
+
from moriarty.modules.port_scanner import format_scan_results
|
568
|
+
|
569
|
+
# Validações
|
570
|
+
if profile not in PROFILES:
|
571
|
+
console.print(f"[red]Erro:[/red] Perfil inválido. Use um destes: {', '.join(PROFILES.keys())}")
|
572
|
+
raise typer.Exit(1)
|
573
|
+
|
574
|
+
if output:
|
575
|
+
output = Path(output)
|
576
|
+
if output.suffix not in (".json", ".txt", ".md"):
|
577
|
+
console.print("[yellow]Aviso:[/yellow] Extensão de arquivo não suportada. Usando .json")
|
578
|
+
output = output.with_suffix(".json")
|
508
579
|
|
509
580
|
async def _run():
|
510
581
|
scanner = PortScanner(
|
511
|
-
target=
|
582
|
+
target=target,
|
512
583
|
profile=profile,
|
513
584
|
concurrency=concurrency,
|
514
585
|
timeout=timeout,
|
586
|
+
stealth_level=stealth,
|
587
|
+
resolve_services=resolve_services,
|
588
|
+
check_vulns=check_vulns,
|
515
589
|
)
|
516
590
|
return await scanner.scan()
|
517
591
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
592
|
+
# Executa o scanner
|
593
|
+
console.print(f"[bold]🔍 Iniciando varredura em:[/bold] {target}")
|
594
|
+
console.print(f"📊 Perfil: {profile} (portas: {len(PROFILES[profile])})")
|
595
|
+
console.print(f"🕵️ Nível de stealth: {stealth}")
|
596
|
+
|
597
|
+
try:
|
598
|
+
results = asyncio.run(_run())
|
599
|
+
|
600
|
+
if not results:
|
601
|
+
console.print("[yellow]⚠️ Nenhuma porta aberta detectada[/yellow]")
|
602
|
+
return
|
603
|
+
|
604
|
+
# Formata a saída
|
605
|
+
if format.lower() == "json":
|
606
|
+
output_text = json.dumps([r.to_dict() for r in results], indent=2)
|
607
|
+
if not output:
|
608
|
+
console.print_json(data=json.loads(output_text))
|
609
|
+
else:
|
610
|
+
output_text = format_scan_results(results, output_format=format)
|
611
|
+
if not output:
|
612
|
+
console.print(output_text)
|
613
|
+
|
614
|
+
# Salva em arquivo se especificado
|
615
|
+
if output:
|
616
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
617
|
+
with open(output, "w", encoding="utf-8") as f:
|
618
|
+
if output.suffix == ".json":
|
619
|
+
json.dump([r.to_dict() for r in results], f, indent=2, ensure_ascii=False)
|
620
|
+
else:
|
621
|
+
f.write(output_text)
|
622
|
+
console.print(f"\n[green]✅ Resultados salvos em:[/green] {output.absolute()}")
|
623
|
+
|
624
|
+
except Exception as e:
|
625
|
+
console.print(f"[red]❌ Erro durante a varredura:[/red] {str(e)}")
|
626
|
+
if "object NoneType can't be used in 'await' expression" in str(e):
|
627
|
+
console.print("[yellow]Dica:[/yellow] Tente aumentar o timeout com --timeout 5.0")
|
628
|
+
raise typer.Exit(1)
|
532
629
|
console.print(f"[green]✓ Resultado salvo em[/green] {output}")
|
533
630
|
|
534
631
|
|
@@ -76,17 +76,23 @@ class DomainScanner:
|
|
76
76
|
stealth_level: int = 0,
|
77
77
|
threads: int = 10,
|
78
78
|
timeout: int = 30,
|
79
|
+
verbose: bool = False,
|
79
80
|
):
|
80
81
|
self.target = target
|
81
82
|
self.modules = modules or self.DEFAULT_MODULES
|
82
83
|
self.stealth_level = stealth_level
|
83
84
|
self.threads = threads
|
84
85
|
self.timeout = timeout
|
86
|
+
self.verbose = verbose
|
85
87
|
self.result = ScanResult(target=target)
|
86
88
|
self.stealth = None
|
89
|
+
|
90
|
+
# Configura o nível de log com base no modo verbose
|
91
|
+
log_level = logging.DEBUG if verbose else logging.INFO
|
92
|
+
logging.basicConfig(level=log_level)
|
93
|
+
|
87
94
|
if self.stealth_level > 0:
|
88
95
|
from moriarty.modules.stealth_mode import StealthMode
|
89
|
-
|
90
96
|
self.stealth = StealthMode(level=self.stealth_level)
|
91
97
|
|
92
98
|
self.tech_profile: Optional[Dict[str, Any]] = None
|