Contact

A couple weeks ago (as of March 18th, 2017), I reported two Plug vulnerabilities to
the Elixir team. They quickly fixed the issues, and made a disclosure on the
forum.

I wanted to go into a little more detail, and cover the aspects I found
interesting. I also hate it when I can't find I workable PoC for a disclosure, so
that's here as well.

Arbitrary Code Execution in Cookie Serialization

This vulnerability is the less practical of the two, and probably hasn't
been exploitable in the wild. However, the method of exploitation hasn't
been covered yet and I thought it was particularly interesting. Here's most
of the original report:

Impact:

Users with the ability to forge session cookies may be able to achieve
arbitrary code execution.

Details:

The default "session" plug provides session cookie storage and serialization
functionality. Cookie data is validated via a signing mechanism, which
makes use of a secret_key_base token. The serialization process utilizes
Erlang's binary_to_term and term_to_binary functions, which will serialize
and deserialize any term or data structure, including partials and
function captures. A user with the ability to forge cookies (e.g. as
a result of a source code disclosure) can create cookies containing
these dangerous values. Function captures are enumerable, which means
that any instance in which a cookie value is enumerated is vulnerable
to code execution.

For example, consider an application that stores cart IDs in the session cookie
with the intention of doing something special for any ID over 10:

To achieve code execution, a malicious user could forge a cookie with
the following value:

%{"cart" => &({IO.inspect(System.cwd), &1, &2})}

The initial get_session call would return the function capture, and
the value &({IO.inspect(System.cwd), &1, &2}) would be stored in
cart_ids. The Enum.any? call would iterate over the capture, and
execute IO.inspect(System.cwd), printing the current working directory
to the log.

Reproduction Steps:

Set up a working Elixir and Phoenix environment.

Clone the following repository: https://github.com/GriffinMB/web

Install dependencies, and run the server. Navigate to the homepage
to view the default Phoenix Framework information.

Null Byte Injection in Plug.Static

This one has been covered a bit more fully, but here are the details I provided:

Impact:

Users with the ability to upload files served by the "static" plug can
bypass filetype restrictions, which may lead to cross-site scripting
and other arbitrary file upload exploits.

Details:

The "static" plug, used to serve assets in Plug-based web
frameworks, serves two primary functions: locating the requested
file, and setting the response content type. The asset content
type is set dynamically, using the Mime.from_path function.
For example, a request for the file "images/phoenix.png" will result
in a content type of "image/png." However, if the request is updated
to "images/phoenix.png%00.html," the resulting content type will be
set to "text/html."

The problem with this is that the mechanism for reading
the file, :prim_file.read_file_info, is null byte terminated. This
means that both "images/phoenix.png" and "images/phoenix.png%00.html"
will return the same static asset. So, if file upload functionality is
provided by the application, and the assets are served with the
"static" plug, a malicious user could do something like the following:

Upload a file, "evil.png," with embedded JavaScript

Request the file at "evil.png%00.html"

Achieve XSS

Reproduction Steps:

Set up a working Elixir and Phoenix environment.

Create a new Phoenix project, and run the server.

In the static images directory, create a file ("evil.png") with the
following content: