A Slice of PIE

Posted by Ian, Comments

On May 21, 2015 I gave a presentation at AppSec EU discussing security policies and managers, and specifically noting their utility in blocking known and unknown exploits. I noted that these tools tend to be difficult to use, and as a feature of my presentation introduced PIE, an open source tool for the painless generation of security policies.

In this post, I'm going to discuss how PIE works, walk through a simple use-case of creating a policy for the Java Security Manager, and show how it is able to block a remote code execution vulnerability in an old version of Struts 2 without any specific knowledge of Struts 2 or this vulnerability.

The Java Security Manager

Before we dig into PIE itself, it will help to know a bit about the Java Security Manager, which is responsible for enforcing the policy we'll generate. The Java Security Manager is a part of the JVM that has existed since the first version of Java was released in 1996. Its most common use has been to sandbox untrusted code (such as web applets) so that applications can't, for example, access filesystem resources and execute other processes.

The policy definition for the Security Manager allows for very granular and precise definitions of allowed actions, including conditions such as:

  • The code source, e.g. the class requiring the permission, or less precisely the JAR containing the executing code.
  • The permission class being requested (e.g. java.io.FilePermission or java.net.SocketPermission)
  • A "name" qualifier on the permission requested (e.g. "/tmp/foo.bar" or "foo.bar.example.com")
  • An "action" qualifier on the permission requested (e.g. "read,write" or "resolve,open")

The JDK documentation has a full list of the built-in permissions known to the Security Manager and describes the risk of granting those permissions to an application. Besides those, any application can define their own permission classes and apply security checks to those permissions:

System.getSecurityManager().checkPermission(new MyCustomPermission("contextualName", "contextualAction"));

Another very useful feature of the Java Security Manager is its application of the security policy to all "protection domains" on the stack. For our purposes, you can think of a ProtectionDomain as a class, but it more generally refers to a collection of classes with the same set of granted permissions (such as a JAR). By applying the permission check to all the protection domains on the stack, the Java Security Manager avoids the confused deputy problem (although there are ways a confused deputy may still shoot himself in the foot).

The result of this is an incredibly powerful tool that can be used to not just restrict untrusted code, but help protect trusted code against unintended behavior. For example, by creating a fine-grained policy that removes the option of creating a classloader and disallows the "execute" action from FilePermissions, the security manager can mitigate or even eliminate the exploitability of a remote code execution vulnerability. The downside of such a robust security solution is the difficulty inherent in using it. Not even considering custom permissions defined by frameworks your application may be using, writing a security policy for your application would involve deciding which of the 18 built-in permissions you need to whitelist, which of the dozens of potential "names" you need to add for each of those permissions, and you need to make those decisions for every class/JAR in your application.

As you can imagine, creating a precise policy can be extremely difficult and result in something quite unwieldy. In practice, this means many people will create a less granular policy, or simply forgo use of the Security Manager altogether.

Time For PIE

Recognizing the value of the Java Security Manager, but also recognizing the subtleties in using it well, we embarked on a project to simply and automatically generate a policy for use by the Java Security Manager. The goal of the project was to be able to generate security policies for applications in a way that makes a policy which is not overly permissive, not overly restrictive, and which can be created without reading every last line of the Security Manager's permission documentation or having to know every detail of the application being protected.

The result is PIE -- Policy Instantiation & Enforcement -- which, similar to system-level controls such as grsecurity and SELinux, includes a learning-mode where it observes the application's behavior and generates a policy based on its required permissions.

In order to manage complex policies, and to handle permissions which may have dynamic components (such as file paths and host names), PIE also uses heuristics to collapse and simplify policies, making them easier to read, verify, and manage.

A Use Case

To demonstrate how PIE can protect a web application, we're going to demonstrate how you can deploy PIE in a Tomcat container, and how its generated policy can protect against a remote code execution vulnerability. For this, we use Roller 5.0.0 which uses a version of Struts 2 which is vulnerable to CVE-2013-4212. Assuming that Roller is deployed locally, this vulnerability (an OGNL injection) can be exploited with the following request:

curl -s -X GET -G \
  http://localhost:8080/roller/roller-ui/login.rol \
  --data-urlencode "pageTitle=\${(#_memberAccess[\"allowStaticMethodAccess\"]=true,@java.lang.Runtime@getRuntime().exec('calc'),'')}"

To use PIE in a Tomcat container, all you need to do is the following:

  1. Download the PIE JARs and put them in Tomcat's lib directory.
  2. Restart Tomcat and exercise the application under intended usage. The more coverage your application gets, the more accurate the generated policy will be.
  3. Create a PIE configuration file which puts PIE in enforcement mode instead of learning mode. To do this, you can create a file in Tomcat's lib directory named pieConfig.properties with the line securityManager.isReportOnlyMode = false.
  4. Restart Tomcat. PIE protection will now be enabled!

Once you have done this, you can try running the above exploit again. This time no calculators will pop up on your screen, and if you inspect Tomcat's log you'll see the line:

Observed violation: ("ognl.OgnlInvokePermission" "invoke.com.opensymphony.xwork2.ognl.SecurityMemberAccess.setAllowStaticMethodAccess")

So without any knowledge of Struts, OGNL, Roller, or this vulnerability, PIE has effortlessly protected this application from a remote code execution attack.

A Second Helping of PIE

This article focused on introducing PIE and demonstrating the benefit of using it with the Java Security Manager. But PIE has a number of features built into it which make it useful in an even wider scope. As a first step for production deployment, you may want to use the generated policy but leave PIE in report-only mode. This way, any permission exceptions (which are likely to stem from gaps in your exercising the application) won't break the application and you can subsequently improve the security coverage of your end-to-end tests.

Additionally, PIE includes a Maven plugin which can be used to help verify, update, and maintain your application's security policy as part of the build process. PIE is also designed to be a general framework which can generate policies for more than just the Java Security Manager. Out of the box, PIE can also generate policies for CSP to protect your web application against XSS attacks. Both the Java Security Manager and CSP are written as modules for PIE, and it's easy to write modules for security managers specific to your application; included in the PIE source repository is an example of using PIE with Spring Security. In a follow-up article, I'll take a deeper dive into PIE, exploring in detail other use cases for PIE and how you can use these features.