Home > database >  How to start a new process as user "NT AUTHORITY\Network Service"?
How to start a new process as user "NT AUTHORITY\Network Service"?

Time:12-01

I am trying to launch a new process as NT AUTHORITY\Network Service from a process that is running as NT AUTHORITY\System.

I have looked at other questions, such as the following, which does not provide a working example: CreateProcess running as user: "NT AUTHORITY/Network Service" without knowing the credentials?

And, I have come across some posts which talk about copying a token from a process that is already running as NT AUTHORITY\Network Service: Windows API and Impersonation Part 1 - How to get SYSTEM using Primary Tokens.

I wonder, is there a way to launch a process without having to depend on another process to copy a token from? Is there a way to hand-craft a token that can help launch a process as NT AUTHORITY\Network Service using CreateProcessAsUserW(), for example?

CodePudding user response:

I suggest you do it via a scheduled task and then delete the task after it runs (or maybe there is a one-shot setting you could use). While System has the create token privilege the NtCreateToken function is not part of the documented API and using it would be an enormous pain. If not a scheduled task then as a service (again even if you are only going to run it once).

CodePudding user response:

Is there a way to hand-craft a token that can help launch a process as NT AUTHORITY\Network Service

yes. by call NtCreateToken. but for this need have SE_CREATE_TOKEN_PRIVILEGE. but services.exe and services, even running as 'NT AUTHORITY\System' have not it. so you can not just call NtCreateToken. first you need find token with this privilege and only after this.

for get token with required privileges set we can use next code:

extern const SECURITY_QUALITY_OF_SERVICE sqos = {
    sizeof (sqos), SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
};

extern const OBJECT_ATTRIBUTES oa_sqos = { sizeof(oa_sqos), 0, 0, 0, 0, const_cast<SECURITY_QUALITY_OF_SERVICE*>(&sqos) };

NTSTATUS GetToken(_In_ PVOID buf, _In_ const TOKEN_PRIVILEGES* RequiredSet, _Out_ PHANDLE phToken)
{
    NTSTATUS status;

    union {
        PVOID pv;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    pv = buf;
    ULONG NextEntryOffset = 0;

    do 
    {
        pb  = NextEntryOffset;

        HANDLE hProcess, hToken, hNewToken;

        CLIENT_ID ClientId = { pspi->UniqueProcessId };

        if (ClientId.UniqueProcess)
        {
            if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, 
                const_cast<POBJECT_ATTRIBUTES>(&oa_sqos), &ClientId))
            {
                status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE, &hToken);

                NtClose(hProcess);

                if (0 <= status)
                {
                    status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE|TOKEN_QUERY, 
                        const_cast<POBJECT_ATTRIBUTES>(&oa_sqos), FALSE, TokenImpersonation, &hNewToken);

                    NtClose(hToken);

                    if (0 <= status)
                    {
                        status = NtAdjustPrivilegesToken(hNewToken, FALSE, const_cast<PTOKEN_PRIVILEGES>(RequiredSet), 0, 0, 0);

                        if (STATUS_SUCCESS == status)   
                        {
                            *phToken = hNewToken;
                            return STATUS_SUCCESS;
                        }

                        NtClose(hNewToken);
                    }
                }
            }
        }

    } while (NextEntryOffset = pspi->NextEntryOffset);

    return STATUS_UNSUCCESSFUL;
}

NTSTATUS GetToken(_In_ const TOKEN_PRIVILEGES* RequiredSet, _Out_ PHANDLE phToken)
/*  
Routine Description:
    try found process token with RequiredSet; duplicate and adjust privilege 
Arguments:
    RequiredSet - set of privileges which must be in token
    phToken - Impersonation Token with all privileges from RequiredSet, all it is enabled (even if some is disabled in original token)
--*/
{
    NTSTATUS status;

    ULONG cb = 0x40000;

    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PBYTE buf = new BYTE[cb  = PAGE_SIZE])
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                status = GetToken(buf, RequiredSet, phToken);

                if (status == STATUS_INFO_LENGTH_MISMATCH)
                {
                    status = STATUS_UNSUCCESSFUL;
                }
            }

            delete [] buf;
        }

    } while(status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}

with this we can do next:

