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.
Files changed (27) hide show
  1. moriarty/__init__.py +1 -1
  2. moriarty/cli/app.py +4 -3
  3. moriarty/cli/domain_cmd.py +5 -1
  4. moriarty/modules/directory_fuzzer.py +25 -5
  5. moriarty/modules/web_crawler.py +448 -91
  6. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/METADATA +3 -3
  7. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/RECORD +9 -27
  8. moriarty/modules/wifippler/__init__.py +0 -92
  9. moriarty/modules/wifippler/cli/__init__.py +0 -8
  10. moriarty/modules/wifippler/cli/commands.py +0 -123
  11. moriarty/modules/wifippler/core/__init__.py +0 -94
  12. moriarty/modules/wifippler/core/attacks/__init__.py +0 -146
  13. moriarty/modules/wifippler/core/attacks/deauth.py +0 -262
  14. moriarty/modules/wifippler/core/attacks/handshake.py +0 -402
  15. moriarty/modules/wifippler/core/attacks/pmkid.py +0 -424
  16. moriarty/modules/wifippler/core/attacks/wep.py +0 -467
  17. moriarty/modules/wifippler/core/attacks/wpa.py +0 -446
  18. moriarty/modules/wifippler/core/attacks/wps.py +0 -474
  19. moriarty/modules/wifippler/core/models/__init__.py +0 -10
  20. moriarty/modules/wifippler/core/models/network.py +0 -240
  21. moriarty/modules/wifippler/core/scanner.py +0 -903
  22. moriarty/modules/wifippler/core/utils/__init__.py +0 -624
  23. moriarty/modules/wifippler/core/utils/exec.py +0 -182
  24. moriarty/modules/wifippler/core/utils/network.py +0 -262
  25. moriarty/modules/wifippler/core/utils/system.py +0 -153
  26. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/WHEEL +0 -0
  27. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/entry_points.txt +0 -0
@@ -1,624 +0,0 @@
1
- """
2
- Módulo de utilidades do WiFiPPLER.
3
-
4
- Este módulo fornece funções auxiliares para operações comuns de rede e sistema.
5
- """
6
- import os
7
- import subprocess
8
- from typing import Optional, List, Dict, Any, Union
9
-
10
- # Importações principais
11
- from ..models.network import WiFiCipherType, WiFiAuthType, WiFiNetwork, WiFiClient
12
-
13
- from .network import (
14
- get_interface_ip,
15
- get_interface_netmask,
16
- get_interface_gateway,
17
- is_wireless_interface,
18
- get_network_interfaces,
19
- is_interface_up,
20
- get_interface_mac,
21
- get_monitor_interfaces
22
- )
23
-
24
- from .system import (
25
- is_root,
26
- check_dependencies,
27
- command_exists,
28
- ensure_root,
29
- ensure_dependencies,
30
- get_available_interfaces,
31
- get_wireless_interfaces
32
- )
33
-
34
- from .exec import (
35
- run_command,
36
- run_command_async,
37
- run_sudo_command,
38
- command_success,
39
- get_command_output,
40
- get_command_output_safe
41
- )
42
-
43
- # Configuração de logging
44
- import logging
45
- logging.basicConfig(level=logging.INFO)
46
- logger = logging.getLogger(__name__)
47
-
48
- # Exporta apenas o que é necessário para uso externo
49
- __all__ = [
50
- # Funções de rede
51
- 'get_interface_ip',
52
- 'get_interface_netmask',
53
- 'get_interface_gateway',
54
- 'is_wireless_interface',
55
- 'get_network_interfaces',
56
- 'is_interface_up',
57
- 'get_interface_mac',
58
-
59
- # Funções do sistema
60
- 'is_root',
61
- 'check_dependencies',
62
- 'command_exists',
63
- 'ensure_root',
64
- 'ensure_dependencies',
65
- 'get_available_interfaces',
66
- 'get_wireless_interfaces',
67
-
68
- # Funções de execução de comandos
69
- 'run_command',
70
- 'run_command_async',
71
- 'run_sudo_command',
72
- 'command_success',
73
- 'get_command_output',
74
- 'get_command_output_safe',
75
-
76
- # Logger
77
- 'logger'
78
- ]
79
- SIOCGIFNETMASK = 0x891B
80
- SIOCGIFBRDADDR = 0x8919
81
- SIOCGIFMTU = 0x8921
82
- SIOCGIFINDEX = 0x8933
83
- SIOCGIFNAME = 0x8910
84
- SIOCGIFFLAGS = 0x8913
85
- SIOCSIFFLAGS = 0x8914
86
-
87
- # O mapeamento de códigos de segurança foi movido para core/models/network.py
88
-
89
- # Mapeamento de cifras
90
- CIPHER_MAP = {
91
- 'CCMP': WiFiCipherType.CCMP,
92
- 'TKIP': WiFiCipherType.TKIP,
93
- 'WEP': WiFiCipherType.WEP_40, # Usando WEP_40 como padrão para WEP
94
- 'WEP-40': WiFiCipherType.WEP_40,
95
- 'WEP-104': WiFiCipherType.WEP_104,
96
- 'WEP-128': WiFiCipherType.WEP_104, # WEP-128 não está definido, usando WEP_104 como alternativa
97
- 'NONE': WiFiCipherType.NONE,
98
- 'GCMP': WiFiCipherType.GCMP
99
- }
100
-
101
- # Mapeamento de autenticação
102
- AUTH_MAP = {
103
- 'PSK': WiFiAuthType.WPA_PSK, # Usando WPA_PSK para PSK
104
- 'WPA-PSK': WiFiAuthType.WPA_PSK,
105
- 'WPA2-PSK': WiFiAuthType.WPA2_PSK,
106
- 'WPA3-SAE': WiFiAuthType.WPA3_SAE,
107
- 'EAP': WiFiAuthType.WPA_EAP, # Usando WPA_EAP para EAP
108
- 'WPA-EAP': WiFiAuthType.WPA_EAP,
109
- 'WPA2-EAP': WiFiAuthType.WPA2_EAP,
110
- 'WPA3-EAP': WiFiAuthType.WPA3_EAP,
111
- 'OPEN': WiFiAuthType.OPEN,
112
- 'SHARED': WiFiAuthType.SHARED,
113
- 'OWE': WiFiAuthType.OWE,
114
- 'NONE': WiFiAuthType.OPEN,
115
- }
116
-
117
- def is_root() -> bool:
118
- """Verifica se o script está sendo executado como root."""
119
- return os.geteuid() == 0
120
-
121
- def check_dependencies() -> List[str]:
122
- """Verifica as dependências necessárias."""
123
- required = ['iwconfig', 'iwlist', 'aircrack-ng', 'airodump-ng', 'aireplay-ng', 'airmon-ng']
124
- missing = []
125
-
126
- for dep in required:
127
- if not command_exists(dep):
128
- missing.append(dep)
129
-
130
-
131
- def command_exists(cmd: str) -> bool:
132
- """Verifica se um comando existe no sistema."""
133
- return shutil.which(cmd) is not None
134
-
135
- # A função get_wireless_interfaces já está definida acima, então não precisamos duplicá-la
136
-
137
- def run_command(cmd: Union[str, List[str]], capture_output: bool = False,
138
- check: bool = True, **kwargs) -> subprocess.CompletedProcess:
139
- """
140
- Executa um comando no shell com tratamento de erros.
141
-
142
- Args:
143
- cmd: Comando a ser executado (string ou lista)
144
- capture_output: Se deve capturar a saída padrão e de erro
145
- check: Se deve lançar uma exceção em caso de código de saída diferente de zero
146
- **kwargs: Argumentos adicionais para subprocess.run()
147
-
148
- Returns:
149
- subprocess.ClosedProcess: Resultado da execução do comando
150
- """
151
- if isinstance(cmd, str):
152
- cmd = cmd.split()
153
-
154
- kwargs.setdefault('stdout', subprocess.PIPE if capture_output else None)
155
- kwargs.setdefault('stderr', subprocess.PIPE if capture_output else None)
156
- kwargs.setdefault('text', True)
157
-
158
- try:
159
- return subprocess.run(cmd, check=check, **kwargs)
160
- except Exception as e:
161
- logger.error(f"Erro ao executar comando: {e}")
162
- raise
163
-
164
- def set_monitor_mode(interface: str, channel: int = None) -> bool:
165
- """Ativa o modo monitor em uma interface."""
166
- if not is_root():
167
- logger.error("Privilégios de root são necessários para ativar o modo monitor")
168
- return False
169
-
170
- try:
171
- # Para processos que podem interferir
172
- subprocess.run(['airmon-ng', 'check', 'kill'],
173
- stdout=subprocess.PIPE,
174
- stderr=subprocess.PIPE)
175
-
176
- # Ativa o modo monitor
177
- cmd = ['airmon-ng', 'start', interface]
178
- if channel:
179
- cmd.extend(['-c', str(channel)])
180
-
181
- result = subprocess.run(cmd,
182
- stdout=subprocess.PIPE,
183
- stderr=subprocess.PIPE,
184
- text=True)
185
-
186
- if result.returncode != 0:
187
- logger.error(f"Falha ao ativar o modo monitor: {result.stderr}")
188
- return False
189
-
190
- return True
191
-
192
- except subprocess.CalledProcessError as e:
193
- logger.error(f"Erro ao executar airmon-ng: {e}")
194
- return False
195
- except Exception as e:
196
- logger.error(f"Erro inesperado: {e}")
197
- return False
198
-
199
- def restore_network_interface(interface: str) -> bool:
200
- """Restaura a interface para o modo gerenciado."""
201
- if not is_root():
202
- logger.error("Privilégios de root são necessários para restaurar a interface")
203
- return False
204
-
205
- try:
206
- # Para o modo monitor
207
- subprocess.run(['airmon-ng', 'stop', interface], check=True)
208
-
209
- # Reinicia o gerenciador de rede
210
- if command_exists('systemctl'):
211
- subprocess.run(['systemctl', 'restart', 'NetworkManager'], check=False)
212
- elif command_exists('service'):
213
- subprocess.run(['service', 'network-manager', 'restart'], check=False)
214
-
215
- return True
216
- except subprocess.CalledProcessError as e:
217
- logger.error(f"Erro ao parar o modo monitor: {e}")
218
- return False
219
- except Exception as e:
220
- logger.error(f"Erro inesperado: {e}")
221
- return False
222
-
223
- def start_monitor_mode(interface: str, channel: int = None) -> Optional[str]:
224
- """Inicia o modo monitor em uma interface usando airmon-ng."""
225
- if not is_root():
226
- logger.error("Privilégios de root são necessários para iniciar o modo monitor")
227
- return None
228
-
229
- try:
230
- # Para processos que podem interferir
231
- subprocess.run(['airmon-ng', 'check', 'kill'],
232
- stdout=subprocess.PIPE,
233
- stderr=subprocess.PIPE)
234
-
235
- # Inicia o modo monitor
236
- cmd = ['airmon-ng', 'start', interface]
237
- if channel:
238
- cmd.extend(['-c', str(channel)])
239
-
240
- result = subprocess.run(cmd,
241
- stdout=subprocess.PIPE,
242
- stderr=subprocess.PIPE,
243
- text=True)
244
-
245
- if result.returncode != 0:
246
- logger.error(f"Falha ao iniciar o modo monitor: {result.stderr}")
247
- return None
248
-
249
- # Obtém o nome da interface em modo monitor (pode mudar, ex: wlan0 -> wlan0mon)
250
- for line in result.stdout.split('\n'):
251
- if 'monitor mode' in line and 'enabled' in line:
252
- parts = line.split()
253
- if len(parts) > 0:
254
- return parts[0].strip('()')
255
-
256
- return f"{interface}mon" # Padrão comum
257
-
258
- except subprocess.CalledProcessError as e:
259
- logger.error(f"Erro ao executar airmon-ng: {e}")
260
- return None
261
- except Exception as e:
262
- logger.error(f"Erro inesperado: {e}")
263
- return None
264
-
265
- def stop_monitor_mode(interface: str) -> bool:
266
- """Para o modo monitor em uma interface usando airmon-ng."""
267
- if not is_root():
268
- logger.error("Privilégios de root são necessários para parar o modo monitor")
269
- return False
270
-
271
- try:
272
- # Para o modo monitor
273
- subprocess.run(['airmon-ng', 'stop', interface], check=True)
274
-
275
- # Reinicia o gerenciador de rede
276
- if command_exists('systemctl'):
277
- subprocess.run(['systemctl', 'restart', 'NetworkManager'], check=False)
278
- elif command_exists('service'):
279
- subprocess.run(['service', 'network-manager', 'restart'], check=False)
280
-
281
- return True
282
- except subprocess.CalledProcessError as e:
283
- logger.error(f"Erro ao parar modo monitor: {e}")
284
- return False
285
- except Exception as e:
286
- logger.error(f"Erro inesperado: {e}")
287
- return False
288
-
289
- def get_interface_mac(interface: str) -> Optional[str]:
290
- """Obtém o endereço MAC de uma interface de rede."""
291
- try:
292
- with open(f"/sys/class/net/{interface}/address") as f:
293
- return f.read().strip()
294
- except (IOError, FileNotFoundError):
295
- return None
296
-
297
- def get_interface_ip(interface: str) -> Optional[str]:
298
- """Obtém o endereço IP de uma interface de rede."""
299
- try:
300
- return netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
301
- except (KeyError, IndexError):
302
- return None
303
-
304
- def get_interface_netmask(interface: str) -> Optional[str]:
305
- """Obtém a máscara de rede de uma interface de rede."""
306
- try:
307
- return netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['netmask']
308
- except (KeyError, IndexError):
309
- return None
310
-
311
- def get_interface_gateway(interface: str) -> Optional[str]:
312
- """Obtém o gateway padrão de uma interface de rede."""
313
- try:
314
- gateways = netifaces.gateways()
315
- return gateways['default'][netifaces.AF_INET][0]
316
- except (KeyError, IndexError):
317
- return None
318
-
319
- def is_wireless_interface(interface: str) -> bool:
320
- """Verifica se uma interface é sem fio."""
321
- try:
322
- with open(f"/sys/class/net/{interface}/wireless/uevent", 'r') as f:
323
- return True
324
- except (IOError, FileNotFoundError):
325
- return False
326
-
327
- def get_interface_signal(interface: str) -> Optional[int]:
328
- """Obtém a intensidade do sinal de uma interface sem fio em dBm."""
329
- try:
330
- result = subprocess.run(
331
- ['iwconfig', interface],
332
- stdout=subprocess.PIPE,
333
- stderr=subprocess.PIPE,
334
- text=True
335
- )
336
-
337
- if result.returncode != 0:
338
- return None
339
-
340
- match = re.search(r'Signal level=(-?\d+) dBm', result.stdout)
341
- if match:
342
- return int(match.group(1))
343
- return None
344
- except (subprocess.SubprocessError, ValueError):
345
- return None
346
-
347
- def get_interface_ssid(interface: str) -> Optional[str]:
348
- """Obtém o SSID ao qual a interface está conectada."""
349
- try:
350
- result = subprocess.run(
351
- ['iwgetid', '-r', interface],
352
- stdout=subprocess.PIPE,
353
- stderr=subprocess.PIPE,
354
- text=True
355
- )
356
-
357
- if result.returncode == 0 and result.stdout.strip():
358
- return result.stdout.strip()
359
- return None
360
- except (subprocess.SubprocessError, FileNotFoundError):
361
- return None
362
-
363
- def get_interface_channel(interface: str) -> Optional[int]:
364
- """Obtém o canal em que a interface está operando."""
365
- try:
366
- result = subprocess.run(
367
- ['iwlist', interface, 'channel'],
368
- stdout=subprocess.PIPE,
369
- stderr=subprocess.PIPE,
370
- text=True
371
- )
372
-
373
- if result.returncode != 0:
374
- return None
375
-
376
- match = re.search(r'Channel (\d+)', result.stdout)
377
- if match:
378
- return int(match.group(1))
379
- return None
380
- except (subprocess.SubprocessError, ValueError):
381
- return None
382
-
383
- def get_interface_bitrate(interface: str) -> Optional[float]:
384
- """Obtém a taxa de transmissão da interface em Mbps."""
385
- try:
386
- result = subprocess.run(
387
- ['iwconfig', interface],
388
- stdout=subprocess.PIPE,
389
- stderr=subprocess.PIPE,
390
- text=True
391
- )
392
-
393
- if result.returncode != 0:
394
- return None
395
-
396
- match = re.search(r'Bit Rate[:=]([\d.]+) (\w+)', result.stdout)
397
- if match:
398
- rate, unit = match.groups()
399
- rate = float(rate)
400
- if unit == 'Mb/s':
401
- return rate
402
- elif unit == 'Kb/s':
403
- return rate / 1000
404
- return None
405
- except (subprocess.SubprocessError, ValueError):
406
- return None
407
-
408
- # Funções para manipulação de pacotes
409
- def create_deauth_packet(bssid: str, client: str, reason: int = 7, count: int = 1) -> bytes:
410
- """Cria um pacote de desautenticação."""
411
- # Cabeçalho do pacote 802.11
412
- frame = [
413
- 0x00, 0x00, 0x0c, 0x00, # Radiotap header (versão, pad, len, presente)
414
- 0x04, 0x80, 0x00, 0x00, # Presente flags
415
- 0x00, 0x00, 0x00, 0x00, # Timestamp
416
- 0x00, 0x00, 0x00, 0x00, # Flags e taxa de dados
417
- 0x00, 0x00, # Canal, sinal, ruído, etc.
418
- 0x00, 0x00, 0x00, 0x00, # MCS conhecido, flags, mcs
419
- 0x00, 0x00, 0x00, 0x00, # A-MPDU
420
-
421
- # Cabeçalho 802.11 (tipo Management, subtipo Deauthentication)
422
- 0x00, 0x00, 0x0c, 0x00, # Controle de versão, tipo, subtipo, etc.
423
- 0x00, 0x00, # Duração
424
- ]
425
-
426
- # Endereço de destino (broadcast ou cliente específico)
427
- dest = [int(x, 16) for x in client.split(':')]
428
- frame.extend(dest)
429
-
430
- # Endereço de origem (BSSID)
431
- src = [int(x, 16) for x in bssid.split(':')]
432
- frame.extend(src)
433
-
434
- # BSSID (mesmo que origem para redes de infraestrutura)
435
- frame.extend(src)
436
-
437
- # Número de sequência
438
- frame.extend([0x00, 0x00])
439
-
440
- # Código de motivo (2 bytes, little-endian)
441
- frame.extend([reason, 0x00])
442
-
443
- return bytes(frame) * count
444
-
445
- # Funções para execução de comandos
446
- async def run_command_async(cmd: Union[str, List[str]], **kwargs) -> subprocess.CompletedProcess:
447
- """
448
- Executa um comando de forma assíncrona.
449
-
450
- Args:
451
- cmd: Comando a ser executado (string ou lista)
452
- **kwargs: Argumentos adicionais para asyncio.create_subprocess_exec()
453
-
454
- Returns:
455
- subprocess.CompletedProcess: Resultado da execução do comando
456
- """
457
- if isinstance(cmd, str):
458
- cmd = cmd.split()
459
-
460
- process = await asyncio.create_subprocess_exec(
461
- *cmd,
462
- stdout=asyncio.subprocess.PIPE,
463
- stderr=asyncio.subprocess.PIPE,
464
- **kwargs
465
- )
466
-
467
- stdout, stderr = await process.communicate()
468
-
469
- return subprocess.CompletedProcess(
470
- args=cmd,
471
- returncode=process.returncode,
472
- stdout=stdout,
473
- stderr=stderr
474
- )
475
-
476
- # Funções para processamento de saída
477
- def parse_airodump_csv(csv_file: str) -> List[WiFiNetwork]:
478
- """Analisa o arquivo CSV gerado pelo airodump-ng."""
479
- networks = []
480
-
481
- try:
482
- with open(csv_file, 'r') as f:
483
- lines = f.readlines()
484
-
485
- # Encontra o início dos dados das redes
486
- start_idx = 0
487
- for i, line in enumerate(lines):
488
- if line.startswith('BSSID, First time seen,'):
489
- start_idx = i + 1
490
- break
491
-
492
- # Processa as redes
493
- for line in lines[start_idx:]:
494
- line = line.strip()
495
- if not line or line.startswith('Station'):
496
- break
497
-
498
- # Extrai os campos da linha
499
- parts = [p.strip() for p in line.split(',')]
500
- if len(parts) < 14: # Número mínimo de campos esperados
501
- continue
502
-
503
- bssid = parts[0].strip()
504
- first_seen = parts[1].strip()
505
- last_seen = parts[2].strip()
506
- channel = int(parts[3].strip())
507
- speed = parts[4].strip()
508
- privacy = parts[5].strip()
509
- cipher = parts[6].strip()
510
- auth = parts[7].strip()
511
- power = int(parts[8].strip())
512
- beacons = int(parts[9].strip())
513
- iv = int(parts[10].strip())
514
- ip = parts[11].strip()
515
- id_len = int(parts[12].strip())
516
- essid = parts[13].strip()
517
-
518
- # Cria o objeto da rede
519
- network = WiFiNetwork(
520
- bssid=bssid,
521
- ssid=essid,
522
- channel=channel,
523
- signal=power,
524
- encryption=privacy,
525
- cipher=cipher,
526
- authentication=auth,
527
- first_seen=first_seen,
528
- last_seen=last_seen,
529
- speed=speed,
530
- beacons=beacons,
531
- iv=iv,
532
- ip=ip if ip != '0.0.0.0' else None
533
- )
534
-
535
- networks.append(network)
536
-
537
- except Exception as e:
538
- logger.error(f"Erro ao analisar arquivo CSV: {e}")
539
-
540
- return networks
541
-
542
- def parse_airodump_stations(csv_file: str) -> List[WiFiClient]:
543
- """Analisa a seção de estações do arquivo CSV do airodump-ng."""
544
- clients = []
545
-
546
- try:
547
- with open(csv_file, 'r') as f:
548
- lines = f.readlines()
549
-
550
- # Encontra o início da seção de estações
551
- start_idx = 0
552
- for i, line in enumerate(lines):
553
- if line.startswith('Station MAC,'):
554
- start_idx = i + 1
555
- break
556
-
557
- # Processa as estações
558
- for line in lines[start_idx:]:
559
- line = line.strip()
560
- if not line:
561
- continue
562
-
563
- # Extrai os campos da linha
564
- parts = [p.strip() for p in line.split(',')]
565
- if len(parts) < 6: # Número mínimo de campos esperados
566
- continue
567
-
568
- mac = parts[0].strip()
569
- first_seen = parts[1].strip()
570
- last_seen = parts[2].strip()
571
- power = int(parts[3].strip())
572
- packets = int(parts[4].strip())
573
- bssid = parts[5].strip()
574
-
575
- # Cria o objeto do cliente
576
- client = WiFiClient(
577
- mac=mac,
578
- bssid=bssid,
579
- signal=power,
580
- packets=packets,
581
- first_seen=first_seen,
582
- last_seen=last_seen
583
- )
584
-
585
- clients.append(client)
586
-
587
- except Exception as e:
588
- logger.error(f"Erro ao analisar estações do arquivo CSV: {e}")
589
-
590
- return clients
591
-
592
- def randomize_mac(interface: str) -> bool:
593
- """
594
- Aleatoriza o endereço MAC de uma interface de rede.
595
-
596
- Args:
597
- interface: Nome da interface de rede
598
-
599
- Returns:
600
- bool: True se bem-sucedido, False caso contrário
601
- """
602
- if not is_root():
603
- logger.error("Privilégios de root são necessários para alterar o endereço MAC")
604
- return False
605
-
606
- try:
607
- # Gera um endereço MAC aleatório
608
- import random
609
- new_mac = ':'.join(['%02x' % random.randint(0x00, 0xff) for _ in range(6)])
610
-
611
- # Desativa a interface
612
- subprocess.run(['ip', 'link', 'set', interface, 'down'], check=True)
613
-
614
- # Define o novo endereço MAC
615
- subprocess.run(['ip', 'link', 'set', 'dev', interface, 'address', new_mac], check=True)
616
-
617
- # Reativa a interface
618
- subprocess.run(['ip', 'link', 'set', interface, 'up'], check=True)
619
-
620
- logger.info(f"Endereço MAC da interface {interface} alterado para {new_mac}")
621
- return True
622
- except subprocess.CalledProcessError as e:
623
- logger.error(f"Falha ao aleatorizar o endereço MAC: {e}")
624
- return False