|
20 | 20 |
|
21 | 21 | # TODO: Bring back the 'print' option, for easy copy/pasting. Just one-liners people can paste into terminal. |
22 | 22 |
|
| 23 | +def decode_hex_essid_if_needed(essid): |
| 24 | + """ |
| 25 | + Decode hex-encoded ESSID with strict validation to prevent false positives. |
| 26 | + |
| 27 | + Only decodes if ALL of the following criteria are met: |
| 28 | + 1. Length >= 16 characters (minimum for 8-char ESSID encoded as hex) |
| 29 | + 2. Length is even (valid hex pairs) |
| 30 | + 3. All characters are valid hexadecimal digits |
| 31 | + 4. Decoded result is shorter than original (hex encoding expands length) |
| 32 | + 5. Decoded result contains only printable characters (ASCII 32-126 or UTF-8 >= 128) |
| 33 | + |
| 34 | + This prevents false positives for legitimate network names like "CAFE", "DEAD", "BEEF", etc. |
| 35 | + |
| 36 | + Args: |
| 37 | + essid: ESSID string to potentially decode |
| 38 | + |
| 39 | + Returns: |
| 40 | + Decoded ESSID if valid hex-encoded, otherwise original ESSID |
| 41 | + """ |
| 42 | + # Validation check 1: Minimum length (too short to be hex-encoded) |
| 43 | + if len(essid) < 16: |
| 44 | + return essid |
| 45 | + |
| 46 | + # Validation check 2: Even length (valid hex pairs) |
| 47 | + if len(essid) % 2 != 0: |
| 48 | + return essid |
| 49 | + |
| 50 | + # Validation check 3: All characters are hex digits |
| 51 | + if not all(c in '0123456789ABCDEFabcdef' for c in essid): |
| 52 | + return essid |
| 53 | + |
| 54 | + try: |
| 55 | + # Try to decode from hex with strict error handling |
| 56 | + decoded_essid = bytes.fromhex(essid).decode('utf-8', errors='strict') |
| 57 | + |
| 58 | + # Validation check 4: Decoded result must not be empty |
| 59 | + if not decoded_essid or len(decoded_essid) == 0: |
| 60 | + return essid |
| 61 | + |
| 62 | + # Validation check 5: Hex encoding should make string longer, not shorter |
| 63 | + if len(decoded_essid) >= len(essid): |
| 64 | + return essid |
| 65 | + |
| 66 | + # Validation check 6: Check for printable characters (ASCII 32-126 or extended UTF-8) |
| 67 | + if not all(32 <= ord(c) <= 126 or ord(c) >= 128 for c in decoded_essid): |
| 68 | + return essid |
| 69 | + |
| 70 | + # All checks passed, use decoded version |
| 71 | + if Configuration.verbose > 1: |
| 72 | + Color.pl('{+} {C}Decoded hex ESSID: {O}%s{W} -> {G}%s{W}' % (essid, decoded_essid)) |
| 73 | + |
| 74 | + return decoded_essid |
| 75 | + |
| 76 | + except (ValueError, UnicodeDecodeError) as e: |
| 77 | + # Decoding failed, keep original |
| 78 | + # This is expected for legitimate network names like "CAFE", "DEAD", etc. |
| 79 | + if Configuration.verbose > 2: |
| 80 | + Color.pl('{!} {O}Hex ESSID decode failed for %s: %s{W}' % (essid, str(e))) |
| 81 | + return essid |
| 82 | + |
| 83 | + |
23 | 84 | class CrackHelper: |
24 | 85 | """Manages handshake retrieval, selection, and running the cracking commands.""" |
25 | 86 |
|
@@ -197,30 +258,8 @@ def get_handshakes(cls): |
197 | 258 | essid = essid if essid_discovery is None else essid_discovery |
198 | 259 | elif hs_type == 'PMKID': |
199 | 260 | # Decode hex-encoded ESSID from passive PMKID capture |
200 | | - # Only decode if it looks like hex-encoded UTF-8 (not just valid hex) |
201 | | - # Criteria: |
202 | | - # 1. Length >= 16 (minimum for 8-char ESSID encoded as hex) |
203 | | - # 2. Even length (valid hex pairs) |
204 | | - # 3. All characters are hex digits |
205 | | - # 4. Decoded result is shorter than original (hex encoding expands) |
206 | | - # 5. Decoded result contains only printable characters |
207 | | - if (len(essid) >= 16 and |
208 | | - len(essid) % 2 == 0 and |
209 | | - all(c in '0123456789ABCDEFabcdef' for c in essid)): |
210 | | - try: |
211 | | - # Try to decode from hex (strict mode) |
212 | | - decoded_essid = bytes.fromhex(essid).decode('utf-8', errors='strict') |
213 | | - |
214 | | - # Validate decoded result |
215 | | - if (decoded_essid and |
216 | | - len(decoded_essid) > 0 and |
217 | | - len(decoded_essid) < len(essid) and # Hex encoding should be longer |
218 | | - all(32 <= ord(c) <= 126 or ord(c) >= 128 for c in decoded_essid)): # Printable chars |
219 | | - essid = decoded_essid |
220 | | - except (ValueError, UnicodeDecodeError): |
221 | | - # If decoding fails, keep the original hex string |
222 | | - # This is expected for legitimate network names like "CAFE", "DEAD", etc. |
223 | | - pass |
| 261 | + # Use dedicated function with strict validation to prevent false positives |
| 262 | + essid = decode_hex_essid_if_needed(essid) |
224 | 263 |
|
225 | 264 | handshake = { |
226 | 265 | 'filename': os.path.join(hs_dir, hs_file), |
|
0 commit comments