Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
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
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.
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.
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.
Read more about Tools & Methods in JavaWorld's Tools & Methods section.