searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

降权启动进程

2024-06-17 09:28:27
39
0

降权启动进程

 

一 背景

在有些情况下,windows通过计划任务(通过服务也是如此)调起的进程是system权限的。而在system权限下进程可能会遇到某些问题:

1)通过注册表或expand 环境变量等方法得到的系统目录并不是我们想要的,例如通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径为C:\windows\system32\config\systemprofile\appdata\local;通过GetEnvironmentVariable获取TMP的路径为C:\Windows\TEMP等;这一类的目录包括且不限于:desktop, paograms, appdata, etc..

2)具有system权限的进程创建的子进程也是具有system权限的,这样子进程也会遇到上面第1点的问题

3GetEnvironmentVariable函数获取到的环境变量都是SYSTEM用户的

4)对HKEY_CURRENT_USER的部分注册表的写操作将会被重定向到HKEY_USERS.DEFAULT

二 解决方案

1. 解决目录问题

1)如何判断是在system

正常的方法当然是通过通过权限相关的API来判断,当然也可以有一些小技巧来判断。例如上面说到的通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径,非system权限下为C:\Users\username\AppData\Local,而在system下为C:\windows\system32\config\systemprofile\appdata\local。因此,代码如下:

 

bool IsSystemPrivilegeImp()

{

    static bool isSystemPrivilege = false;

    if (isSystemPrivilege)

    {

        return isSystemPrivilege;

    }

 

    char szPath[MAX_PATH] = {0};

    if (::SHGetSpecialFolderPathA(NULL, szPath, CSIDL_APPDATA, TRUE))

    {

        std::string flag("config\\systemprofile");

        std::string path(szPath);

        if (path.find(flag) != std::string::npos)

        {

            isSystemPrivilege = true;

        }

    }

 

    return isSystemPrivilege;

}

2)模拟当前登陆用户

想要获取的正确的目录,需要模拟当前登陆用户,获取登录用户的token,再调用SHGetSpecialFolderPath传入此token来获取。另外通过此token,还可以通过CreateProcessAsUser来创建登录用户权限的进程。

 

// 若执行成功,传出获取的token

bool ImpersonateLoggedOnUserWrapper(HANDLE& hToken)

{

    DWORD dwConsoleSessionId = WTSGetActiveConsoleSessionId();

    if (WTSQueryUserToken(dwConsoleSessionId, &hToken))

    {

        if (ImpersonateLoggedOnUser(hToken))

        {

            return true;

        }

    }

    return false;

}

3)获取正确的目录

通过上面获取的token传入SHGetSpecialFolderPath来获取CSIDL_LOCAL_APPDATA等正确的路径。网上资料说这个函数有时会失败,GetLastError返回5。而Windows提供的另外一个API叫做SHGetFolderPath,是可以正常获取路径的,因此我们使用后面这个API

 

BOOL WINAPI SHGetSpecialFolderPathWrapper(

    HWND hwnd,

    LPWSTR lpszPath,

    int csidl,

    BOOL fCreate

    )

{

    BOOL ret = FALSE;

    do

    {

        if (false == IsSystemPrivilegeImp())

        {

            if (SHGetSpecialFolderPath(hwnd, lpszPath, csidl, fCreate))

            {

                ret = TRUE;

                break;

            }

        }

 

        HANDLE hToken = NULL;

        if (false == ImpersonateLoggedOnUserWrapper(hToken))

        {

            break;

        }

 

        if (S_OK == SHGetFolderPath(NULL, csidl, hToken, SHGFP_TYPE_DEFAULT, lpszPath))

        {

            ret = TRUE;

 

            //使用完毕之后通过调用RevertToSelf取消模拟

            RevertToSelf();

 

            break;

        }

 

    } while (0);

 

    return ret;

}

2. 降权启动进程

判断当前是否是system权限,如果是system权限,则以普通管理员用户的权限重新把自己调起。

 

#define TokenLinkedToken 19

 

 

DWORD GetActiveSessionID()

{

 

    DWORD dwSessionId = 0;

    PWTS_SESSION_INFO pSessionInfo = NULL;

    DWORD dwCount = 0;

 

    WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);

 

    for(DWORD i = 0; i < dwCount; i++)

    {

        WTS_SESSION_INFO si = pSessionInfo[i];

        if(WTSActive == si.State)

        {

            dwSessionId = si.SessionId;

            break;

        }

    }

 

    WTSFreeMemory(pSessionInfo);

    return dwSessionId;

 

}

