Keynote SSTIC 2009 – Code injection into a Javacard
Presentation : Jean-Louis LANET, Julien IGUCHI-CARTIGNY
Javacards include a Java VM. To what extent is it possible tobypassing normal application usage on these smart cards?
- The system uses strong typing, the bytecode is verified, therefore it is a priori more difficult to access arbitrary memory spaces.
- The applications are isolated from each other by the "« firewall«", which controls access rights from one application to another. Despite this, it turns out that only instances are filtered, not the classes themselves (and their static methods)!"
- Finally, loading an applet can only be done after prior authentication of the application.
What's interesting is that The bytecode verification is not performed on the card itself. (in most cases). It is then possible to modify the code after this verification, and then load it onto the card.
For example, to replace a call made in case of an invalid PIN code (standard application) the attack will be carried out in 3 steps.
1. Modify the CAP file (bytecode verified): By manipulating the Java stack, we are able to modify a method of our application, to make it return the memory address of the argument we pass to it.
This method is used to retrieve the address of the array that contains code to be executed, as well as the address of the "trojan" instance.
2. Modify the code itself (JVM): The Java linker will modify the addresses used in the code, particularly for referencing them. This behavior prevents the
specification of an arbitrary memory address.
Therefore, the "« reference location »"From the compiled file, remove the address translation from this reference.".
Thus, by placing an arbitrary address there, it will not be replaced, and modification of the javacard memory will be possible, it's a success.
3. Replace the "invoke" method to be erased by an invoke to the address of the array containing the bytecode which will do nothing (sort of like shellcode in short), using putstatic with the address of the array, as long as it is a valid method structure.
With all that, we just need to scan the memory for the pattern to nop, replace the invoke with our invoke "nop", which does nothing.
However, Not all cards are compatible., especially if they contain an integrated bytecode checker, the cards "commit suicide".
Other methods allow traversing memory, such as type casting (works quite well, but is based on a flaw in the VM)
Countermeasures exist; bytecode checks at runtime have been implemented for testing, and they work very well. It doesn't make the binary too big, but the execution time is lengthened.
The option remains to activate it only on certain sensitive methods.
The next step in this study: execute native code (exit the JVM) for example, to dump the ROM.
