package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rc4" "encoding/base64" "encoding/hex" "flag" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" // X Packages "golang.org/x/crypto/argon2" // 3rd Party "github.com/fatih/color" ) func main() { verbose := flag.Bool("v", false, "Enable verbose output") encryptionType := flag.String("type", "", "The type of encryption to use [xor, aes256, rc4, null]") key := flag.String("key", "", "Encryption key") b64 := flag.Bool("base64", false, "Base64 encode the output. Can be used with or without encryption") input := flag.String("i", "", "Input file path of binary file") output := flag.String("o", "", "Output file path") mode := flag.String("mode", "encrypt", "Mode of operation to perform on the input file [encrypt,decrypt]") salt := flag.String("salt", "", "Salt, in hex, used to generate an AES256 32-byte key through Argon2. Only used during decryption") inputNonce := flag.String("nonce", "", "Nonce, in hex, used to decrypt an AES256 input file. Only used during decryption") flag.Usage = func() { flag.PrintDefaults() os.Exit(0) } flag.Parse() // Check to make sure the input file exists _, errInputFile := os.Stat(*input) if os.IsNotExist(errInputFile) { color.Red(fmt.Sprintf("[!]The file does not exist: %s", *input)) os.Exit(1) } shellcode, errShellcode := ioutil.ReadFile(*input) if errShellcode != nil { color.Red(fmt.Sprintf("[!]%s", errShellcode.Error())) os.Exit(1) } // Check to make sure an output file was provided if *output == "" { color.Red("[!]The -o output argument is required") os.Exit(1) } // Check to make sure the output directory exists dir, outFile := filepath.Split(*output) if *verbose { color.Yellow(fmt.Sprintf("[-]Output directory: %s", dir)) color.Yellow(fmt.Sprintf("[-]Output file name: %s", outFile)) } outDir, errOutDir := os.Stat(dir) if errOutDir != nil { color.Red(fmt.Sprintf("[!]%s", errOutDir.Error())) os.Exit(1) } if !outDir.IsDir() { color.Red(fmt.Sprintf("[!]The output directory does not exist: %s", dir)) } if *verbose { color.Yellow(fmt.Sprintf("[-]File contents (hex): %x", shellcode)) } if strings.ToUpper(*mode) != "ENCRYPT" && strings.ToUpper(*mode) != "DECRYPT" { color.Red("[!]Invalid mode provided. Must be either encrypt or decrypt") os.Exit(1) } // Make sure a key was provided if *encryptionType != "" { if *key == "" { color.Red("[!]A key must be provided with the -key parameter to encrypt the input file") os.Exit(1) } } var outputBytes []byte switch strings.ToUpper(*mode) { case "ENCRYPT": var encryptedBytes []byte switch strings.ToUpper(*encryptionType) { case "XOR": // https://kylewbanks.com/blog/xor-encryption-using-go if *verbose { color.Yellow(fmt.Sprintf("[-]XOR encrypting input file with key: %s", *key)) } encryptedBytes = make([]byte, len(shellcode)) tempKey := *key for k, v := range shellcode { encryptedBytes[k] = v ^ tempKey[k%len(tempKey)] } case "AES256": // https://github.com/gtank/cryptopasta/blob/master/encrypt.go if *verbose { color.Yellow("[-]AES256 encrypting input file") } // Generate a salt that is used to generate a 32 byte key with Argon2 salt := make([]byte, 32) _, errReadFull := io.ReadFull(rand.Reader, salt) if errReadFull != nil { color.Red(fmt.Sprintf("[!]%s", errReadFull.Error())) os.Exit(1) } color.Green(fmt.Sprintf("[+]Argon2 salt (hex): %x", salt)) // Generate Argon2 ID key from input password using a randomly generated salt aesKey := argon2.IDKey([]byte(*key), salt, 1, 64*1024, 4, 32) // I leave it up to the operator to use the password + salt for decryption or just the Argon2 key color.Green(fmt.Sprintf("[+]AES256 key (32-bytes) derived from input password %s (hex): %x", *key, aesKey)) // Generate AES Cipher Block cipherBlock, err := aes.NewCipher(aesKey) if err != nil { color.Red(fmt.Sprintf("[!]%s", err.Error())) } gcm, errGcm := cipher.NewGCM(cipherBlock) if err != nil { color.Red(fmt.Sprintf("[!]%s", errGcm.Error())) os.Exit(1) } // Generate a nonce (or IV) for use with the AES256 function nonce := make([]byte, gcm.NonceSize()) _, errNonce := io.ReadFull(rand.Reader, nonce) if errNonce != nil { color.Red(fmt.Sprintf("[!]%s", errNonce.Error())) os.Exit(1) } color.Green(fmt.Sprintf("[+]AES256 nonce (hex): %x", nonce)) encryptedBytes = gcm.Seal(nil, nonce, shellcode, nil) case "RC4": if *verbose { color.Yellow("[-]RC4 encrypting input file") } cipher, err := rc4.NewCipher([]byte(*key)) if err != nil { color.Red(fmt.Sprintf("[!]%s", err.Error())) os.Exit(1) } encryptedBytes = make([]byte, len(shellcode)) cipher.XORKeyStream(encryptedBytes, shellcode) case "": if *verbose { color.Yellow("[-]No encryption type provided, continuing on...") } encryptedBytes = append(encryptedBytes, shellcode...) default: color.Red(fmt.Sprintf("[!]Invalid method type: %s", *encryptionType)) os.Exit(1) } if len(encryptedBytes) <= 0 { color.Red("[!]Encrypted byte slice length is equal to or less than 0") os.Exit(1) } if *b64 { outputBytes = make([]byte, base64.StdEncoding.EncodedLen(len(encryptedBytes))) base64.StdEncoding.Encode(outputBytes, encryptedBytes) } else { outputBytes = append(outputBytes, encryptedBytes...) } case "DECRYPT": var decryptedBytes []byte switch strings.ToUpper(*encryptionType) { case "AES256": // https://github.com/gtank/cryptopasta/blob/master/encrypt.go if *verbose { color.Yellow("[-]AES256 decrypting input file") } // I leave it up to the operator to use the password + salt for decryption or just the Argon2 key if *salt == "" { color.Red("[!]A 32-byte salt in hex format must be provided with the -salt argument to decrypt AES256 input file") os.Exit(1) } if len(*salt) != 64 { color.Red("[!]A 32-byte salt in hex format must be provided with the -salt argument to decrypt AES256 input file") color.Red(fmt.Sprintf("[!]A %d byte salt was provided", len(*salt)/2)) os.Exit(1) } saltDecoded, errSaltDecoded := hex.DecodeString(*salt) if errShellcode != nil { color.Red(fmt.Sprintf("[!]%s", errSaltDecoded.Error())) os.Exit(1) } if *verbose { color.Yellow("[-]Argon2 salt (hex): %x", saltDecoded) } aesKey := argon2.IDKey([]byte(*key), saltDecoded, 1, 64*1024, 4, 32) if *verbose { color.Yellow("[-]AES256 key (hex): %x", aesKey) } cipherBlock, err := aes.NewCipher(aesKey) if err != nil { color.Red(fmt.Sprintf("[!]%s", err.Error())) } gcm, errGcm := cipher.NewGCM(cipherBlock) if err != nil { color.Red(fmt.Sprintf("[!]%s", errGcm.Error())) os.Exit(1) } if len(shellcode) < gcm.NonceSize() { color.Red("[!]Malformed ciphertext is larger than nonce") os.Exit(1) } if len(*inputNonce) != gcm.NonceSize()*2 { color.Red("[!]A nonce, in hex, must be provided with the -nonce argument to decrypt the AES256 input file") color.Red(fmt.Sprintf("[!]A %d byte nonce was provided but %d byte nonce was expected", len(*inputNonce)/2, gcm.NonceSize())) os.Exit(1) } decryptNonce, errDecryptNonce := hex.DecodeString(*inputNonce) if errDecryptNonce != nil { color.Red("[!]%s", errDecryptNonce.Error()) os.Exit(1) } if *verbose { color.Yellow(fmt.Sprintf("[-]AES256 nonce (hex): %x", decryptNonce)) } var errDecryptedBytes error decryptedBytes, errDecryptedBytes = gcm.Open(nil, decryptNonce, shellcode, nil) if errDecryptedBytes != nil { color.Red("[!]%s", errDecryptedBytes.Error()) os.Exit(1) } case "XOR": // https://kylewbanks.com/blog/xor-encryption-using-go if *verbose { color.Yellow(fmt.Sprintf("[-]XOR decrypting input file with key: %s", *key)) } decryptedBytes = make([]byte, len(shellcode)) tempKey := *key for k, v := range shellcode { decryptedBytes[k] = v ^ tempKey[k%len(tempKey)] } case "RC4": if *verbose { color.Yellow("[-]RC4 decrypting input file") } cipher, err := rc4.NewCipher([]byte(*key)) if err != nil { color.Red(fmt.Sprintf("[!]%s", err.Error())) os.Exit(1) } decryptedBytes = make([]byte, len(shellcode)) cipher.XORKeyStream(decryptedBytes, shellcode) default: color.Red("[!]Invalid method") os.Exit(1) } if len(decryptedBytes) <= 0 { color.Red("[!]Decrypted byte slice length is equal to or less than 0") os.Exit(1) } if *b64 { outputBytes = make([]byte, base64.StdEncoding.EncodedLen(len(decryptedBytes))) base64.StdEncoding.Encode(outputBytes, decryptedBytes) } else { outputBytes = append(outputBytes, decryptedBytes...) } } if *verbose { if *b64 { color.Green("[+]Output (string):\r\n") fmt.Println(fmt.Sprintf("%s", outputBytes)) } else { color.Green("[+]Output (hex):\r\n") fmt.Println(fmt.Sprintf("%x", outputBytes)) } } // Write the file err := ioutil.WriteFile(*output, outputBytes, 0660) if err != nil { color.Red(fmt.Sprintf("[!]%s", err.Error())) os.Exit(1) } color.Green(fmt.Sprintf("[+]%s %s input and wrote %d bytes to: %s", *encryptionType, *mode, len(outputBytes), *output)) }