如何使用Boost Intercocess在服务和用户程序之间进行安全通信?

发布于 2025-02-10 20:06:03 字数 376 浏览 1 评论 0原文

我有一项具有本地系统特权的服务,我希望它能够与我的客户端应用程序进行通信,该应用程序是在无私人用户(我在Windows上)下运行的。我使用的是“共享内存”的boost :: intercosess,这实际上只是在引擎盖下创建内存映射的文件。现在,我只需使用generic_read和generic_write创建一个为每个人提供的内存映射文件,以便客户端可以与服务通信。但是,我想知道当前的安全风险是什么,以及如何使其安全?

首先,有人可以覆盖内存映射的文件中的数据,从而中断客户端和服务之间的通信。但是,我想知道是否可以更激烈的事情,例如有人将服务重定向到其他内存地址,通过编辑内存映射文件来执行恶意代码。我不将指针存储在内存映射的文件本身中,我只使用boost :: intercess offset_ptr和普通数据。

I have a service with LOCAL SYSTEM privileges, and I want it to be able to communicate with my client application, which is run under unprivileged users (I'm on Windows). I am using boost::interprocess for "shared memory", which really just creates memory-mapped files under the hood. Right now I simply create a memory-mapped file with GENERIC_READ and GENERIC_WRITE given to everyone, so that the client can communicate with the service. However, I'm wondering what the current security risks of that are, and how to make this secure?

For one, someone could overwrite the data in the memory mapped file, interrupting communication between the client and the service. But I'm wondering if something more drastic is possible, like someone redirecting the service to other memory addresses to execute malicious code by editing the memory-mapped file. I don't store pointers within the memory-mapped file itself, I only use the boost::interprocess offset_ptr and plain data.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

是的,人们对您的共享内存篡改可能会损害大多数应用程序。

不,使用offset_ptr并不适合此。

  • 作为一个琐碎的反面示例,请想象使用offset_ptr的链接列表或图形。如果恶意演员在该列表中列出了一个周期,则如果您的应用程序不保护它,它可能会导致无限循环。

  • offset_ptr仅允许将指针值解释为单独的进程空间中的等效地址,但没有提供对实际值的验证。一个简单的反示例是,当您在共享内存区域的有效范围之外使指针指向点。使用offset_ptr存储它并不重要,指针值仍然无效。

访问控制

您似乎是访问控制。这很难实现,因此只有有限的控制才能以 boost :: intercocess :: permissions

// In header: <boost/interprocess/permissions.hpp>


class permissions {
public:
  // construct/copy/destruct
  permissions(os_permissions_type) noexcept;
  permissions() noexcept;

  // public member functions
  void set_default() noexcept;
  void set_unrestricted() noexcept;
  void set_permissions(os_permissions_type) noexcept;
  os_permissions_type get_permissions() const noexcept;
};

默认权限是Windows或security_attributes security_attributes 代码> 644 posix上的filemode(u = rw,go = r)。

要限制对所有者(有效地创建共享内存对象的用户)的读写访问权限,请使用EG 0600

demo

coliru

#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/permissions.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <mutex>

// demo data structure
#include <boost/container/static_vector.hpp>
#include <boost/range/iterator_range.hpp>
#include <fmt/ranges.h>

namespace bip = boost::interprocess;
namespace bc = boost::container;

struct SharedData {
    struct Layout {
        using Lock = std::unique_lock<bip::interprocess_mutex>;

        bip::interprocess_mutex mutable mutex_;
        bc::static_vector<int, 1024> vec_{};

        Lock obtainLock() const { return Lock(mutex_); }

        void add(int i) {
            auto lk = obtainLock();
            vec_.push_back(i);
        }
    };

    bip::shared_memory_object _smo = [] {
        bip::permissions          _perms{0600}; // u=rw
        bip::shared_memory_object smo(bip::open_or_create, "demo_shared",
                                      bip::mode_t::read_write, _perms);
        smo.truncate(sizeof(Layout));
        return smo;
    }();
    bip::mapped_region _mr{_smo, bip::mode_t::read_write, 0, sizeof(Layout)};

    void add(int i) { raw().add(i); }
    auto list() {
        auto lk = raw().obtainLock();
        return std::make_pair(std::move(lk),
                              boost::make_iterator_range(raw().vec_));
    }

  private:
    Layout& raw() { return *reinterpret_cast<Layout*>(_mr.get_address()); }
};

int main(int argc, char** argv) {
    auto const addable = std::vector(argv + 1, argv + argc);
    SharedData data;

    auto dump = [&data] {
        auto [lk, ls] = data.list();
        fmt::print("Existing: {}\n", ls);
    };

    dump();

    for (auto i : addable)
    {
        fmt::print("Adding element: {}\n", i);
        data.add(std::stoi(i));

        dump();
    }
}

2 3 打印EG

$ ./build/sotest 1
Existing: []
Adding element: 1
Existing: [1]

$ ./build/sotest 2 3 4
Existing: [1]
Adding element: 2
Existing: [1, 2]
Adding element: 3
Existing: [1, 2, 3]
Adding element: 4
Existing: [1, 2, 3, 4]

和共享内存模式为-RW -------

stat /dev/shm/demo_shared 
  File: /dev/shm/demo_shared
  Size: 4144            Blocks: 16         IO Block: 4096   regular file
Device: 18h/24d Inode: 29          Links: 1
Access: (0600/-rw-------)  Uid: ( 2000/    sehe)   Gid: ( 2000/    sehe)
Access: 2022-06-28 00:25:47.167492907 +0200
Modify: 2022-06-28 00:25:10.559796466 +0200
Change: 2022-06-28 00:25:10.559796466 +0200
 Birth: -

“

Yes people tampering with your shared memory can compromise most applications.

No, using offset_ptr does not innoculate against that.

  • As a trivial counter example, imagine a linked list or graph using offset_ptr. If a malicious actor made a cycle in that list, it could lead to DoS by infinite loop if your application doesn't protect against that.

  • offset_ptr only allows pointer values to be interpreted as the equivalent addresses in separate process spaces, but it provides no validation of the actual value. A simple counter-example here is when you make the pointer point outside the valid range for the shared memory region. It doesn't matter that you store it using offset_ptr, the pointer value will still be invalid.

Access Control

What you seem to be after is access control. This is hard to achieve portably, and as such only limited control is present in Boost Interprocess, in the form of boost::interprocess::permissions:

// In header: <boost/interprocess/permissions.hpp>


class permissions {
public:
  // construct/copy/destruct
  permissions(os_permissions_type) noexcept;
  permissions() noexcept;

  // public member functions
  void set_default() noexcept;
  void set_unrestricted() noexcept;
  void set_permissions(os_permissions_type) noexcept;
  os_permissions_type get_permissions() const noexcept;
};

The default permissions are a null SECURITY_ATTRIBUTES on windows or 644 filemode on POSIX (u=rw,go=r).

To restrict read and write access to the owner (effectively the user creating the shared memory object) on linux, use e.g. 0600.

Demo

Coliru

#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/permissions.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <mutex>

// demo data structure
#include <boost/container/static_vector.hpp>
#include <boost/range/iterator_range.hpp>
#include <fmt/ranges.h>

namespace bip = boost::interprocess;
namespace bc = boost::container;

struct SharedData {
    struct Layout {
        using Lock = std::unique_lock<bip::interprocess_mutex>;

        bip::interprocess_mutex mutable mutex_;
        bc::static_vector<int, 1024> vec_{};

        Lock obtainLock() const { return Lock(mutex_); }

        void add(int i) {
            auto lk = obtainLock();
            vec_.push_back(i);
        }
    };

    bip::shared_memory_object _smo = [] {
        bip::permissions          _perms{0600}; // u=rw
        bip::shared_memory_object smo(bip::open_or_create, "demo_shared",
                                      bip::mode_t::read_write, _perms);
        smo.truncate(sizeof(Layout));
        return smo;
    }();
    bip::mapped_region _mr{_smo, bip::mode_t::read_write, 0, sizeof(Layout)};

    void add(int i) { raw().add(i); }
    auto list() {
        auto lk = raw().obtainLock();
        return std::make_pair(std::move(lk),
                              boost::make_iterator_range(raw().vec_));
    }

  private:
    Layout& raw() { return *reinterpret_cast<Layout*>(_mr.get_address()); }
};

int main(int argc, char** argv) {
    auto const addable = std::vector(argv + 1, argv + argc);
    SharedData data;

    auto dump = [&data] {
        auto [lk, ls] = data.list();
        fmt::print("Existing: {}\n", ls);
    };

    dump();

    for (auto i : addable)
    {
        fmt::print("Adding element: {}\n", i);
        data.add(std::stoi(i));

        dump();
    }
}

When run with e.g. sotest 1 2 3 prints e.g.

$ ./build/sotest 1
Existing: []
Adding element: 1
Existing: [1]

$ ./build/sotest 2 3 4
Existing: [1]
Adding element: 2
Existing: [1, 2]
Adding element: 3
Existing: [1, 2, 3]
Adding element: 4
Existing: [1, 2, 3, 4]

And the shared memory mode is -rw-------:

stat /dev/shm/demo_shared 
  File: /dev/shm/demo_shared
  Size: 4144            Blocks: 16         IO Block: 4096   regular file
Device: 18h/24d Inode: 29          Links: 1
Access: (0600/-rw-------)  Uid: ( 2000/    sehe)   Gid: ( 2000/    sehe)
Access: 2022-06-28 00:25:47.167492907 +0200
Modify: 2022-06-28 00:25:10.559796466 +0200
Change: 2022-06-28 00:25:10.559796466 +0200
 Birth: -

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文