Show Notes

175 - Pwn2Own Bugs and WAF Bypasses

Two vulns in Netgear RAX30 routers that were patched 2 days before the Pwn2Own draw. One was a LAN bug, the other a WAN issue.

Command Injection in puhttpsniff (LAN) When investigating the router, they found a service called puhhttpsniff that ran by default. When reversing it, they found it would take a user-agent string and use it to build a pudil command that gets passed to system(). It was not sanitized at all against command injection, and so you could simply set the user-agent string to a payload that broke out of the quotes and executed system commands. You’re limited to 255 bytes, but this is more than enough to get a reverse shell.

Firewall Misconfiguration (WAN) They discovered services on the router (such as ssh, telnet, etc.) were listening on both IPV4 and IPV6 addresses. While on IPV4 all ports are closed to WAN, on IPV6, things like telnet are open. There are some ip6table rules in place to restrict access, but they’re only applied on br0 (LAN) and WAN if a public IPV6 is provided. If a link-local address is used on WAN to connect to telnet for example, rules won’t be applied to it, and so anyone on the same network segment as the router can query the link-local address and connect to various services. They used this to exfiltrate /etc/passwd, and cracked the support account’s password. This got them a restricted shell in telnet, though escaping that was as trivial as using the hidden sh command.

A couple command injection bugs on the NetGear RAX30 router, straight forward IoT bugs.

The first, is in the DHCPREQUEST packet, the hostname is taken from the request, and filled into a lease structure. That lease structure is then used and the hostname sprintf‘d into a command line that is passed into system. Allowing an attacker to breakout and inject their own commands to be run.

The second issue, this one reachable on the WAN side, but requires a well-positioned attacker is in the firmwire upgrade process. On boot, the pucfu binary is run, and it makes a request over HTTPS to NetGear. In response it receives the URL for firmware upgrades and caches it. Later the pufwUpgrade binary is executed that reads the cached URL and downloads the file.

There are two problems, first is that despite using HTTPS the request explicitly turns certificate validation off. So a well-positioned attacker may be able to redirect or otherwise tamper with the response. The second issue is that like with the DHCPREQUEST this URL that is read from the cache is simply sprintf‘d into a command line string, allowing an attacker to escape and gain code execution.

Two parts to the post the vulnerability is a simple SQL injection, URL data winds up in the query. Nothing too special there. First part of the post covers the exploit crafting as they deal with a few issues that make exploitation more challenging. The second part of the post is when they discover the WAF was blocking their exploit from landing on the cloud deployments of the application.

So the first part, they had a few challenges to figure out.

  1. The injection allowed them to do a union based injection but the injected rows had to be integers. They used string_to_array to turn their strings into an array (multiple rows) of integers that could just be returned. Which leads into the second problem
  2. The rows were returned in random order. The order was based on the order asynchronous tasks completed in, this meant their string that was turned into many rows would be shuffled around, They got around this by adding the row index multiplied by 1000 so it wouldn’t clobber the ascii value. Allowing their one integer to contain both the original ascii and the row index so the proper order could be retrieved.
  3. Performance limitations meant that doing exfiltrated a single-row at a time was not time-feasible and asking for too many rows would timeout. To get around this they used a larger, BIGINT type, and crammed multiple rows into it, bitshifting the values.

The second part of the post is the “universal” WAF bypass.

They had this fun exploit, it would land against on-premisis deployments of the application, it would not however land against the cloud hosted instances. this was because of the AWS WAF blocking the SQL injection payload.

In researching how the WAFs worked they wondered if there was any SQL syntax that the WAF wouldn’t pick up on, and here enters native JSON support within the major database systems. What they found is intriguing, by injecting a JSON operation in front of their more traditional SQL injection they could bypass the WAF. I’m not entirely sure why this works, the authors say doing so “ threw the WAF into a loop and allowed us to supply malicious SQLi payloads”. Regardless, the impact here is that JSON operators resulted in the syntax parsing either failing or otherwise being shutdown. Which is cool, and even more surprising that this worked against multiple WAFs.

Great documentation of the process finding a WAF process, building up the final payload bit by bit.

The actual bug being exploited is this Spring bug. Its an SSTI so you can inject SpEL (Spring Expression Language) expressions which give you access to sort of “navigate” Java objects using standard dot notation and call functions, chain responses. The classic way to exploit this might be:

${T(java.lang.Runtime).getRuntime().exec("<my command here>")}

Unfortunately there is a WAF that must be bypassed and it is blocking the most direct route. Most of the post follows his path towards finding a bypass.

The first step, was “try the obvious,” or as I tend to say “test your assumptions”. Kinda the same idea, do the obvious things, test everything, and establish some ground truth/things that definitely work to work from.

Then we just get into the problem solving flow:

First, trying to gain access to an arbitrary class. This ended up being easy ${2.class} worked. From there he discovered that the common technique of using .forName was easily detected by the WAF and did not work because the ' and " were being transformed in some way. So they couldn’t use strings directly.

So the hunt for a way to instantiate a string begun…and died as the means to directly instantiate a new class were all blocked by the WAF. So they could not simply instantiate a new String, instead they found a round-about way to craft a String object containing a single character.

${(2.toString()+2).charAt(0).class.toString(99)}

Basically what they get here is access to the Character class in Java which contains a .toString method that takes in character value (avoiding the need for quotes) and returns a String.

  • 2.toString() giving an original instance of a string object
  • .charAt(0) returning a Character object for the first character
  • .class giving access to the Character class
  • .toString(99) - the desired toString class that will give a controllable output.

This string crafting method then became the basis for accessing arbitrary Classes, as now the forName method could be called without tripping the WAF. With arbitrary Classes the java.lang.Runtime could be accessed, then .getRuntime() which has the .exec method to execute commands.

Two parts to the post the vulnerability is a simple SQL injection, URL data winds up in the query. Nothing too special there. First part of the post covers the exploit crafting as they deal with a few issues that make exploitation more challenging. The second part of the post is when they discover the WAF was blocking their exploit from landing on the cloud deployments of the application.

So the first part, they had a few challenges to figure out.

  1. The injection allowed them to do a union based injection but the injected rows had to be integers. They used string_to_array to turn their strings into an array (multiple rows) of integers that could just be returned. Which leads into the second problem
  2. The rows were returned in random order. The order was based on the order asynchronous tasks completed in, this meant their string that was turned into many rows would be shuffled around, They got around this by adding the row index multiplied by 1000 so it wouldn’t clobber the ascii value. Allowing their one integer to contain both the original ascii and the row index so the proper order could be retrieved.
  3. Performance limitations meant that doing exfiltrated a single-row at a time was not time-feasible and asking for too many rows would timeout. To get around this they used a larger, BIGINT type, and crammed multiple rows into it, bitshifting the values.

The second part of the post is the “universal” WAF bypass.

They had this fun exploit, it would land against on-premisis deployments of the application, it would not however land against the cloud hosted instances. this was because of the AWS WAF blocking the SQL injection payload.

In researching how the WAFs worked they wondered if there was any SQL syntax that the WAF wouldn’t pick up on, and here enters native JSON support within the major database systems. What they found is intriguing, by injecting a JSON operation in front of their more traditional SQL injection they could bypass the WAF. I’m not entirely sure why this works, the authors say doing so “ threw the WAF into a loop and allowed us to supply malicious SQLi payloads”. Regardless, the impact here is that JSON operators resulted in the syntax parsing either failing or otherwise being shutdown. Which is cool, and even more surprising that this worked against multiple WAFs.

The title is all you really need on this one, the OTP was reflected in the cookies so no need to actually receive it.

} }