我的进程(服务器)通过
CreateProcess
创建了一个子进程(客户端),并且我在这些进程之间进行 IPC。我从匿名管道开始,但很快我发现它不支持重叠操作,如here所述。
所以,命名管道是我的第二选择。我的困惑是:如果我创建一个命名管道,是否可以将该管道的访问限制为仅由先前调用
CreateProcess
创建的子进程?因此,即使另一个进程获得了管道的名称,它仍然无法读取或写入管道。
我的IPC使用仅限于本地机器和单一平台(Windows)。
顺便说一句,我可以更改这些进程的两个代码。
您可以使用
lpSecurityAttributes
参数显式地将ACL分配给新管道。 这将允许您确保,如果其他用户登录,他们无法连接到管道。
但是,如果您在父进程中创建管道的两端,那么不法行为的范围就很小,因此通常不需要显式设置 ACL。 一旦打开管道的客户端,任何其他进程都无法连接到该管道(如果您希望它们这样做,则必须创建第二个实例),因此只有一个非常短暂的时间间隔,在此期间另一个进程可以连接到该管道。干扰;即使发生这种情况,您也无法连接客户端,因此您会知道出了问题。
换句话说,攻击范围仅限于拒绝服务,并且由于攻击进程需要在同一台机器上运行,因此只需消耗 CPU 就可以实现更有效的拒绝服务。
请注意:
创建管道时应使用
FILE_FLAG_FIRST_PIPE_INSTANCE
标志,以确保您知道是否存在名称冲突。
出于显而易见的原因,您还应该使用
PIPE_REJECT_REMOTE_CLIENTS
。
命名管道的默认权限不允许其他非管理用户创建新实例,因此在这种情况下,中间人风格的攻击不存在风险。
以同一用户或管理用户身份运行的恶意进程,可能潜在地中间人您的连接(无论您是否设置 ACL),但因为任何此类恶意进程也可能注入如果恶意代码直接进入父级和/或子级,则无需担心。 攻击者已经位于气密舱口的错误一侧;锁窗户对你没有任何好处。
如果您的进程以提升的权限运行,您可能应该在管道上设置 ACL。 默认 ACL 可能允许以相同用户上下文运行的非提升进程来中间人连接。 您可以通过设置仅向管理员授予完全访问权限的 ACL 来解决此问题。 风险仍然很小,但在这种特殊情况下,纵深防御措施可能是合适的。
匿名管道被实现为具有唯一名称的命名管道,因此使用命名管道实际上并没有丢失任何内容。 原则上,攻击者可以像中间人一样轻松地攻击匿名管道。(编辑:根据 RbMm,这不再是事实。)
异步(重叠)操作当然完全受匿名管道支持。支持或不支持异步操作 - 仅取决于调用
FILE_SYNCHRONOUS_IO_[NO]NALERT
和 ZwCreateNamedPipeFile
中使用的 ZwOpenFile
,但不依赖于具有管道的名称(或空)。 CreatePipe
使用 FILE_SYNCHRONOUS_IO_NONALERT
选项创建管道对 - 只是因为从此 api 返回的句柄不能在异步操作中使用。不幸的是 CreatePipe
没有参数来改变这种行为,但我们可以自己完成这个任务
从 vista 开始,我们可以创建匿名(未命名)和异步管道对,但为此您需要使用 ndll api。下一个代码几乎类似于
CreatePipe
内部代码,除了我创建异步管道对。
NTSTATUS CreatePipeAnonymousPair(PHANDLE phServerPipe, PHANDLE phClientPipe)
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
static UNICODE_STRING NamedPipe = RTL_CONSTANT_STRING(L"\\Device\\NamedPipe\\");
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &NamedPipe, OBJ_CASE_INSENSITIVE };
NTSTATUS status;
if (0 <= (status = ZwOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
oa.RootDirectory = hFile;
static LARGE_INTEGER timeout = { 0, MINLONG };
static UNICODE_STRING empty = {};
oa.ObjectName = ∅
if (0 <= (status = ZwCreateNamedPipeFile(phServerPipe,
FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA|
FILE_CREATE_PIPE_INSTANCE,
&oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_CREATE, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE,
FILE_PIPE_QUEUE_OPERATION, 1, 0, 0, &timeout)))
{
oa.RootDirectory = *phServerPipe;
oa.Attributes = OBJ_CASE_INSENSITIVE|OBJ_INHERIT;
if (0 > (status = ZwOpenFile(phClientPipe, FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
ZwClose(oa.RootDirectory);
*phServerPipe = 0;
}
}
ZwClose(hFile);
}
return status;
}
注意 hClientPipe 创建为 Inherited - 因此可以将其传递给子进程。另外,当您将在 ConnectNamedPipe 中使用 hServerPipe 时,您会得到
FALSE
和 GetLastError() == ERROR_PIPE_CONNECTED
(因为客户端已连接)/或者如果您将使用 FSCTL_PIPE_LISTEN
- 您得到 STATUS_PIPE_CONNECTED
- 这确实不是错误,但是好的代码