Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Secure your Java apps from end to end, Part 1

The foundation of Java security: Virtual machine and byte code security

  • Print
  • Feedback

Page 4 of 4

  Method void main(java.lang.String[])
     0 new #3 <class java.lang.Float>
     3 dup
     4 ldc2_w #13 <Double 56.78>
     7 invokespecial #8 <Method java.lang.Float(double)>
    10 astore_1
    11 new #4 <Class java.lang.Integer>
    14 dup
    15 sipush 1234
    18 invokespecial #9 <Method java.lang.Integer(int)>
    21 astore_2
    22 getstatic #10 <Field java.io.PrintStream out>
    25 aload_1
    26 invokevirtual #12 <Method java.lang.String toString()>
    29 invokevirtual #11 <Method void println(java.lang.String)>
    32 return


The listing above contains the main() method's disassembled output. At byte code offset 25 in this method, the virtual machine loads a reference to an instance of Float that was created earlier (at offsets 0 to 10). That is the instruction we will modify. The disassembled output of the modified class is below:

  Method void main(java.lang.String[])
     0 new #3 <Class java.lang.Float>
     3 dup
     4 ldc2_w #13 <Double 56.78>
     7 invokespecial #8 <Method java.lang.Float(double)>
    10 astore_1
    11 new #4 <Class java.lang.Integer>
    14 dup
    15 sipush 1234
    18 invokespecial #9 <Method java.lang.Integer(int)>
    21 astore_2
    22 getstatic #10 <Field java.io.PrintStream out>
    25 aload_2
    26 invokevirtual #12 <Method java.lang.String toString()>
    29 invokevirtual #11 <Method void println(java.lang.String)>
    32 return


The class is identical byte for byte except for the instruction at offset 25, which loads a reference to an instance of the Integer class.

It is important to note that the result is still valid byte code, meaning the JVM can still execute the byte code without dumping core or heading off into space. The verifier, however, can tell that something has changed under the hood. On my system, I receive the following error when I try to load the class:

  Exception in thread "main" java.lang.VerifyError:
  (class: Test1, method: main signature: ([Ljava/lang/String;)V)
  Incompatible object argument for function call


If you turn the verifier off or if you could exploit a virtual machine flaw and pass the code by the verifier in systems in the wild, the illegal and possibly subversive code works. If I execute the command below I receive the answer 1234 -- the value of the Integer instance.

  java -noverify Test1


That example is as innocuous as can be, but the potential for harm is real enough. Techniques like those presented above, in conjunction with a virtual machine flaw that allows unverified code to be executed, make type confusion exploits possible.

Type confusion

The notion of type is integral to the Java programming language. Every value has an associated type, and the JVM uses its knowledge of a value's type to determine the set of operations that can be performed on the value.

Consistent application of type information is essential for virtual machine security. A type confusion attack, enabled by the introduction of malicious and unverified code, attempts to undermine this foundation of Java security by fooling the JVM into thinking that a block of memory representing one class instance is really an instance of another class. If the attack is successful, the application can then manipulate the instance's state in ways the class's designer never intended. That assault is called a type confusion attack because the virtual machine becomes confused as to the type of the compromised class.

If a class is properly verified, type confusion should never be possible. In the second listing above, the verifier caught the attempt and threw a VerifyError. As long as the verifier isn't turned off or bypassed, security is assured.

Fortunately, the last flaw in the Java byte code verifier of which I am aware was discovered and fixed in late 1999. Given this fact, you might assume you aren't in immediate danger; however, you'd be assuming too much.

While flaws are becoming few and far between, ample opportunity remains for slipping unverified code into an application. Remember, you can manually turn verification off. In the last two months I've come across three major Java applications that instruct the user to turn verification off in certain circumstances. One of those applications even has a significant RMI (remote method invocation) component (as you will learn later in the series, RMI allows network class loading to occur at places in applications that you might not expect). If you can avoid it, don't turn verification off.

Be assured

JVM security is an important facet of overall security. The discussion of unverified code and type confusion exploits should help you understand why. Without the assurance of properly verified downloaded code and a type system on which you can depend, secure computing of any kind is not possible.

Next month, I will explore another context of Java security: application security.

About the author

Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed applications in C++, Todd moved on to the Java programming language when it became the obvious choice for that sort of thing. In addition to writing, Todd is cofounder and chief architect of PointFire, Inc.

Read more about Tools & Methods in JavaWorld's Tools & Methods section.

  • Print
  • Feedback

Resources