#define BEGIN_PRIVILEGES(name, n) static const union { TOKEN_PRIVILEGES name;\
struct { ULONG PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[n];} label(_) = { n, {

#define LAA(se) {{se}, SE_PRIVILEGE_ENABLED }
#define LAA_D(se) {{se} }

#define END_PRIVILEGES }};};

BEGIN_PRIVILEGES(tp_dbg, 2)
    LAA(SE_DEBUG_PRIVILEGE),        // need for open processes
    LAA(SE_IMPERSONATE_PRIVILEGE),  // need for impersonate token
END_PRIVILEGES

BEGIN_PRIVILEGES(tp_cai, 3)
    LAA(SE_CREATE_TOKEN_PRIVILEGE),
    LAA(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE),
    LAA(SE_INCREASE_QUOTA_PRIVILEGE),
END_PRIVILEGES

EXTERN_C NTSYSCALLAPI NTSTATUS NTAPI NtCreateToken(
    _Out_ PHANDLE   TokenHandle,
    _In_ ACCESS_MASK    DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES     ObjectAttributes,
    _In_ TOKEN_TYPE     TokenType,
    _In_ PLUID      AuthenticationId,
    _In_ PLARGE_INTEGER     ExpirationTime,
    _In_ PTOKEN_USER    User,
    _In_ PTOKEN_GROUPS      Groups,
    _In_ PTOKEN_PRIVILEGES      Privileges,
    _In_opt_ PTOKEN_OWNER   Owner,
    _In_ PTOKEN_PRIMARY_GROUP   PrimaryGroup,
    _In_opt_ PTOKEN_DEFAULT_DACL    DefaultDacl,
    _In_ PTOKEN_SOURCE      TokenSource 
    );

NTSTATUS CreateServiceToken(HANDLE hToken, PHANDLE phToken)
{
    NTSTATUS status;
    PVOID stack = alloca(guz);
    PVOID buf = 0;
    ULONG cb = 0, rcb;

    struct {
        PTOKEN_GROUPS ptg;
        PTOKEN_STATISTICS pts;
        PTOKEN_DEFAULT_DACL ptdd;
        PTOKEN_PRIVILEGES ptp;
    } s;

    void** ppv = (void**)&s.ptp;

    static const ULONG rcbV[] = {
        sizeof(TOKEN_GROUPS) 0x80,
        sizeof(TOKEN_STATISTICS),
        sizeof(TOKEN_DEFAULT_DACL) 0x80,
        sizeof(TOKEN_PRIVILEGES) 0x80,
    };

    static TOKEN_INFORMATION_CLASS TokenInformationClassV[] = { 
        TokenGroups, 
        TokenStatistics,
        TokenDefaultDacl, 
        TokenPrivileges, 
    };

    ULONG n = _countof(TokenInformationClassV);

    do 
    {
        TOKEN_INFORMATION_CLASS TokenInformationClas = TokenInformationClassV[--n];

        rcb = rcbV[n], cb = 0;

        do 
        {
            if (cb < rcb)
            {
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            }

            status = NtQueryInformationToken(hToken, TokenInformationClas, buf, cb, &rcb);

        } while (status == STATUS_BUFFER_TOO_SMALL);

        if (0 > status)
        {
            return status;
        }

        *(ppv--) = buf, stack = buf;

    } while (n);

    
    static const SID NetworkService = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_NETWORK_SERVICE_RID } };
    static const TOKEN_OWNER to = { const_cast<SID*>(&NetworkService) };
    static const TOKEN_USER tu = { { const_cast<SID*>(&NetworkService) } };
    static const TOKEN_SOURCE ts = { {"Advapi"}, SYSTEM_LUID};

    return NtCreateToken(phToken, TOKEN_ALL_ACCESS, 0, TokenPrimary, 
        &s.pts->AuthenticationId, &s.pts->ExpirationTime, 
        const_cast<PTOKEN_USER>(&tu), s.ptg, s.ptp, const_cast<PTOKEN_OWNER>(&to), 
        (PTOKEN_PRIMARY_GROUP)&to, s.ptdd, const_cast<PTOKEN_SOURCE>(&ts));
}

NTSTATUS RunAsNetworkService()
{
    HANDLE hMyToken, hToken;
    NTSTATUS status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hMyToken);
    if (0 <= status)
    {
        if (0 <= (status = NtAdjustPrivilegesToken(hMyToken, FALSE, const_cast<PTOKEN_PRIVILEGES>(&tp_dbg), 0, 0, 0)))
        {
            if (0 <= (status = GetToken(&tp_cai, &hToken)))
            {
                status = RtlSetCurrentThreadToken(hToken);

                NtClose(hToken);

                if (0 <= status)
                {
                    if (0 <= (status = CreateServiceToken(hMyToken, &hToken)))
                    {
                        ULONG SessionId;
                        ProcessIdToSessionId(GetCurrentProcessId(), &SessionId);

                        if (0 <= (status = NtSetInformationToken(hToken, TokenSessionId, &SessionId, sizeof(SessionId))))
                        {
                            STARTUPINFO si = { sizeof(si) };
                            si.lpDesktop = const_cast<PWSTR>(L"WinSta0\\Default");
                            PROCESS_INFORMATION pi;
                            WCHAR cmdline[] = L"cmd /k whoami.exe /user";

                            if (CreateProcessAsUserW(hToken, 0, cmdline, 0, 0, 0, 0, 0, 0, &si, &pi))
                            {
                                NtClose(pi.hThread);
                                NtClose(pi.hProcess);
                            }
                            else
                            {
                                status = RtlGetLastNtStatus();
                            }
                        }

                        NtClose(hToken);
                    }

                    RtlSetCurrentThreadToken();
                }
            }
        }

        NtClose(hMyToken);
    }

    return status;
}

(code not use /RTCs )

  • Related