Windows 命名管道访问控制

6
我的进程(服务器)通过CreateProcess创建一个子进程(客户端),我正在这些进程之间进行IPC。我一开始使用匿名管道,但很快发现它不支持重叠操作,如这里所解释的那样。
因此,命名管道是我的第二选择。我的困惑是:如果我创建了一个命名管道,是否可能将此管道的访问权限限制为仅限于先前调用CreateProcess创建的子进程?因此,即使另一个进程获得了管道的名称,它仍然无法读取或写入该管道。
我的IPC使用仅限于本地机器和单个平台(Windows)。
顺便说一下,我可以更改这两个进程的代码。

在调用CreateNamedPipe时,将指向SECURITY_ATTRIBUTES结构的指针作为最后一个参数传递。这样可以根据需要精确限制访问权限。通过查看CreateNamedPipe的文档,这个问题本来很容易解决:lpSecurityAttributes参数的功能与CreatePipe相同... - Cody Gray
匿名管道当然完全支持异步(重叠)操作。支持异步操作或否-仅取决于在调用ZwCreateNamedPipeFile和ZwOpenFile时使用的FILE_SYNCHRONOUS_IO_[NO]NALERT,而不是管道的名称(或空)。CreatePipe使用FILE_SYNCHRONOUS_IO_NONALERT选项创建管道对-只因为从此API返回的句柄不能用于异步操作。不幸的是,CreatePipe没有参数来更改此行为,但我们可以自己完成此任务。 - RbMm
@CodyGray - 当然,我们可以设置一些非默认安全描述符,但是任何SD都无法限制对仅子进程的访问。 此外,拥有“SE_TAKE_OWNERSHIP_PRIVILEGE”的人可以更改管道上的SD。从另一方面来看,只有子进程可以访问管道的问题也存在着一些意义。 - RbMm
2个回答

4
您可以使用lpSecurityAttributes参数显式地为新管道分配ACL。这将确保,如果另一个用户已登录,则他们无法连接到管道。
但是,如果您在父进程中创建了管道的两端,那么恶意行为的余地就非常小,因此通常不需要显式设置ACL。一旦您打开了管道的客户端端口,其他进程就无法连接到管道(如果您想让它们这样做,您必须创建第二个实例),因此只有一个非常短暂的时间间隔,另一个进程才能干扰;即使发生这种情况,您也无法连接客户端端口,因此您会知道出了什么问题。
换句话说,攻击范围仅限于拒绝服务,而且由于攻击进程需要在同一台机器上运行,因此只需通过降低CPU性能就可以实现更有效的拒绝服务。
请注意:
  • 创建管道时应使用FILE_FLAG_FIRST_PIPE_INSTANCE标志,以确保您知道是否存在名称冲突。
  • 出于明显的原因,您还应该使用PIPE_REJECT_REMOTE_CLIENTS
  • 命名管道的默认权限不允许其他非管理员用户创建新实例,因此在这种情况下不存在中间人攻击的风险。
  • 作为同一用户或管理员用户运行的恶意进程可能会潜在地干扰您的连接(无论是否设置了ACL),但由于此类恶意进程也可以直接注入恶意代码到父进程和/或子进程中,所以担心这种情况没有什么用。攻击者已经处于不可透过的舱口错误的一侧;锁定窗户对您没有好处。
  • 如果您的进程具有提升的权限,则可能应在管道上设置ACL。默认ACL可能允许以相同用户上下文运行的非提升进程干扰连接。您可以通过设置只向管理员授予完全访问权限的ACL来解决此问题。风险仍然很小,但在这种特殊情况下,深度防御措施可能是适当的。
  • 匿名管道实现为具有唯一名称的命名管道,因此使用命名管道并没有失去任何东西。攻击者原则上可以像使用命名管道一样轻松地中间人攻击匿名管道。 (编辑:根据RbMm的说法,这不再是真实情况。)

1
匿名管道是使用具有唯一名称的命名管道实现的,这只在xp/win2003中是正确的。但从vista开始,匿名管道已经没有名称了。它们是如何创建的-在我的答案中。所以从vista开始(以及所有最新的系统),可以创建无法通过名称打开的匿名管道。不幸的是,CreatePipe没有用于创建异步管道的参数,并且限制一个管道仅写入,另一个管道仅读取。但是,如果使用本机API-从vista开始,可以自己创建真正的匿名管道。 - RbMm
你知道有没有官方提到这个事实的记录吗?我没看到过。 - Keith Russell
博客链接似乎已经失效了。有没有人能够恢复它? - sytech

0

匿名管道当然支持异步(重叠)操作。支持异步操作或不支持取决于在调用ZwCreateNamedPipeFileZwOpenFile时是否使用了FILE_SYNCHRONOUS_IO_[NO]NALERT,而不是管道的名称(或为空)。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 = &empty;

        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是作为继承创建的 - 因此可以将其传递给子进程。 另外,当你在ConnectNamedPipe中使用hServerPipe时,如果客户端已经连接,则会返回FALSEGetLastError() == ERROR_PIPE_CONNECTED / 或者如果你使用FSCTL_PIPE_LISTEN - 你会得到STATUS_PIPE_CONNECTED - 这真的不是错误而是正常代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接