How dangerous is Request Splitting, a vulnerability in Golang or how we found the RCE in Portainer and hacked Uber

Andrei Abakumov
4 min readMay 25, 2020

--

One night I was sitting and decided to look at the security of Uber infrastructure, launched a scanner and my eye caught on the host data-07.uberinternal.com I charged nmap, port 9000 was found. It was an obscure web service - Portainer.

A few minutes search and the victim was found at https://github.com/portainer/portainer. It turned out to be a popular open-source solution for Docker container orchestration. After playing with demo http://demo.portainer.io/ it became clear that Docker containers are managed through the Docker API, but it is not safe to allow access to the Docker API, so a TCP proxy scheme is used through the Websocket interface. Proxying is done without authentication because the request contains a unique container identifier — id . It acts as an API key to a particular container.

Proxy scheme

HTTP-upgrade request is sent:

This query is addressed to Docker api, POST /exec/uuid/start.
A Websocket connection is then established, which is proxied to the container via TCP and the user can run commands on the website.

After careful study of the source code SSRF vulnerability was found.

Container launch code

What’s wrong with this code?

The thing is that the user controls the execID and it is concatenated into the path of the HTTP request. This is the id parameter that is passed when the Websocket connection is established. So we can try to make Traversal POST /exec/…/anything/start . Great, now we control the path of the request to the Docker API, but what does it give us?
By reading the documentation from docs, it became clear that nothing. We are prevented from starting at the end of our request path.
After a little thought, I remembered about HTTP_pipelining. A technology that allows you to send multiple HTTP requests within the same TCP connection. To send multiple requests, you must be able to pass CRLF control characters.
@kyprizel and @buglloc were called for help.

0day in Golang

@buglloc found CRLF injection in standard Golang library — net/url . This is because the query parameters do not pass through urlencode . We reported this problem to the Golang security team and the patch was ready in just one day — https://go-review.googlesource.com/c/go/+/159157 They decided to cut out all control characters (this is ok, this is what many people do). It turns out that they already knew about this problem, made a fix, but accidentally rolled it back. Always check that you haven’t rolled back the security bugfix in new versions!
Example of a CRLF injection in http.NewRequest:

CRLF bug in Golang
POC of CRLF

This code shows the CRLF injection capability, which means that we can add our headers to the Docker API request.

This type of vulnerability is called Request Splitting.

HTTP-pipeline

Now we can use HTTP pipelining when requesting the Docker API.
As an example we will ask the Docker API to create an image by downloading it from our host jf2958pei4fqzxnwktp1snmvtmzcn1.burpcollaborator.net .
Kraft the request to the frontend by separating our headers with the line break character %0a.

HTTP Pipeline Request in query

Then the view request will arrive at the Docker API:

And on our server jf2958pei4fqzxnwktp1snmvtmzcn1.burpcollaborator.net we will receive an HTTP request for a Docker image.

So, Request Splitting helped us exploit the SSRF vulnerability and get the ability to make arbitrary requests to the local Docker API.
Information about this vulnerability was sent to Portainer author and Uber security Team. Fix from Portainer author. The vulnerability received a CVE-2018–12678. In addition, Portainer was found on Dr.Web servers to which we also reported the problem.

Hacking Uber’s server

Uber Security Team asked for more information and we decided to see what else we could do. The ability to create our own containers is certainly interesting, but it is more interesting to get the code executed on the host. It turns out that if we do not pass the image to the Docker API, he thinks that we are creating a container from scratch.

Finally, SSRF + CRLF + HTTP Pipeline + Docker API = RCE

How to hack Uber:

  • launch the container from scratch
  • mount all host directories
  • tunnel the host network
  • run the command
  • PROFIT!!!

We wrote exploit, sent to Uber Security Team, received the answer and reward:
It turns out that these servers had already been decommissioned from PEAK hosting back in 2015, so this was not our infrastructure. We do appreciate you bringing this to our attention and look forward to your next submission to our program!

--

--