Idea about implementing a SECCOMP alike mechanism in BEAM

Henrique Marcomini henrique4win@REDACTED
Wed Dec 11 01:01:19 CET 2019


Hi,

  I've been working with erlang/elixir for the past year and I really miss
SECCOMP like features in the BEAM. So I started an implementation based on
https://github.com/erlang/otp and I wanted to know what you people think
about
it.

  The idea is to provide a way to blacklist/whitelist function calls on a
process level, so for example if I'm evaluating human input for a
calculation,
I can guarantee that no other function except the ones that are necessary
are
called. Going further, in a case where my erlang cookie is leaked, I know
that only a limited set of functions are callable using rpc or node.spawn/2.

  The way I envision it (and I'm implementing it) is adding a byte to the
process struct with the following meaning:

   0 1 2 3 4 5 6 7
  +-+-+-+-+-+-+-+-+
  |E|M|S|I|U|U|U|U|
  +-+-+-+-+-+-+-+-+

  Where:
    E -> Whether the mechanism is active (0:Off/1:On)
    M -> Operation Mode (0:Whitelist/1:Blacklist)
    S -> Disable Spawn (0:Can spawn new process/1:Cannot spawn new process)
    I -> Whether a child process will inherit the
    U -> Unused

  There are some implicit rule in this byte:
    - M,S, and I are unused whether E is set to 0
    - I is unused if S is set to 1

  I choosed to use a byte because bitwise operation are cheap and are the
least
expensive way I could think, and bitmasks can be combined in a meaningful
way.

  The verification of this byte would occur at the apply function, so we can
check the byte every time a function is called. To know which function is
whitelisted/blacklisted I added an Eterm to the process struct. This Eterm
is a
NIL terminated list of tuples, each tuple contains two atoms representing
the
module name and the function name which is whitelisted/blacklisted.

  Probably a hashmap, or a binary tree of hashs would be quicker to search.
But I don't know if there is any good low level way to introduce it without
adding a lot of code to the code base.

  To implement process inherit capabilites, I added a verification on
spawn,
but there are some possible bypasses that would need to be treated latter
on.

  For example:

-------------------------------------------------------------------------------

  If there is a process running as a dynamic supervisor (P1), some other
process (P2) may send a message to spawn some worker (P5) and the father
process would be the supervisor (P1), which may not have the mechanism
active.

Diagram below:


             |                              |
             |  P1 - Dynamic Supervisor     | P2 (With active mechanism)
             V                              |
      ===============                       |
      | P3   | P4                           V
      V      V

  When P2 asks P1 to spawn a new worker, the diagram will look like the
following:


             |                              |
             |  P1 - Dynamic Supervisor     | P2 (With active mechanism)
             V                              |
      ===============                       |
      | P3   | P4   | P5                    V
      V      V      V

  Where P3, P4, and P5 are spawned with P1 as a parent, so it will not
inherit
any rules from P2. At this point P5 can execute any code and send a message
to
P2, bypassing the mechanism.

-------------------------------------------------------------------------------

  On another case a process (P1) on Node 1 which is under this mechanism may
spawn another process (P2) to Node 2, and then P1 spawns another process
(P3)
on Node 1. If process generated by spawns of other nodes are less secure
than
the process that called the spawn function, it will lead to privilege
escalation.

Diagram below:

 +---------------+              +---------------+
 |               |              |               |
 |    Node 1     |              |    Node 2     |
 |               |              |               |
 |        P1     |   spawn/2    |               |
 |   ------------> -------------> --------+     |
 |       calls   |              |         |     |
 |               |              |         | P2  |
 |               |              |         |     |
 |        P3     |   spawn/2    |  calls  |     |
 |   <---------- <------------- <---------+     |
 |               |              |               |
 +---------------+              +---------------+

  If P3 restrictions are less strict than P1, then P1 escalated privilege.

-------------------------------------------------------------------------------

  The code that I'm working is at https://github.com/Supitto/OTP/tree/maint
and it is built upon the maint branch. It still quite imature and have some
edges to trim (I'm still figthing with allocations and Eterms). But if this
idea is apreciated I will implement everything on the main branch. Also if
you can think of some other scenario where this mechanism is defeated,
please
infome me :D

Thanks,

Henrique Almeida Marcomini

Telegram -> @supitto
IRC (freenode) -> Supitto

Ps. The code on the repo may be not working (depends on the commit), but the
    idea is there.

Pps. I made everything in ASCII so to see it properly use monospace fonts
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20191210/744a01bd/attachment.htm>


More information about the erlang-questions mailing list