Skip to content

Commit 9cf4b77

Browse files
authored
Merge pull request #500 from kimocoder/copilot/apply-patch-changes
Apply patch.patch: Dragonblood timing attack, live hashcat progress, and security hardening
2 parents 267e53c + 3a58fef commit 9cf4b77

27 files changed

Lines changed: 2131 additions & 532 deletions

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Core dependencies for wifite2
22
# These should match pyproject.toml [project.dependencies]
3-
chardet>=7.0.1
3+
chardet>=7.4.1
44
requests>=2.32.5
55
rich>=14.0.0
66
scapy>=2.7.0; python_version < '4'

tools/run-automation.sh

Lines changed: 0 additions & 20 deletions
This file was deleted.

wifite/args.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,35 @@ def _add_wpa_args(self, wpa):
768768
'(default: {G}%d sec{W})' % self.config.wpa_attack_timeout))
769769
wpa.add_argument('-wpa3-timeout', help=argparse.SUPPRESS, action='store', dest='wpa3_attack_timeout', type=int)
770770

771+
# Dragonblood timing attack (CVE-2019-13377)
772+
wpa.add_argument('--dragonblood-timing',
773+
action='store_true',
774+
dest='dragonblood_timing',
775+
help=Color.s('Enable {C}Dragonblood timing attack{W} (CVE-2019-13377) for APs with '
776+
'vulnerable MODP groups (22, 23, 24). Probes the AP to measure SAE '
777+
'response latency and partitions the password search space. '
778+
'(default: {G}off{W})'))
779+
wpa.add_argument('-dragonblood-timing', help=argparse.SUPPRESS, action='store_true', dest='dragonblood_timing')
780+
781+
wpa.add_argument('--dragonblood-samples',
782+
action='store',
783+
dest='dragonblood_samples',
784+
metavar='[count]',
785+
type=int,
786+
help=self._verbose('Number of timing samples per password candidate '
787+
'during Dragonblood attack. More samples improve accuracy '
788+
'but take longer. (default: {G}3{W})'))
789+
wpa.add_argument('-dragonblood-samples', help=argparse.SUPPRESS, action='store', dest='dragonblood_samples', type=int)
790+
791+
wpa.add_argument('--dragonblood-max',
792+
action='store',
793+
dest='dragonblood_max_passwords',
794+
metavar='[count]',
795+
type=int,
796+
help=self._verbose('Maximum number of passwords to probe during '
797+
'Dragonblood timing attack. (default: {G}50{W})'))
798+
wpa.add_argument('-dragonblood-max', help=argparse.SUPPRESS, action='store', dest='dragonblood_max_passwords', type=int)
799+
771800
wpa.add_argument('--owe',
772801
action='store_true',
773802
dest='owe_filter',

wifite/attack/pmkid.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from ..model.attack import Attack
55
from ..config import Configuration
6-
from ..tools.hashcat import HcxDumpTool, HcxPcapngTool, Hashcat
6+
from ..tools.hashcat import HcxDumpTool, HcxPcapngTool, Hashcat, HashcatCracker
77
from ..util.color import Color
88
from ..util.timer import Timer
99
from ..util.output import OutputManager
@@ -515,7 +515,36 @@ def crack_pmkid_file(self, pmkid_file):
515515
wordlist_name = os.path.basename(wordlist)
516516
Color.clear_entire_line()
517517
Color.pattack('PMKID', self.target, 'CRACK', 'Cracking PMKID using {C}%s{W} ...\n' % wordlist_name)
518-
key = Hashcat.crack_pmkid(pmkid_file, wordlist=wordlist)
518+
519+
try:
520+
with HashcatCracker(pmkid_file, wordlist) as cracker:
521+
cracker.start(show_command=Configuration.verbose > 1)
522+
523+
# Polling loop for progress tracking
524+
while not cracker.is_finished():
525+
status = cracker.poll_status()
526+
if self.view:
527+
self.view.update_progress({
528+
'progress': status['progress'],
529+
'metrics': {
530+
'Speed': status['speed'],
531+
'ETA': status['eta']
532+
}
533+
})
534+
535+
# Update TUI status line periodically (approx every 2 seconds)
536+
# We use a simple sleep since this is a dedicated attack thread
537+
time.sleep(2)
538+
539+
key = cracker.get_result()
540+
except KeyboardInterrupt:
541+
return self._handle_hashcat_failure(
542+
'\n{!} {R}Failed to crack PMKID: {O}Cracking interrupted by user{W}'
543+
)
544+
except Exception as e:
545+
log_error('AttackPMKID', f'Error during hashcat polling: {e}')
546+
continue
547+
519548
if key is not None:
520549
break
521550
Color.pl('{!} {O}%s did not contain password{W}' % wordlist_name)

wifite/attack/portal/credential_handler.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,20 +364,38 @@ def get_validation_results(self) -> List[ValidationResult]:
364364
"""
365365
return self.validation_results.copy()
366366

367+
def clear_credentials(self):
368+
"""Overwrite all stored credential data in memory to prevent leakage."""
369+
# Overwrite passwords in validation results
370+
for result in self.validation_results:
371+
if result.submission.password:
372+
result.submission.password = '\x00' * len(result.submission.password)
373+
result.submission.password = ''
374+
# Overwrite passwords in pending submissions
375+
for submission in self.pending_submissions.values():
376+
if submission.password:
377+
submission.password = '\x00' * len(submission.password)
378+
submission.password = ''
379+
# Overwrite valid credential tuples
380+
for i, (ssid, password) in enumerate(self.valid_credentials):
381+
self.valid_credentials[i] = (ssid, '\x00' * len(password))
382+
self.valid_credentials.clear()
383+
log_info('CredentialHandler', 'Credential data cleared from memory')
384+
367385
def clear_statistics(self):
368386
"""Clear all statistics and results."""
387+
self.clear_credentials()
369388
self.validation_results.clear()
370-
self.valid_credentials.clear()
371389
self.client_attempts.clear()
372390
self.client_last_attempt.clear()
373-
391+
374392
self.total_submissions = 0
375393
self.total_validations = 0
376394
self.successful_validations = 0
377395
self.failed_validations = 0
378-
396+
379397
log_info('CredentialHandler', 'Statistics cleared')
380-
398+
381399
def __str__(self):
382400
stats = self.get_statistics()
383401
return (f'CredentialHandler: {stats["total_submissions"]} submissions, '

wifite/attack/wpa.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from ..model.attack import Attack
55
from ..tools.aircrack import Aircrack
6-
from ..tools.hashcat import Hashcat
6+
from ..tools.hashcat import Hashcat, HashcatCracker, HcxDumpTool, HcxPcapngTool
77
from ..tools.airodump import Airodump
88
from ..tools.aireplay import Aireplay
99
from ..config import Configuration
@@ -413,7 +413,37 @@ def run(self):
413413
})
414414

415415
try:
416-
key = Hashcat.crack_handshake(handshake, target_is_wpa3_sae, show_command=Configuration.verbose > 1, wordlist=wordlist)
416+
# Use the new HashcatCracker for non-blocking execution and progress polling
417+
# First, generate the hash file (we use the existing HcxPcapngTool logic via a helper)
418+
hash_file = HcxPcapngTool.generate_hash_file(handshake, target_is_wpa3_sae, show_command=Configuration.verbose > 1)
419+
420+
if hash_file is None:
421+
# Fallback to aircrack-ng if hash file generation failed
422+
key = Hashcat.crack_handshake(handshake, target_is_wpa3_sae, show_command=Configuration.verbose > 1, wordlist=wordlist)
423+
else:
424+
try:
425+
with HashcatCracker(hash_file, wordlist, target_is_wpa3_sae=target_is_wpa3_sae) as cracker:
426+
cracker.start(show_command=Configuration.verbose > 1)
427+
428+
while not cracker.is_finished():
429+
status = cracker.poll_status()
430+
if self.view:
431+
self.view.update_progress({
432+
'progress': status['progress'],
433+
'metrics': {
434+
'Speed': status['speed'],
435+
'ETA': status['eta']
436+
}
437+
})
438+
time.sleep(2)
439+
440+
key = cracker.get_result()
441+
finally:
442+
if hash_file and os.path.exists(hash_file):
443+
try:
444+
os.remove(hash_file)
445+
except OSError:
446+
pass
417447
except ValueError as e: # Catch errors from hash file generation (e.g. bad capture)
418448
error_msg = f"Error during hash file generation for cracking: {e}"
419449
Color.pl(f"[!] {error_msg}")

0 commit comments

Comments
 (0)