23 Nov 2020 | Peter Stöckli
Remote code execution in Elixir-based Paginator
Intro
In August of this year I found a remote code execution vulnerability in the Elixir-based Paginator open-source project from Duffel (a UK-based startup in the flight searching space). The vulnerability has the CVE number CVE-2020-15150 assigned. Since Duffel seemed to use Paginator for its own REST API it seems likely that an attacker exploiting this vulnerability would have been able to execute code on Duffel’s (cloud) assets.
Vulnerability
This code execution vulnerability existed due to the use of Erlang’s binary_to_term
in combination with untrusted user data.
This function is much more dangerous when used in Elixir.
The vulnerability could have been triggered via Paginator’s user provided before/after cursors. As seen in Duffel’s Pagination API:
The string g2wAAAACbQAAABBBZXJvbWlzdC1LaGFya2l2bQAAAB=
is a Base64 encoded binary serialized Erlang term (ETF).
Such an Erlang term can contain anything from simple string values to full-blown functions containing almost any code you’d like.
However, in normal Erlang such a function provided in the payload would not be executed automatically (at least if nobody explicitly calls that function).
In Elixir there’s a much higher chance that such a function is executed later down the road, thanks to the Enumerable protocol of Elixir.
Exploits
To demonstrate this vulnerability I created two exploits.
The first one starts xcalc
:
The second one prints the stacktrace (so we see where our anonymous function has been triggered):
The functions above create a Base64 encoded exploit payload (same as the cursors used by Paginator). However, they do not include information about the whereabouts of the cursor, but instead contain an anonymous function that we want the server to execute. (An attacker would execute this functions above on his side, only providing the Base64 encoded payload to an API using Duffel’s Paginator.)
The stacktrace output of the second exploit payload looked like this (when executed from a unit test):
This stacktrace reveals that the exploit function was triggered on line 43 of query.ex by the function Enum.zip
:
our anonymous function is implicitly called by Elixir (thanks to the Enumerable protocol).
Additional information
This is not the first time a vulnerability caused by the use of binary_to_term
in combination with untrusted data has been found.
Griffin Byatt probably discovered the first publicly known:
Code execution through the session cookie in the popular
and widely used Elixir Plug.
The Security Working Group of the Erlang Ecosystem Foundation has some recommendations regarding Serialisation and deserialisation including recommendations for mitigations.
Thanks
Thanks are in order for Duffel (the maintainers of this project):
- Firstly: Duffel fixed the vulnerability in less than one day and acted very professionally throughout the process.
- Secondly: Despite not having a bug bounty program, Duffel payed a bounty of 1000 GBP, which I donated in parts to a fund providing help for victims of the explosion in the port of Lebanon.