BOOL TriggerAppExecute(std::wstring wstrCmdLine/*, INT32& n32ExitResult*/)

{

    DWORD dwProcesses = 0;

    BOOL bResult = FALSE;

 

    DWORD dwSid = GetActiveSessionID();

 

    DWORD dwRet = 0;

    PROCESS_INFORMATION pi;

    STARTUPINFO si;

    HANDLE hProcess = NULL, hPToken = NULL, hUserTokenDup = NULL;

    if (!WTSQueryUserToken(dwSid, &hPToken))

    {

        PROCESSENTRY32 procEntry;

        DWORD dwPid = 0;

        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnap == INVALID_HANDLE_VALUE)

        {

            return FALSE;

        }

 

        procEntry.dwSize = sizeof(PROCESSENTRY32);

        if (Process32First(hSnap, &procEntry))

        {

            do

            {

                if (_tcsicmp(procEntry.szExeFile, _T("explorer.exe")) == 0)

                {

                    DWORD exeSessionId = 0;

                    if (ProcessIdToSessionId(procEntry.th32ProcessID, &exeSessionId) && exeSessionId == dwSid)

                    {

                        dwPid = procEntry.th32ProcessID;

                        break;

                    }

                }

 

            } while (Process32Next(hSnap, &procEntry));

        }

        CloseHandle(hSnap);

 

        // explorer进程不存在

        if (dwPid == 0)

        {

            return FALSE;

        }

 

        hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPid);

        if (hProcess == NULL)

        {

            return FALSE;

        }

 

        if(!::OpenProcessToken(hProcess, TOKEN_ALL_ACCESS_P,&hPToken))

        {

            CloseHandle(hProcess);

            return FALSE;

        }

    }

 

    if (hPToken == NULL)

        return FALSE;

 

    TOKEN_LINKED_TOKEN admin;

    bResult = GetTokenInformation(hPToken, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &dwRet);

 

    if (!bResult) // vista 以前版本不支持TokenLinkedToken

    {

        TOKEN_PRIVILEGES tp;

        LUID luid;

        if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))

        {

            tp.PrivilegeCount =1;

            tp.Privileges[0].Luid =luid;

            tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;

        }

        //复制token

 DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);

    }

    else

    {

        hUserTokenDup = admin.LinkedToken;

    }

 

    LPVOID pEnv =NULL;

    DWORD dwCreationFlags = CREATE_PRESERVE_CODE_AUTHZ_LEVEL;

 

    // hUserTokenDup为当前登陆用户的令牌

    if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))

    {

        //如果传递了环境变量参数,CreateProcessAsUser

        //dwCreationFlags参数需要加上CREATE_UNICODE_ENVIRONMENT

        //这是MSDN明确说明的

        dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;

    }

    else

    {

        //环境变量创建失败仍然可以创建进程,

        //但会影响到后面的进程获取环境变量内容

        pEnv = NULL;

    }

 

    ZeroMemory( &si, sizeof(si) );

    si.cb = sizeof(si);

    si.dwFlags = STARTF_USESHOWWINDOW;

    si.wShowWindow = SW_HIDE;

    ZeroMemory( &pi, sizeof(pi) );

 

    bResult = CreateProcessAsUser(

        hUserTokenDup,                     // client's access token

        NULL,    // file to execute

        (LPTSTR) wstrCmdLine.c_str(),                 // command line

        NULL,            // pointer to process SECURITY_ATTRIBUTES

        NULL,               // pointer to thread SECURITY_ATTRIBUTES

        FALSE,              // handles are not inheritable

        dwCreationFlags,     // creation flags

        pEnv,               // pointer to new environment block

        NULL,               // name of current directory

        &si,               // pointer to STARTUPINFO structure

        &pi                // receives information about new process

        ); 

 

 

    if(pi.hProcess)

    {

        CloseHandle(pi.hThread);

        CloseHandle(pi.hProcess);

    }

 

    if (hUserTokenDup != NULL)

        CloseHandle(hUserTokenDup);

    if (hProcess != NULL)

        CloseHandle(hProcess);

    if (hPToken != NULL)

        CloseHandle(hPToken);

    if (pEnv != NULL)

        DestroyEnvironmentBlock(pEnv);

 

    return TRUE;

}

 

0条评论
0 / 1000
w****n
6文章数
0粉丝数
w****n
6 文章 | 0 粉丝
w****n
6文章数
0粉丝数
w****n
6 文章 | 0 粉丝
原创

