Erlang Authenticated Remote Code Execution
Erlang is a programming language that I have tried to learn a few times in the past but never really dug in, that is, until recently.
Erlang is an interesting language because it has “built-in concurrency, distribution, and fault tolerance”. To me, this means that it does job queuing and distributed tasks right out of the gate.
A little bit of history
I first started digging into Erlang again from an attackers point of view at BSides Philadelphia 2016, where I talked about SolarWinds ORION. I was in the audience making last minute changes to my slides when I happened upon an “erlang-node” port (25672) listening. You can watch the video here (I’ve jumped ahead to the part about the Erlang port):
After I gave that talk, I asked around NoVA Hackers. Luckily one such individual did, and that’s why I love what NoVA Hackers has grown into, but this post isn’t about that.
With a little bit of back and forth with what I was trying to accomplish, we worked out the needed requirements for code execution.
Erlnag Nodes
Erlang uses “Nodes” to execution code. As far as I can tell (and I’ll probably be corrected) but you must run a node to execute any Erlang code, so everything written in Erlang (RabbitMQ, CouchDB, etc.) must be running a node. Erlang nodes are identified by their hostname. For my SolarWinds box, it’s WIN-PM0ID6F0AHN
. This is the most straightforward piece of information to acquire for the authenticated RCE.
The Cookie
The Erlang cookie SHOULD be the hardest to acquire. It’s essentially the shared secret password between nodes, and just a simple string. This cookie exists even if only a single node is spun up (such is the case for SolarWinds). It is most commonly stored in a file called .erlang.cookie
with nothing in the file other than the string. You can find it in home directories, userprofiles, project files and such. On Linux, it begining with the .
means that it’s automatically a “hidden” file, but on Windows it doesn’t mean much ;). RabbitMQ documents well where the key is located:
Example .erlang.cookie
file:
|
|
One Windows for Solar Winds it can be found in Program Data:
The Cluster Name
The cluster name is needed to join the cluster but this is more of a guessing game. However, luckily these are regularly named based on the service being offered. RabbitMQ in particular almost always has a cluster name of rabbit
.
The Node Port
This is actually pretty interesting that I haven’t dived into completely yet, but the port isn’t specified or default. There is some sort of discovery process so no matter the port the nodes are running on Erlang is able t find it. In the case of SolarWinds Orion’s RabbitMQ, its 25672
but I’ve seen erlang nodes on a number of other ports, usually particular to the project it’s being run for.
Remote Code Execution
A simple apt install erlang
gets you ready for exectuion. Once installed you need to put your .erlang.cookie
file in your home directory ~/
. You also need to resolve the hostname (Node name) you wil be connecting to.
Then it’s just running a couple commands:
test
is the node name you’ll be calling your attack box when connecting to the clusterrabbit
is the cluster name being connected toWIN-PM0ID6F0AHN
is the hostname I’m connecting to the cluster via
Start up Erlang with erl -sname test
:
|
|
Connecting to the cluster (the period on the end of the commands important terminators):
|
|
If you get a true
that means the connection was successful.
Code exeuction (starting calc of course):
|
|
And so, like any good code execution attack, calc.exe
is executed:
On Windows of course you can even do SMB shares:
|
|
Exiting the Erlang Shell is done with the init::stop function:
|
|
Warning: By running this code execution method you are running a node as well, as as long as you have the connection open, you can be exploited the same way you are exploiting the remote host.
The Bad, and the worse…
In many Docker instances the .erlang.cookie
file is statically assigned and easy to find in the code. This is incredibly dangerous to have a statically assigned Erlang cookie for anyone who uses your Docker image, however because Docker doesn’t automatically expose ports from the docker instance, this is actually a better scenario in many cases than running the erlang project without containers.
This is not always the case and just like private keys on github, really easy to find:
Searching for .erlang.cookie
on Github:
The Ugly
Changing the Erlang Cookie is a pain, and usually very deep in the project or setup, so once you have the file, you have future proof code execution.