moriarty-project 0.1.24__py3-none-any.whl → 0.1.26__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 -1
- moriarty/cli/domain_cmd.py +629 -119
- moriarty/modules/domain_scanner.py +182 -59
- moriarty/modules/port_scanner.py +312 -122
- moriarty/modules/port_scanner_nmap.py +290 -0
- moriarty/modules/web_crawler.py +774 -152
- {moriarty_project-0.1.24.dist-info → moriarty_project-0.1.26.dist-info}/METADATA +5 -3
- {moriarty_project-0.1.24.dist-info → moriarty_project-0.1.26.dist-info}/RECORD +10 -9
- {moriarty_project-0.1.24.dist-info → moriarty_project-0.1.26.dist-info}/WHEEL +0 -0
- {moriarty_project-0.1.24.dist-info → moriarty_project-0.1.26.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,290 @@
|
|
1
|
+
"""Port scanning avançado com detecção de serviços usando Nmap."""
|
2
|
+
from __future__ import annotations
|
3
|
+
|
4
|
+
import asyncio
|
5
|
+
import json
|
6
|
+
import re
|
7
|
+
import nmap
|
8
|
+
from dataclasses import dataclass, field
|
9
|
+
from datetime import datetime
|
10
|
+
from typing import Dict, List, Optional, Any, Set, Union
|
11
|
+
from pathlib import Path
|
12
|
+
import structlog
|
13
|
+
from rich.console import Console
|
14
|
+
from rich.table import Table, box
|
15
|
+
|
16
|
+
logger = structlog.get_logger(__name__)
|
17
|
+
console = Console()
|
18
|
+
|
19
|
+
# Perfis de varredura
|
20
|
+
PROFILES = {
|
21
|
+
"quick": "21-23,25,53,80,110,111,135,139,143,389,443,445,465,587,993,995,1433,1521,2049,3306,3389,5432,5900,6379,8080,8443,9000,10000,27017",
|
22
|
+
"web": "80,443,8080,8443,8000,8888,10443,4443",
|
23
|
+
"db": "1433,1521,27017-27019,28017,3306,5000,5432,5984,6379,8081",
|
24
|
+
"full": "1-1024",
|
25
|
+
"all": "1-65535",
|
26
|
+
}
|
27
|
+
|
28
|
+
@dataclass
|
29
|
+
class ServiceInfo:
|
30
|
+
"""Informações detalhadas sobre um serviço."""
|
31
|
+
name: str = "unknown"
|
32
|
+
version: Optional[str] = None
|
33
|
+
ssl: bool = False
|
34
|
+
ssl_info: Optional[Dict[str, Any]] = None
|
35
|
+
banner: Optional[str] = None
|
36
|
+
vulns: List[str] = field(default_factory=list)
|
37
|
+
cpe: Optional[str] = None
|
38
|
+
extra: Dict[str, Any] = field(default_factory=dict)
|
39
|
+
confidence: float = 0.0
|
40
|
+
last_checked: Optional[datetime] = field(default_factory=datetime.utcnow)
|
41
|
+
|
42
|
+
def to_dict(self) -> Dict[str, Any]:
|
43
|
+
"""Converte o objeto para dicionário."""
|
44
|
+
return {
|
45
|
+
"name": self.name,
|
46
|
+
"version": self.version,
|
47
|
+
"ssl": self.ssl,
|
48
|
+
"ssl_info": self.ssl_info,
|
49
|
+
"banner": self.banner,
|
50
|
+
"vulns": self.vulns,
|
51
|
+
"cpe": self.cpe,
|
52
|
+
"extra": self.extra,
|
53
|
+
"confidence": self.confidence,
|
54
|
+
"last_checked": self.last_checked.isoformat() if self.last_checked else None,
|
55
|
+
}
|
56
|
+
|
57
|
+
@dataclass
|
58
|
+
class PortScanResult:
|
59
|
+
"""Resultado da varredura de uma porta."""
|
60
|
+
port: int
|
61
|
+
protocol: str = "tcp"
|
62
|
+
status: str = "closed"
|
63
|
+
target: Optional[str] = None
|
64
|
+
service: Optional[ServiceInfo] = None
|
65
|
+
banner: Optional[str] = None
|
66
|
+
timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
|
67
|
+
|
68
|
+
def to_dict(self) -> Dict[str, Any]:
|
69
|
+
"""Converte o resultado para dicionário."""
|
70
|
+
return {
|
71
|
+
"port": self.port,
|
72
|
+
"protocol": self.protocol,
|
73
|
+
"status": self.status,
|
74
|
+
"target": self.target,
|
75
|
+
"service": self.service.to_dict() if self.service else None,
|
76
|
+
"banner": self.banner,
|
77
|
+
"timestamp": self.timestamp,
|
78
|
+
}
|
79
|
+
|
80
|
+
def to_json(self) -> str:
|
81
|
+
"""Retorna uma representação JSON do resultado."""
|
82
|
+
return json.dumps(self.to_dict(), indent=2)
|
83
|
+
|
84
|
+
class PortScanner:
|
85
|
+
"""Execução de port scanning com detecção avançada de serviços usando Nmap."""
|
86
|
+
|
87
|
+
def __init__(
|
88
|
+
self,
|
89
|
+
target: str,
|
90
|
+
ports: Union[str, List[int], None] = None,
|
91
|
+
scan_type: str = "syn",
|
92
|
+
stealth_level: int = 0,
|
93
|
+
resolve_services: bool = True,
|
94
|
+
check_vulns: bool = True,
|
95
|
+
):
|
96
|
+
self.target = target
|
97
|
+
self.ports = self._parse_ports(ports) if ports else PROFILES["all"]
|
98
|
+
self.scan_type = scan_type if scan_type in ["syn", "tcp", "udp", "sS", "sT", "sU"] else "sS"
|
99
|
+
self.stealth_level = max(0, min(stealth_level, 5))
|
100
|
+
self.resolve_services = resolve_services
|
101
|
+
self.check_vulns = check_vulns
|
102
|
+
self.nm = nmap.PortScanner()
|
103
|
+
|
104
|
+
# Configurações baseadas no nível de stealth
|
105
|
+
self.scan_arguments = self._get_scan_arguments()
|
106
|
+
|
107
|
+
def _parse_ports(self, ports: Union[str, List[int]]) -> str:
|
108
|
+
"""Converte diferentes formatos de portas para o formato do Nmap."""
|
109
|
+
if isinstance(ports, list):
|
110
|
+
return ",".join(str(p) for p in ports)
|
111
|
+
elif ports in PROFILES:
|
112
|
+
return PROFILES[ports]
|
113
|
+
return ports
|
114
|
+
|
115
|
+
def _get_scan_arguments(self) -> str:
|
116
|
+
"""Gera os argumentos do Nmap baseados nas configurações."""
|
117
|
+
# Argumentos base - removendo -O (OS detection) e usando -T4 para velocidade
|
118
|
+
args = "-T4 -sT" # Usando TCP Connect por padrão sem necessidade de root
|
119
|
+
|
120
|
+
# Adiciona detecção de versão se necessário
|
121
|
+
if self.resolve_services:
|
122
|
+
args += " -sV"
|
123
|
+
|
124
|
+
# Adiciona scripts padrão se não for muito furtivo
|
125
|
+
if self.stealth_level < 3:
|
126
|
+
args += " -sC"
|
127
|
+
|
128
|
+
# Adiciona argumentos de furtividade baseado no nível
|
129
|
+
if self.stealth_level > 0:
|
130
|
+
args += f" --max-rtt-timeout {1000 - (self.stealth_level * 100)}ms"
|
131
|
+
args += f" --scan-delay {self.stealth_level * 2}s"
|
132
|
+
|
133
|
+
return args.strip()
|
134
|
+
|
135
|
+
@staticmethod
|
136
|
+
def render_pipe_summary(results: List[PortScanResult]) -> str:
|
137
|
+
if not results:
|
138
|
+
return "Nenhuma porta aberta encontrada."
|
139
|
+
headers = ["PORTA", "STATUS", "SERVICO", "PROTOCOLO", "VERSAO", "SSL"]
|
140
|
+
rows = []
|
141
|
+
for r in sorted(results, key=lambda x: (x.protocol, x.port)):
|
142
|
+
service_name = r.service.name if r.service else "unknown"
|
143
|
+
version = (r.service.version or "").strip() if r.service else ""
|
144
|
+
ssl = "✅" if (r.service and r.service.ssl) else ""
|
145
|
+
rows.append([
|
146
|
+
str(r.port), r.status, service_name or "unknown",
|
147
|
+
r.protocol.upper(), version, ssl
|
148
|
+
])
|
149
|
+
|
150
|
+
col_widths = [len(h) for h in headers]
|
151
|
+
for row in rows:
|
152
|
+
for i, cell in enumerate(row):
|
153
|
+
col_widths[i] = max(col_widths[i], len(cell))
|
154
|
+
|
155
|
+
def fmt_line(cells):
|
156
|
+
return " | ".join(cell.ljust(col_widths[i]) for i, cell in enumerate(cells))
|
157
|
+
|
158
|
+
out = [fmt_line(headers), " | ".join("-" * w for w in col_widths)]
|
159
|
+
out.extend(fmt_line(row) for row in rows)
|
160
|
+
return "\n".join(out)
|
161
|
+
|
162
|
+
async def scan(self) -> List[PortScanResult]:
|
163
|
+
"""Executa a varredura de portas usando Nmap."""
|
164
|
+
console.print(f"[bold]🔍 Iniciando varredura Nmap em {self.target}[/bold]")
|
165
|
+
console.print(f"📊 Portas: [bold]{self.ports}[/bold]")
|
166
|
+
console.print(f"⚙️ Argumentos: [bold]{self.scan_arguments}[/bold]")
|
167
|
+
|
168
|
+
if self.stealth_level > 0:
|
169
|
+
console.print(f"🔒 Modo furtivo: nível {self.stealth_level}")
|
170
|
+
|
171
|
+
try:
|
172
|
+
# Executa o scan Nmap
|
173
|
+
self.nm.scan(
|
174
|
+
hosts=self.target,
|
175
|
+
ports=self.ports,
|
176
|
+
arguments=self.scan_arguments,
|
177
|
+
sudo=False # Necessário para SYN scan
|
178
|
+
)
|
179
|
+
|
180
|
+
# Processa os resultados
|
181
|
+
results = []
|
182
|
+
for host in self.nm.all_hosts():
|
183
|
+
for proto in self.nm[host].all_protocols():
|
184
|
+
ports = self.nm[host][proto].keys()
|
185
|
+
|
186
|
+
for port in ports:
|
187
|
+
port_info = self.nm[host][proto][port]
|
188
|
+
|
189
|
+
# Cria o objeto ServiceInfo
|
190
|
+
service_info = ServiceInfo(
|
191
|
+
name=port_info.get('name', 'unknown'),
|
192
|
+
version=port_info.get('version', ''),
|
193
|
+
banner=port_info.get('product', ''),
|
194
|
+
ssl=port_info.get('tunnel') == 'ssl' or 'ssl' in port_info.get('name', '').lower()
|
195
|
+
)
|
196
|
+
|
197
|
+
# Adiciona CPE se disponível
|
198
|
+
if 'cpe' in port_info:
|
199
|
+
service_info.cpe = port_info['cpe']
|
200
|
+
|
201
|
+
state = port_info.get('state', 'closed')
|
202
|
+
if state != 'open':
|
203
|
+
continue
|
204
|
+
|
205
|
+
# Cria o resultado da porta
|
206
|
+
result = PortScanResult(
|
207
|
+
port=port,
|
208
|
+
protocol=proto,
|
209
|
+
status=state,
|
210
|
+
target=host,
|
211
|
+
service=service_info,
|
212
|
+
banner=port_info.get('product', '') + ' ' + port_info.get('version', '')
|
213
|
+
)
|
214
|
+
|
215
|
+
results.append(result)
|
216
|
+
|
217
|
+
# Ordena os resultados por número de porta
|
218
|
+
results.sort(key=lambda x: x.port)
|
219
|
+
|
220
|
+
# Exibe resumo
|
221
|
+
open_ports = len(results)
|
222
|
+
console.print(f"\n✅ [bold green]Varredura concluída![/bold green] {open_ports} portas abertas encontradas.")
|
223
|
+
|
224
|
+
# Mostra a visão tipo "PORTA | STATUS | SERVICO | ..."
|
225
|
+
console.print("\n[bold]Resumo das portas abertas[/bold]")
|
226
|
+
console.print(self.render_pipe_summary(results))
|
227
|
+
|
228
|
+
return results
|
229
|
+
|
230
|
+
except Exception as e:
|
231
|
+
console.print(f"[bold red]❌ Erro ao executar Nmap: {str(e)}[/bold red]")
|
232
|
+
raise
|
233
|
+
|
234
|
+
def format_scan_results(results: List[PortScanResult], output_format: str = "text", total_ports: Optional[int] = None) -> str:
|
235
|
+
"""Formata os resultados da varredura no formato solicitado.
|
236
|
+
|
237
|
+
Args:
|
238
|
+
results: Lista de resultados da varredura
|
239
|
+
output_format: Formato de saída ('text' ou 'json')
|
240
|
+
total_ports: Número total de portas verificadas (opcional)
|
241
|
+
"""
|
242
|
+
if output_format == "json":
|
243
|
+
return json.dumps([r.to_dict() for r in results], indent=2)
|
244
|
+
|
245
|
+
if output_format == "pipe":
|
246
|
+
base = PortScanner.render_pipe_summary(results)
|
247
|
+
suffix = f"\n\n🔍 Total de portas abertas: {len(results)}"
|
248
|
+
if total_ports:
|
249
|
+
suffix = f"\n\n🔍 Resumo: {len(results)} portas abertas de {total_ports} verificadas"
|
250
|
+
return base + suffix
|
251
|
+
|
252
|
+
|
253
|
+
# Formato de texto
|
254
|
+
output = []
|
255
|
+
|
256
|
+
if not results:
|
257
|
+
return "Nenhuma porta aberta encontrada."
|
258
|
+
|
259
|
+
# Cria tabela
|
260
|
+
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
261
|
+
table.add_column("Porta", style="cyan", width=10)
|
262
|
+
table.add_column("Protocolo", style="blue")
|
263
|
+
table.add_column("Serviço", style="green")
|
264
|
+
table.add_column("Versão", style="yellow")
|
265
|
+
table.add_column("SSL/TLS", style="magenta")
|
266
|
+
|
267
|
+
for result in results:
|
268
|
+
service = result.service.name if result.service and hasattr(result.service, 'name') else "desconhecido"
|
269
|
+
version = result.service.version if result.service and hasattr(result.service, 'version') else ""
|
270
|
+
ssl = "✅" if result.service and result.service.ssl else ""
|
271
|
+
|
272
|
+
table.add_row(
|
273
|
+
f"{result.port}",
|
274
|
+
result.protocol.upper(),
|
275
|
+
service,
|
276
|
+
version,
|
277
|
+
ssl
|
278
|
+
)
|
279
|
+
|
280
|
+
output.append(str(table))
|
281
|
+
|
282
|
+
# Adiciona resumo
|
283
|
+
if total_ports:
|
284
|
+
output.append(f"\n🔍 [bold]Resumo:[/bold] {len(results)} portas abertas de {total_ports} verificadas")
|
285
|
+
else:
|
286
|
+
output.append(f"\n🔍 [bold]Total de portas abertas:[/bold] {len(results)}")
|
287
|
+
|
288
|
+
return "\n".join(output)
|
289
|
+
|
290
|
+
__all__ = ["PortScanner", "PortScanResult", "format_scan_results"]
|