{
	"id": "b6cf9499-c59c-485d-9e96-2aff8aaaf02d",
	"created_at": "2026-04-06T00:22:10.491468Z",
	"updated_at": "2026-04-10T03:24:23.904795Z",
	"deleted_at": null,
	"sha1_hash": "4ab24c0f0034eb5ef2bbcb06adaf941b4379889b",
	"title": "Staying Hidden on the Endpoint: Evading Detection with Shellcode | Mandiant",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1097754,
	"plain_text": "Staying Hidden on the Endpoint: Evading Detection with Shellcode\r\n| Mandiant\r\nBy Mandiant\r\nPublished: 2019-10-10 · Archived: 2026-04-05 22:28:44 UTC\r\nWritten by: Evan Pena, Casey Erikson\r\nTrue red team assessments require a secondary objective of avoiding detection. Part of the glory of a successful\r\nred team assessment is not getting detected by anything or anyone on the system. As modern Endpoint Detection\r\nand Response (EDR) products have matured over the years, the red teams must follow suit. This blog post will\r\nprovide some insights into how the FireEye Mandiant Red Team crafts payloads to bypass modern EDR products\r\nand get full command and control (C2) on their victims’ systems.\r\nShellcode injection or its execution is our favorite method for launching our C2 payload on a victim system; but\r\nwhat is shellcode? Michael Sikorski defines shellcode as a “…term commonly used to describe any piece of self-contained executable code” (Practical Malware Analysis). Most commercial Penetration Testing Frameworks such\r\nas Empire, Cobalt Strike, or Metasploit have a shellcode generator built into the tool. The shellcode generator is\r\ngenerally in either a binary format or hex format depending on whether you generate it as raw output or as an\r\napplication source.\r\nWhy do we use shellcode for all our payloads?\r\nThe use of shellcode in our red team assessment payloads allows us to be incredibly flexible in the type of payload\r\nwe use. Shellcode runners can be in written in a wide range of programming languages that can be incorporated\r\ninto many types of payloads. This flexibility allows us to customize our payloads to support the specific needs of\r\nour clients and of any given situation that may arise during a red team assessment. Since shellcode can be\r\nlaunched from inside a payload or injected into an already running process, we can use several techniques to\r\nincrease the ability of our payloads to evade detection from EDR products depending on the scenario and\r\ntechnology in place in the target environment. Several techniques exist for obfuscating shellcode, such as\r\nencryption and custom encoding, that make it difficult for EDR products to detect shellcode from commercial C2\r\ntools on its own. The flexibility and evasive properties of shellcode are the primary reason that we rely heavily on\r\nshellcode based payloads during red team assessments.\r\nShellcode Injection Vs. Execution\r\nOne of the most crucial parts of any red team assessment is developing a payload that will successfully, reliably,\r\nand stealthily run on the target system. Payloads can either execute shellcode from within its own process or inject\r\nshellcode into the address space of another process that will ultimately execute the shellcode. For the purposes of\r\nthis blog post we’ll refer to shellcode injection as shellcode executed inside a remote process and shellcode\r\nexecution as shellcode executed inside the payload process.\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 1 of 9\n\nShellcode injection is one technique that red teams and malicious attackers use to avoid detection from EDR\r\nproducts and network defenders. Additionally, many EDR products implement detections based on expected\r\nbehavior of windows processes. For example, an attacker that executes Mimikatz from the context of an arbitrary\r\nprocess, let’s say DefinitelyNotEvil.exe, may get detected or blocked outright because the EDR tool does not\r\nexpect that process to access lsass.exe. However, by injecting into a windows process, such as svchost.exe, that\r\nregularly touches lsass.exe, it may be possible to bypass these detections because the EDR product sees this as an\r\nexpected behavior.\r\nIn this blog post, we’ll cover three different techniques for running shellcode.\r\nCreateThread\r\nCreateRemoteThread\r\nQueueUserAPC\r\nEach of these techniques corresponds to a Windows API function that is responsible for the allocation of a thread\r\nto the shellcode, ultimately resulting in the shellcode being run. CreateThread is a technique used for shellcode\r\nexecution while CreateRemoteThread and QueueUserAPC are forms of shellcode injection.\r\nThe following is a high-level outline of the process for running shellcode with each of the three different\r\ntechniques.\r\nCreateThread\r\n1. Allocate memory in the current process\r\n2. Copy shellcode into the allocated memory\r\n3. Modify the protections of the newly allocated memory to allow execution of code from within that memory\r\nspace\r\n4. Create a thread with the base address of the allocated memory segment\r\n5. Wait on the thread handle to return\r\nCreateRemoteThread\r\n1. Get the process ID of the process to inject into\r\n2. Open the target process\r\n3. Allocate executable memory within the target process\r\n4. Write shellcode into the allocated memory\r\n5. Create a thread in the remote process with the start address of the allocated memory segment\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 2 of 9\n\nFigure 1: Windows API calls for CreateRemoteThread injection\r\nQueueUserAPC\r\n1. Get the process ID of the process to inject into\r\n2. Open the target process\r\n3. Allocate memory within the target process\r\n4. Write shellcode into the allocated memory\r\n5. Modify the protections of the newly allocated memory to allow execution of code from within that memory\r\nspace\r\n6. Open a thread in the remote process with the start address of the allocated memory segment\r\n7. Submit thread to queue for execution when it enters an “alertable” state\r\n8. Resume thread to enter “alertable” state\r\nFigure 2: Windows API calls for QueueUserAPC injection\r\nCommand Execution\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 3 of 9\n\nLet’s break down what we’ve talked about so far:\r\nMalicious code is your shellcode – the stage 0 or stage 1 code that is truly going to do the malicious work.\r\nStandard “shellcode runner” application which executes your code via either injection or execution. Most\r\neveryone writes their own shellcode runner, so we don’t necessarily deem this as true malware, the real\r\nmalware is the shellcode itself.\r\nNow that we’ve covered all that, we need a method to execute the code you compiled. Generally, this is either an\r\nexecutable (EXE) or a Dynamic Link Library (DLL). The Red Team prefers using Living Off the Land Binaries\r\n(lolbins) commands which will execute our compiled code.\r\nThe reason we can take advantage of lolbins is because of unmanaged exports. At a high level, when an\r\nexecutable calls a DLL it is looking for a specific export within the DLL to execute the code within that export. If\r\nthe export is not properly protected, then you can craft your own DLL with the export name you know the\r\nexecutable is looking for and run your arbitrary code; which in this case will be your shellcode runner.\r\nPutting It All Together\r\nWe set out to develop a shellcode runner DLL that takes advantage of lolbins through unmanaged exports while\r\nalso providing the flexibility to execute both injected and non-injected shellcode without a need to update the code\r\nbase. This effort resulted in a C# shellcode runner called DueDLLigence, for which the source code can be found\r\nat the GitHub page.\r\nThe DueDLLigence project provides a quick and easy way to switch between different shellcode techniques\r\ndescribed previously in this blog post by simply switching out the value of the global variable shown in Figure 3.\r\nFigure 3: Shellcode technique variable\r\nThe DueDLLigence DLL contains three unmanaged exports inside of it. These exports can be used with the\r\nRasautou, Control, and Coregen native Windows commands as described in Figure 4. Note: The shellcode that is\r\nin the example will only pop calc.\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 4 of 9\n\nNative Windows\r\nExecutable\r\nRequired Export\r\nName\r\nSyntax Used To Run\r\nRasautou Powershell rasautou –d {full path to dll} –p powershell –a a –e e\r\nControl Cplapplet\r\nRename compiled “dll” extension to “cpl” and just\r\ndouble click it!\r\nMSIExec Dllunregisterserver msiexec /z {full path to dll}\r\nFigure 4: DueDLLigence execution outline\r\nWhen you open the source code you will find the example uses the exports shown in Figure 5.\r\nFigure 5: Source code for exported entry points\r\nThe first thing you should do is generate your own shellcode. An example of this is shown in Figure 6, where we\r\nuse Cobalt Strike to generate raw shellcode for the “rev_dns” listener. Once that is complete, we run the base64 -\r\nw0 payload.bin \u003e [outputFileName] command in Linux to generate the base64 encoded version of the shellcode as\r\nshown in Figure 7.\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 5 of 9\n\nFigure 6: Shellcode generation\r\nFigure 7: Converting shellcode to base64\r\nThen you simply replace the base64 encoded shellcode on line 58 with the base64'd version of your own x86 or\r\nx64 shellcode. The screenshot in Figure 6 generated an x86 payload, you will need to check the “use x64 payload”\r\nbox to generate an x64 payload.\r\nAt this point you should reinstall the Unmanaged exports library in the DueDLLigence Visual Studio project\r\nbecause sometimes when you’re using a different project it doesn’t work properly. You can reinstall opening the\r\nNuGet package manager console shown in Figure 8 and running the Install-Package UnmanagedExports -Version\r\n1.2.7 command.\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 6 of 9\n\nFigure 8: Open NuGet Package Manager\r\nAfter you have reinstalled the Unmanaged exports library and replaced the base64 encoded shellcode on line 58\r\nthen you are ready to compile! Go ahead and build the source and look for your DLL in the bin folder. We\r\nstrongly suggest that you test your DLL to ensure it has the proper exports associate with it. Visual Studio Pro\r\ncomes with the Dumpbin.exe utility which you can run against your DLL to view the exports as shown in Figure\r\n9.\r\nFigure 9: Dumpbin.exe output\r\nYou can expand the list as much as you want with more lolbin techniques found over at the GitHub page.\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 7 of 9\n\nWe prefer to remove the unmanaged exports that are not going to be used with the respective payload that was\r\ngenerated so there is a smaller footprint in the payload. In general, this is good tradecraft when crafting payloads\r\nor writing code. In our industry we have the principle of least privilege, well this is the principle of least code!\r\nModern Detections for Shellcode Injection\r\nDespite all the evasive advantages that shellcode offers, there is hope when it comes to detecting shellcode\r\ninjection. We looked at several different methods for process injection.\r\nIn our shellcode runner, the shellcode injection techniques (CreateRemoteThread and QueueUserAPC) spawn a\r\nprocess in a suspended state and then inject shellcode into the running process. Let’s say we choose the process to\r\ninject into as explorer.exe and our payload will run with MSIExec. This will create a process tree where cmd.exe\r\nwill spawn msiexec.exe which will in turn spawn explorer.exe.\r\nFigure 10: Process tree analysis\r\nIn an enterprise environment it is possible to collect telemetry data with a SIEM to determine how often, across all\r\nendpoints, the cmd.exe -\u003e msiexec.exe -\u003e explorer.exe process tree occurs. Using parent-child process\r\nrelationships, defenders can identify potential malware through anomaly detection.\r\nAPI hooking is commonly used by EDR and AV products to monitor and for detect the use of Windows API calls\r\nthat are commonly used by malware authors. Utilizing kernel routines such as\r\nPsSetCreateProcessNotifyRoutine(Ex) and PsSetCreateThreadNotifyRoutine(Ex), security software can monitor\r\nwhen certain API calls are used, such as CreateRemoteThread. Combining this information with other data such as\r\nprocess reputation and enterprise-wide telemetry can be used to provide high fidelity alerts for potential malware.\r\nWhen process injection occurs, one process modifies the memory protections of a memory region in another\r\nprocess’s address space. By detecting the use of API calls such as VirtualProtectEx that result in one process\r\nmodifying the memory protections of address space allowed to another process, especially when the\r\nPAGE_EXECUTE_READWRITE permissions are used as this permission is used to allow the shellcode to be\r\nwritten and executed within the same memory space.\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 8 of 9\n\nAs red teamers and malicious actors continue to develop new process injection techniques, network defenders and\r\nsecurity software continue to adapt to the ever-changing landscape. Monitoring Windows API function calls such\r\nas VirtualAllocEx, VirtualProtectEx, CreateRemoteThread, and NTQueueAPCThread can provide valuable data\r\nfor identifying potential malware. Monitoring for the use of CreateProcess with the CREATE_SUSPENDED and\r\nCREATE_HIDDEN flags may assist in detecting process injection where the attacker creates a suspended and\r\nhidden process to inject into.\r\nAs we’ve seen, process injection techniques tend to follow a consistent order in which they call Windows API\r\nfunctions. For example, both injection techniques call VirtualAllocEx followed by WriteProcessMemory and\r\nidentifying when a process calls these two APIs in that order can be used as a basis for detecting process injection.\r\nConclusion\r\nUsing shellcode as the final stage for payloads during assessments allows Red Teams the flexibility to execute\r\npayloads in a wide array of environments while implementing techniques to avoid detection. The DueDLLigence\r\nshellcode runner is a dynamic tool that takes advantage of the evasive properties of both shellcode and process\r\ninjection to offer Red Teams a way to avoid detection. Detections for the execution of LOLbins on the command\r\nline and process injection at the API and process level should be incorporated into defensive methodology, as\r\nattackers are increasingly being forced into living off the land with the increased adoption of application\r\nwhitelisting.\r\nPosted in\r\nThreat Intelligence\r\nSecurity \u0026 Identity\r\nSource: https://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nhttps://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://www.mandiant.com/resources/staying-hidden-on-the-endpoint-evading-detection-with-shellcode"
	],
	"report_names": [
		"staying-hidden-on-the-endpoint-evading-detection-with-shellcode"
	],
	"threat_actors": [
		{
			"id": "610a7295-3139-4f34-8cec-b3da40add480",
			"created_at": "2023-01-06T13:46:38.608142Z",
			"updated_at": "2026-04-10T02:00:03.03764Z",
			"deleted_at": null,
			"main_name": "Cobalt",
			"aliases": [
				"Cobalt Group",
				"Cobalt Gang",
				"GOLD KINGSWOOD",
				"COBALT SPIDER",
				"G0080",
				"Mule Libra"
			],
			"source_name": "MISPGALAXY:Cobalt",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775434930,
	"ts_updated_at": 1775791463,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/4ab24c0f0034eb5ef2bbcb06adaf941b4379889b.pdf",
		"text": "https://archive.orkl.eu/4ab24c0f0034eb5ef2bbcb06adaf941b4379889b.txt",
		"img": "https://archive.orkl.eu/4ab24c0f0034eb5ef2bbcb06adaf941b4379889b.jpg"
	}
}