降权启动进程

2024-06-17 09:28:27
39
0

降权启动进程

 

一 背景

在有些情况下,windows通过计划任务(通过服务也是如此)调起的进程是system权限的。而在system权限下进程可能会遇到某些问题:

1)通过注册表或expand 环境变量等方法得到的系统目录并不是我们想要的,例如通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径为C:\windows\system32\config\systemprofile\appdata\local;通过GetEnvironmentVariable获取TMP的路径为C:\Windows\TEMP等;这一类的目录包括且不限于:desktop, paograms, appdata, etc..

2)具有system权限的进程创建的子进程也是具有system权限的,这样子进程也会遇到上面第1点的问题

3GetEnvironmentVariable函数获取到的环境变量都是SYSTEM用户的

4)对HKEY_CURRENT_USER的部分注册表的写操作将会被重定向到HKEY_USERS.DEFAULT

二 解决方案

1. 解决目录问题

1)如何判断是在system

正常的方法当然是通过通过权限相关的API来判断,当然也可以有一些小技巧来判断。例如上面说到的通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径,非system权限下为C:\Users\username\AppData\Local,而在system下为C:\windows\system32\config\systemprofile\appdata\local。因此,代码如下:

 

bool IsSystemPrivilegeImp()

{

    static bool isSystemPrivilege = false;

    if (isSystemPrivilege)

    {

        return isSystemPrivilege;

    }

 

    char szPath[MAX_PATH] = {0};

    if (::SHGetSpecialFolderPathA(NULL, szPath, CSIDL_APPDATA, TRUE))

    {

        std::string flag("config\\systemprofile");

        std::string path(szPath);

        if (path.find(flag) != std::string::npos)

        {

            isSystemPrivilege = true;

        }

    }

 

    return isSystemPrivilege;

}

2)模拟当前登陆用户

想要获取的正确的目录,需要模拟当前登陆用户,获取登录用户的token,再调用SHGetSpecialFolderPath传入此token来获取。另外通过此token,还可以通过CreateProcessAsUser来创建登录用户权限的进程。

 

// 若执行成功,传出获取的token

bool ImpersonateLoggedOnUserWrapper(HANDLE& hToken)

{

    DWORD dwConsoleSessionId = WTSGetActiveConsoleSessionId();

    if (WTSQueryUserToken(dwConsoleSessionId, &hToken))

    {

        if (ImpersonateLoggedOnUser(hToken))

        {

            return true;

        }

    }

    return false;

}

3)获取正确的目录

通过上面获取的token传入SHGetSpecialFolderPath来获取CSIDL_LOCAL_APPDATA等正确的路径。网上资料说这个函数有时会失败,GetLastError返回5。而Windows提供的另外一个API叫做SHGetFolderPath,是可以正常获取路径的,因此我们使用后面这个API

 

BOOL WINAPI SHGetSpecialFolderPathWrapper(

    HWND hwnd,

    LPWSTR lpszPath,

    int csidl,

    BOOL fCreate

    )

{

    BOOL ret = FALSE;

    do

    {

        if (false == IsSystemPrivilegeImp())

        {

            if (SHGetSpecialFolderPath(hwnd, lpszPath, csidl, fCreate))

            {

                ret = TRUE;

                break;

            }

        }

 

        HANDLE hToken = NULL;

        if (false == ImpersonateLoggedOnUserWrapper(hToken))

        {

            break;

        }

 

        if (S_OK == SHGetFolderPath(NULL, csidl, hToken, SHGFP_TYPE_DEFAULT, lpszPath))

        {

            ret = TRUE;

 

            //使用完毕之后通过调用RevertToSelf取消模拟

            RevertToSelf();

 

            break;

        }

 

    } while (0);

 

    return ret;

}

2. 降权启动进程

判断当前是否是system权限,如果是system权限,则以普通管理员用户的权限重新把自己调起。

 

#define TokenLinkedToken 19

 

 

DWORD GetActiveSessionID()

{

 

    DWORD dwSessionId = 0;

    PWTS_SESSION_INFO pSessionInfo = NULL;

    DWORD dwCount = 0;

 

    WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);

 

    for(DWORD i = 0; i < dwCount; i++)

    {

        WTS_SESSION_INFO si = pSessionInfo[i];

        if(WTSActive == si.State)

        {

            dwSessionId = si.SessionId;

            break;

        }

    }

 

    WTSFreeMemory(pSessionInfo);

    return dwSessionId;

 

}

