/* * PcapCrack 1.0 * Copyright © 2017 Ben Goldsworthy (rumperuu) * * A program to attempt to brute-force the key used to encrypt a file * in an intercepted network packet. * * This file is part of PcapCrack. * * PcapCrack is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PcapCrack is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PcapCrack. If not, see . */ /** ** This class represents a password-cracking thread utilising a given ** method for generating possible passwords. **/ import java.io.*; import java.net.*; import java.security.*; import java.util.*; import java.util.regex.*; import javax.crypto.*; import javax.crypto.spec.*; /** ** @author Ben Goldsworthy (rumperuu) ** @version 1.0 **/ public class Cracker implements Runnable { private static final String ivVal = "1234567891011121"; private static final char[] leet = {'a', '4', 'e', '3', 'i', '1', 'o', '0', 'l', '|', 't' , '7'}; private static byte[] file; private static boolean found = false; private ArrayList passwords; private int mode; MessageDigest digest; Cipher cipher; IvParameterSpec iv; /** ** Constructor. ** ** @param mode The method of password generation the thread will ** utilise. ** @param file The encoded file to crack, encoded in Base64. ** @param dict The `ArrayList` of words from the chosen dictionary. **/ public Cracker(int mode, String file, ArrayList dict){ this.mode = mode; this.file = Base64.getDecoder().decode(file); // Initialises the dictionary to the passed file for all modes except // 0... if (this.mode > 2) { this.passwords = dict; // ...in which case, initialises the dictionary to the list of the // 10,000 most common passwords included with the .jar. } else { ArrayList commonPasswords = new ArrayList(); try { InputStream is = getClass().getResourceAsStream("/dat/10000-most-common-passwords.txt"); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { commonPasswords.add(line); } this.passwords = commonPasswords; } catch (FileNotFoundException e) { System.out.println("File `10000-most-common-passwords.txt` not found in directory `.\\dat`."); System.exit(1); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } // If the cracker has an even mode, flips the order the password // list. if ((this.mode % 2)== 0) Collections.reverse(this.passwords); // Halves the cracker's list, to avoid duplicating effort with its // even twin. this.passwords = new ArrayList(this.passwords.subList(0, this.passwords.size()/2)); // Initialises as much of the crypto stuff as can be done at this // stage. try { this.digest = MessageDigest.getInstance("SHA-256"); this.cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); this.iv = new IvParameterSpec(this.ivVal.getBytes()); } catch (Exception e) { System.out.println(e); } } /** ** Runs the cracker process on the thread, and alerts on end. **/ @Override public void run() { processCommand(); } /** ** Tests every key generated using the given method for the thread. ** ** Mode 1: 10,000 most common passwords. ** Mode 3: Dictionary words. ** Mode 5: Dictionary words, with each letter capitalised in turn. ** Mode 7: Dictionary words, with ASCII symbols {!-/} appended. ** Mode 9: Dictionary words, with 'leetspeek' replacements. ** Mode 11: Dictionary words, capitalised. ** Mode 13: Dictionary words, with numbers {0-9999} appended ** and prepended. ** Mode 15: Dictionary words, combined in pairs. ** Mode 17: Numbers {0-1,000,000}. ** ** Even-numbered modes are the previous mode, with a reversed ** list. Thus, all modes terminate when halfway through. **/ private void processCommand() { int j, k; int tenth = (int)Math.ceil(this.passwords.size()/10); int current = 0; int percentage = 0; // DEBUG: when using the test dictionary, avoids divide be zero // exceptions later on. if (this.passwords.size() < 10) tenth = 1; for (String password : this.passwords) { // If another cracker has found the correct password, stop. if (this.found) return; current++; switch (this.mode) { // Tests the unaltered dictionary word/common password. case 1:case 2:case 3:case 4: this.crack(password); break; // Capitalises each letter of each word in turn. case 5:case 6: for (j = 1; j < password.length() - 1; j++) { password = password.substring(0,j) + password.substring(j,j+1).toUpperCase() + password.substring(j+1); this.crack(password); } break; // Appends the symbols represented by ASCII values 33-47. case 7:case 8: for (j = 33; j < 48; j++) { password = password + (char)j; this.crack(password); } break; // Replaces each leet character in turn. case 9:case 10: for (j = 0; j < password.length() - 1; j++) { for (k = 0; k < this.leet.length; k+=2) { if (password.charAt(j) == this.leet[k]) { StringBuilder newPassword = new StringBuilder(password); newPassword.setCharAt(j, this.leet[k+1]); this.crack(newPassword.toString()); } } } break; // Capitalises the first letter of every word. case 11:case 12: password = password.substring(0,1).toUpperCase() + password.substring(1); this.crack(password); break; // Appends and prepends the numbers 1-9,999 to every word. case 13:case 14: for (j = 0; j < 9999 - 1; j++) { this.crack(password + j); this.crack(j + password); } break; // Appends and prepends the next word. case 15:case 16: for (j = 0; j < this.passwords.size() - 1; j++) { this.crack(password + this.passwords.get(j)); } break; // Attempts the numbers 0-1,000,000. case 17:case 18: for (j = k = 0; (j <= 500000) && (k >= 500000); j++, k--) { this.crack(""+j); this.crack(""+k); } break; } // Displays the current progress. if ((current % tenth) == 0) { tenth += tenth; percentage += 10; System.out.println("Cracker "+mode+" at "+percentage+"%"); } } // If there's been no return, none of the attempted passwords have // worked. System.out.println("Cracker " + this.mode + " end, no result."); } /** ** Tests a given password to see if it cracks the payload. ** ** @param password The given password. ** @return Whether the file was cracked or not. **/ private boolean crack(String password) { try { // Sets up the cipher with the given password. byte[] keyVal = digest.digest(password.getBytes("UTF-8")); keyVal = Arrays.copyOfRange(keyVal, 0, keyVal.length/2); SecretKeySpec key = new SecretKeySpec(keyVal, "AES"); cipher.init(Cipher.DECRYPT_MODE, key, iv); // Decrypts the file using the cipher. String decryptedFile = new String(cipher.doFinal(this.file)); if (decryptedFile.startsWith("DECRYPTED:")) { // Must be outputted in a single command, or lines will be // broken up by output from other cracker threads. System.out.println("Cracker "+this.mode+" end, successful crack!\n==========\nDecrypted file: " + decryptedFile.split(":",2)[1] + "\nPassword: " + password + "\n==========\n"); this.found = true; return true; } } catch (BadPaddingException e) { // If the password is incorrect, do nothing and move on to // returning "false". } catch (Exception e) { System.out.println(e); } finally { return false; } } }