From ef15a10a4eafe21be4330566980cc5833bca5b7d Mon Sep 17 00:00:00 2001 From: Billy Brawner Date: Sat, 7 Apr 2018 09:06:12 -0500 Subject: [PATCH] Implement incremental cracking --- README.md | 12 +- build.gradle | 15 ++ gradle/wrapper/gradle-wrapper.jar | Bin 54706 -> 54706 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- keecrack-cli/build.gradle | 15 ++ .../java/com/wbrawner/keecrack/cli/Main.java | 74 +++++-- keecrack-gui/build.gradle | 15 ++ .../keecrack/gui/CrackingController.java | 15 ++ .../java/com/wbrawner/keecrack/gui/Main.java | 25 ++- .../wbrawner/keecrack/gui/MainController.java | 78 +++++-- .../src/main/resources/fxml/cracking.fxml | 34 ++- .../src/main/resources/fxml/main.fxml | 141 +++++++----- keecrack-lib/build.gradle | 23 +- .../java/com/wbrawner/keecrack/lib/Code.java | 20 +- .../com/wbrawner/keecrack/lib/KeeCrack.java | 204 +++++++++++++----- .../java/com/wbrawner/keecrack/lib/Utils.java | 44 ++++ .../wbrawner/keecrack/lib/view/BaseView.java | 15 ++ .../keecrack/lib/view/CrackingView.java | 21 +- .../wbrawner/keecrack/lib/view/FormView.java | 19 +- .../keecrack/lib/wordlist/WordList.java | 57 +++++ .../lib/wordlist/WordListIterator.java | 54 +++++ .../wbrawner/keecrack/lib/KeeCrackTest.java | 105 +++++++-- .../java/com/wbrawner/keecrack/lib/Utils.java | 15 ++ .../test/resources/databases/0000-key.kdbx | Bin 0 -> 1118 bytes .../src/test/resources/databases/0000.kdbx | Bin 0 -> 1118 bytes .../src/test/resources/databases/ab-key.kdbx | Bin 0 -> 1118 bytes .../src/test/resources/databases/ab.kdbx | Bin 0 -> 1118 bytes settings.gradle | 17 +- 28 files changed, 833 insertions(+), 189 deletions(-) create mode 100644 keecrack-lib/src/main/java/com/wbrawner/keecrack/lib/Utils.java create mode 100644 keecrack-lib/src/main/java/com/wbrawner/keecrack/lib/wordlist/WordList.java create mode 100644 keecrack-lib/src/main/java/com/wbrawner/keecrack/lib/wordlist/WordListIterator.java create mode 100644 keecrack-lib/src/test/resources/databases/0000-key.kdbx create mode 100644 keecrack-lib/src/test/resources/databases/0000.kdbx create mode 100644 keecrack-lib/src/test/resources/databases/ab-key.kdbx create mode 100644 keecrack-lib/src/test/resources/databases/ab.kdbx diff --git a/README.md b/README.md index b12de1f..832c5d7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,10 @@ [![Test Coverage](https://api.codeclimate.com/v1/badges/a35dff49221e36abf189/test_coverage)](https://codeclimate.com/github/wbrawner/keecrack/test_coverage) KeeCrack is a Java program used for brute-forcing KeePass database file master passwords. This should go without saying -but use of this application is prohibited without the express consent of the owner of the database file. +but use of this application is prohibited without the express consent of the owner of the database file. KeeCrack works +by taking a KeePass database file, an optional key file, and a word list, then attempts to open the database with the +give key file/password pair until it finds a successful password. KeeCrack does not do incremental word list generation +at this time, though you can ## Usage @@ -17,6 +20,13 @@ KeeCrack makes use of Gradle, so to build it yourself, you can just run ./gradlew jfxJar +This will produce a JAR output, though you can also create platform-specific binaries with the following: + + ./gradlew jfxNative + +For more information on building for your OS, please see the README for the +[javafx-gradle-plugin](https://github .com/FibreFoX/javafx-gradle-plugin#requirements) + ## Contributing If you'd like to contribute, please fork the repository, make your changes, squash your commits, and send a pull request diff --git a/build.gradle b/build.gradle index dc341e1..4a4f0f0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ buildscript { repositories { jcenter() diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a45a7cadf71445d0c198b1b18644dcf3f6785e5..a18cfc21eeb9bf4fe49081333e4584673b13596e 100644 GIT binary patch delta 26 gcmdnAnt9V|<_+%-Gfyn*-~91#p&*DcIrXY90JshfR{#J2 delta 26 gcmdnAnt9V|<_+%-GrwwS-~91#p&*DcIrXY90Kl6L6#xJL diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 50428f4..33ec7bf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Apr 07 16:39:20 CDT 2018 +#Sun Apr 15 14:52:35 CDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip diff --git a/keecrack-cli/build.gradle b/keecrack-cli/build.gradle index ff3ce2a..289c449 100644 --- a/keecrack-cli/build.gradle +++ b/keecrack-cli/build.gradle @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ dependencies { compile project(':keecrack-lib') compile project(':keecrack-gui') diff --git a/keecrack-cli/src/main/java/com/wbrawner/keecrack/cli/Main.java b/keecrack-cli/src/main/java/com/wbrawner/keecrack/cli/Main.java index 371abda..8c7e307 100644 --- a/keecrack-cli/src/main/java/com/wbrawner/keecrack/cli/Main.java +++ b/keecrack-cli/src/main/java/com/wbrawner/keecrack/cli/Main.java @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.wbrawner.keecrack.cli; import com.wbrawner.keecrack.lib.Code; @@ -10,13 +25,22 @@ import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; import java.io.File; +import java.text.SimpleDateFormat; import java.time.Duration; +import java.util.Date; import java.util.Locale; public class Main { + private static final String LOG_SEPARATOR = " - "; + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss.SSS]"); private static boolean isVerbose = false; + private static boolean isIncremental = false; public static void main(String[] args) { + if (args.length == 0) { + com.wbrawner.keecrack.gui.Main.main(new String[]{}); + return; + } ArgumentParser parser = ArgumentParsers.newFor("KeeCrack") .build() .description("Brute force KeePass database files"); @@ -24,13 +48,15 @@ public class Main { .help("Increase logging output") .action(Arguments.storeTrue()) .dest("verbose"); - parser.addArgument("--gui") + parser.addArgument("--incremental", "-i") .action(Arguments.storeTrue()) - .help("launch the graphical interface (ignores other options)") - .dest("gui"); + .help("Use pattern-based (incremental) guesses instead of a list of words from a file") + .dest("incremental"); parser.addArgument("--word-list", "-w") - .help("a file containing newline-separated words to use as the passwords") + .help("a file containing newline-separated words to use as the passwords, or the pattern to generate " + + "words from if the --incremental flag is set") .dest("wordlist") + .required(true) .metavar("WORD-LIST-FILE"); parser.addArgument("--key-file", "-k") .help("the key file to use with the database") @@ -44,6 +70,7 @@ public class Main { try { Namespace res = parser.parseArgs(args); isVerbose = res.getBoolean("verbose"); + isIncremental = res.getBoolean("incremental"); KeeCrack keeCrack = KeeCrack.getInstance(); keeCrack.setCrackingView(new CLICrackingView()); @@ -57,25 +84,38 @@ public class Main { keeCrack.setKeyFile(new File(keyfilePath)); } - String wordlistPath = res.getString("wordlist"); - if (wordlistPath != null) ; - keeCrack.setWordlistFile(new File(wordlistPath)); - - if (res.getBoolean("gui")) { - com.wbrawner.keecrack.gui.Main.main(new String[]{}); - } else { - keeCrack.attack(); + String wordlist = res.getString("wordlist"); + if (wordlist != null) { + if (isIncremental) { + keeCrack.setWordListPattern(wordlist); + } else { + keeCrack.setWordListFile(new File(wordlist)); + } } + + keeCrack.attack(); } catch (ArgumentParserException e) { parser.handleError(e); } } + private static void print(String message) { + System.out.print(dateFormat.format(new Date())); + System.out.print(LOG_SEPARATOR); + System.out.println(message); + } + + private static void error(String message) { + System.err.print(dateFormat.format(new Date())); + System.err.print(LOG_SEPARATOR); + System.err.println(message); + } + static class CLICrackingView implements CrackingView { @Override public void onPasswordGuess(String password) { if (isVerbose) - System.out.println("Guessing password: " + password); + print("Guessing password: " + password); } @Override @@ -95,17 +135,17 @@ public class Main { timeElapsed.toString().toLowerCase().substring(2), password ); - System.out.println(message); + print(message); } @Override public void onError(Code code) { String message = ""; switch (code) { - case ERROR_MISSING_DATABASE_FILE: + case ERROR_INVALID_DATABASE_FILE: message = "Please specify a database file that you have read access to"; break; - case ERROR_MISSING_WORD_LIST_FILE: + case ERROR_INVALID_WORD_LIST: message = "Please specify a word list file that you have read access to"; break; case ERROR_CRACKING_INTERRUPTED: @@ -115,7 +155,7 @@ public class Main { message = "An error occurred while trying to read one of the files"; break; } - System.err.println(message); + error(message); System.exit(code.ordinal()); } } diff --git a/keecrack-gui/build.gradle b/keecrack-gui/build.gradle index 4fed6ea..b2a4a88 100644 --- a/keecrack-gui/build.gradle +++ b/keecrack-gui/build.gradle @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ dependencies { compile project(':keecrack-lib') } diff --git a/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/CrackingController.java b/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/CrackingController.java index 10292d1..47dd4af 100644 --- a/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/CrackingController.java +++ b/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/CrackingController.java @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.wbrawner.keecrack.gui; import com.wbrawner.keecrack.lib.Code; diff --git a/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/Main.java b/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/Main.java index fad0418..36d5895 100644 --- a/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/Main.java +++ b/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/Main.java @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.wbrawner.keecrack.gui; import javafx.application.Application; @@ -10,15 +25,15 @@ import java.io.IOException; public class Main extends Application { + public static void main(String[] args) { + launch(args); + } + @Override public void start(Stage primaryStage) throws IOException { Parent root = FXMLLoader.load(getClass().getResource("/fxml/main.fxml")); primaryStage.setTitle("Keecrack"); - primaryStage.setScene(new Scene(root, 400, 400)); + primaryStage.setScene(new Scene(root, 400, 300)); primaryStage.show(); } - - public static void main(String[] args) { - launch(args); - } } diff --git a/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/MainController.java b/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/MainController.java index 2188bde..93404f0 100644 --- a/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/MainController.java +++ b/keecrack-gui/src/main/java/com/wbrawner/keecrack/gui/MainController.java @@ -1,3 +1,18 @@ +/* + * Copyright 2018 William Brawner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.wbrawner.keecrack.gui; import com.wbrawner.keecrack.lib.Code; @@ -6,10 +21,13 @@ import com.wbrawner.keecrack.lib.view.FormView; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.scene.Cursor; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; +import javafx.scene.control.RadioButton; import javafx.scene.control.TextField; +import javafx.scene.control.ToggleGroup; import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.Stage; @@ -21,14 +39,25 @@ import java.util.ResourceBundle; public class MainController implements Initializable, FormView { - @FXML private TextField database; - @FXML private TextField key; - @FXML private TextField wordlist; - @FXML private Button crackButton; + private static KeeCrack keeCrack; + @FXML + private TextField database; + @FXML + private TextField key; + @FXML + private TextField wordlist; + @FXML + private Button crackButton; + @FXML + private ToggleGroup wordlistType; + @FXML + private RadioButton wordlistFile; + @FXML + private RadioButton wordlistPattern; @Override public void initialize(URL location, ResourceBundle resources) { - KeeCrack keeCrack = KeeCrack.getInstance(); + keeCrack = KeeCrack.getInstance(); keeCrack.setFormView(this); database.setOnMouseClicked(event -> { if (KeeCrack.getInstance().isCracking()) { @@ -48,14 +77,7 @@ public class MainController implements Initializable, FormView { keeCrack.setKeyFile(keyFile); }); - wordlist.setOnMouseClicked(event -> { - if (KeeCrack.getInstance().isCracking()) { - return; - } - - File wordlistFile = getFile("Text Files", "txt"); - keeCrack.setWordlistFile(wordlistFile); - }); + updateWordListHandler(); crackButton.setOnMouseClicked(event -> { try { @@ -79,19 +101,21 @@ public class MainController implements Initializable, FormView { return null; }); Parent root = loader.load(); - stage.setScene(new Scene(root, 200, 200)); + stage.setScene(new Scene(root, 400, 350)); stage.showAndWait(); } catch (IOException e) { e.printStackTrace(); } }); + wordlistType.selectedToggleProperty().addListener((observableValue, oldToggle, newToggle) -> + updateWordListHandler()); if (keeCrack.getDatabaseFile() != null) onDatabaseFileSet(keeCrack.getDatabaseFile().getName()); if (keeCrack.getKeyFile() != null) onKeyFileSet(keeCrack.getKeyFile().getName()); - if (keeCrack.getWordlistFile() != null) - onWordListFileSet(keeCrack.getWordlistFile().getName()); + if (keeCrack.getWordListName() != null) + onWordListSet(keeCrack.getWordListName()); } @@ -107,6 +131,26 @@ public class MainController implements Initializable, FormView { return fileChooser.showOpenDialog(stage); } + private void updateWordListHandler() { + wordlist.clear(); + if (wordlistFile.isSelected()) { + wordlist.setEditable(false); + wordlist.setCursor(Cursor.HAND); + wordlist.setOnMouseClicked(event -> { + if (KeeCrack.getInstance().isCracking()) { + return; + } + File wordlistFile = getFile("Text Files", "txt"); + keeCrack.setWordListFile(wordlistFile); + }); + } else if (wordlistPattern.isSelected()) { + wordlist.setEditable(true); + wordlist.setCursor(Cursor.TEXT); + wordlist.setOnMouseExited(mouseEvent -> keeCrack.setWordListPattern(wordlist.getText())); + wordlist.setOnMouseClicked(null); + } + } + @Override public void onDatabaseFileSet(String name) { database.setText(name); @@ -118,7 +162,7 @@ public class MainController implements Initializable, FormView { } @Override - public void onWordListFileSet(String name) { + public void onWordListSet(String name) { wordlist.setText(name); } diff --git a/keecrack-gui/src/main/resources/fxml/cracking.fxml b/keecrack-gui/src/main/resources/fxml/cracking.fxml index 1eebebf..55362b7 100644 --- a/keecrack-gui/src/main/resources/fxml/cracking.fxml +++ b/keecrack-gui/src/main/resources/fxml/cracking.fxml @@ -1,18 +1,32 @@ + - - - - - - - + + + diff --git a/keecrack-gui/src/main/resources/fxml/main.fxml b/keecrack-gui/src/main/resources/fxml/main.fxml index 84aa5f0..e8b4347 100644 --- a/keecrack-gui/src/main/resources/fxml/main.fxml +++ b/keecrack-gui/src/main/resources/fxml/main.fxml @@ -1,61 +1,96 @@ + - + - - - - - - - - - - - - - - - - - - - -