BOOL TriggerAppExecute(std::wstring wstrCmdLine/*, INT32& n32ExitResult*/)

{

    DWORD dwProcesses = 0;

    BOOL bResult = FALSE;

 

    DWORD dwSid = GetActiveSessionID();

 

    DWORD dwRet = 0;

    PROCESS_INFORMATION pi;

    STARTUPINFO si;

    HANDLE hProcess = NULL, hPToken = NULL, hUserTokenDup = NULL;

    if (!WTSQueryUserToken(dwSid, &hPToken))

    {

        PROCESSENTRY32 procEntry;

        DWORD dwPid = 0;

        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnap == INVALID_HANDLE_VALUE)

        {

            return FALSE;

        }

 

        procEntry.dwSize = sizeof(PROCESSENTRY32);

        if (Process32First(hSnap, &procEntry))

        {

            do

            {

                if (_tcsicmp(procEntry.szExeFile, _T("explorer.exe")) == 0)

                {

                    DWORD exeSessionId = 0;

                    if (ProcessIdToSessionId(procEntry.th32ProcessID, &exeSessionId) && exeSessionId == dwSid)

                    {

                        dwPid = procEntry.th32ProcessID;

                        break;

                    }

                }

 

            } while (Process32Next(hSnap, &procEntry));

        }

        CloseHandle(hSnap);

 

        // explorer进程不存在

        if (dwPid == 0)

        {

            return FALSE;

        }

 

        hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPid);

        if (hProcess == NULL)

        {

            return FALSE;

        }

 

        if(!::OpenProcessToken(hProcess, TOKEN_ALL_ACCESS_P,&hPToken))

        {

            CloseHandle(hProcess);

            return FALSE;

        }

    }

 

    if (hPToken == NULL)

        return FALSE;

 

    TOKEN_LINKED_TOKEN admin;

    bResult = GetTokenInformation(hPToken, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &dwRet);

 

    if (!bResult) // vista 以前版本不支持TokenLinkedToken

    {

        TOKEN_PRIVILEGES tp;

        LUID luid;

        if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))

        {

            tp.PrivilegeCount =1;

            tp.Privileges[0].Luid =luid;

            tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;

        }

        //复制token

 DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);

    }

    else

    {

        hUserTokenDup = admin.LinkedToken;

    }

 

    LPVOID pEnv =NULL;

    DWORD dwCreationFlags = CREATE_PRESERVE_CODE_AUTHZ_LEVEL;

 

    // hUserTokenDup为当前登陆用户的令牌

    if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))

    {

        //如果传递了环境变量参数,CreateProcessAsUser

        //dwCreationFlags参数需要加上CREATE_UNICODE_ENVIRONMENT

        //这是MSDN明确说明的

        dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;

    }

    else

    {

        //环境变量创建失败仍然可以创建进程,

        //但会影响到后面的进程获取环境变量内容

        pEnv = NULL;

    }

 

    ZeroMemory( &si, sizeof(si) );

    si.cb = sizeof(si);

    si.dwFlags = STARTF_USESHOWWINDOW;

    si.wShowWindow = SW_HIDE;

    ZeroMemory( &pi, sizeof(pi) );

 

    bResult = CreateProcessAsUser(

        hUserTokenDup,                     // client's access token

        NULL,    // file to execute

        (LPTSTR) wstrCmdLine.c_str(),                 // command line

        NULL,            // pointer to process SECURITY_ATTRIBUTES

        NULL,               // pointer to thread SECURITY_ATTRIBUTES

        FALSE,              // handles are not inheritable

        dwCreationFlags,     // creation flags

        pEnv,               // pointer to new environment block

        NULL,               // name of current directory

        &si,               // pointer to STARTUPINFO structure

        &pi                // receives information about new process

        ); 

 

 

    if(pi.hProcess)

    {

        CloseHandle(pi.hThread);

        CloseHandle(pi.hProcess);

    }

 

    if (hUserTokenDup != NULL)

        CloseHandle(hUserTokenDup);

    if (hProcess != NULL)

        CloseHandle(hProcess);

    if (hPToken != NULL)

        CloseHandle(hPToken);

    if (pEnv != NULL)

        DestroyEnvironmentBlock(pEnv);

 

    return TRUE;

}

 

文章来自个人专栏
技术原理与方案
6 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
2
1