The previous post talked about using Java's
SecurityManager to prevent attackers from gaining access to sensitive resources. This is complicated by the fact that if the wrong permissions are granted, or a type confusion attack in the JVM is used, it's possible to turn off SecurityManager by calling
There should be a way to tell the JVM that once a SecurityManager is set, it should never be unset. But this isn't in the JVM itself right now, and adding it would mean redefining
java.lang.System itself. So let's go do that.
The example project is at https://github.com/wsargent/securityfixer.
The first step is to use the Java Instrumentation API. This will allow us to install a Java agent before the main program starts. In the Java agent, we'll intercept the
setSecurityManager method, and throw an exception if the security manager is already set.
The second step is Byte Buddy, a code generation tool that will create new bytecode representing the System class. Byte Buddy comes with an AgentBuilder that can be attached to the instrumentation instance. Byte Buddy uses ASM under the hood, but doesn't require raw manipulation of byte code and class files the way that ASM does – instead, you write interceptors and Byte Buddy will generate the corresponding byte code. From there, an interceptor appended to the bootstrap class path will be loaded before the actual JVM System class.
The interceptor class lives in its own package and is relatively simple:
The interesting bit is the configuration of the AgentBuilder. Byte Buddy is set up out of the box for class transformation and adding new methods and dynamic classes, not redefinition of static methods, so we have to flip a bunch of switches to get the behavior we want:
Finally, once that's done, we can write a simple test class:
And then running it with the agent enabled and pointing to the interceptor will give us the behavior we want:
Gives us the output:
The above is an example you can run in a test, but throwing an exception or even an error is not what you ultimately want to do. The system is corrupt and attacker code is being run. What you want to do is kill the JVM immediately and notify the system of intrusion: this is the DJB Way.
In this case,
System.exit is not what you want, because shutdown hooks are still run before the JVM exits. What you really want to do is call
Runtime.getRuntime().halt(-137) and then use the
-XX:OnError= command line flag to dump the core and check the exit status. If it's -137, then you have a confirmed intrusion, and should put your system on high alert.
Note that there are other places that must be locked down, such as
Policy.setPolicy, and you should also brainstorm for various other "fixed points" that would cause immediate termination in your security review. You may not be able to stop intrusion, but you'll make it that much harder to avoid detection.