domingo, 15 de julio de 2007

Integer Overflow en Java

En el artículo anterior sobre Integer Overflow, explicaba el concepto y mostraba algunos ejemplos en C. Pues bien, es curioso ver como un lenguaje como Java, considerado muy seguro, también permite este tipo de ataques. Veamos un sencillo ejemplo:




public class ShortOverflow
{
public static void main(String args[])
{
if(args.length<1)
return;

short num = Integer.valueOf(args[0]).shortValue();
System.out.println("num: "+num);

if(num>100)
System.out.println("num > 100");
else
System.out.println("num < 100");
}
}


La versión de máquina virtual usada es la siguiente:


$ java -version
java version "1.5.0_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-b03)
Java HotSpot(TM) Client VM (build 1.5.0_07-b03, mixed mode, sharing)


Si ejecutamos el ejemplo vemos como se produce el overflow sin que se produzca ninguna excepción:


$ javac ShortOverflow.java
$ java ShortOverflow 100
num: 100
num < 100

$ java ShortOverflow 101
num: 101
num > 100

$java ShortOverflow 100000
num: -31072
num < 100




Atención! se usa la clase Integer, no la clase Short que sí controla la excepción. Por lo que el problema se produce al realizar el cast de int a short, en la función shortValue(). Lo mismo ocurre si utilizamos el tipo Byte:





public class ByteOverflow
{
public static void main(String args[])
{
if(args.length<1)
return;

byte num = Integer.valueOf(args[0]).byteValue();
System.out.println("num: "+num);

if(num>100)
System.out.println("num > 100");
else
System.out.println("num < 100");
}
}



$ javac ByteOverflow.java
$ java ByteOverflow 100
num: 100
num < 100

$ java ByteOverflow 101
num: 101
num > 100

$ java ByteOverflow 1000
num: -24
num < 100





Sin embargo, el desbordamiento no se produce si se usa Integer.valueOf("...").intValue(), Short.valueOf("...").shortValue() o similar:




public class IntegerOverflow
{
public static void main(String args[])
{
if(args.length<1)
return;

int num = Integer.valueOf(args[0]).intValue();

System.out.println("num: "+num);

if(num>100)
System.out.println("num > 100");
else
System.out.println("num < 100");

}
}



$ javac IntegerOverflow.java
$ java IntegerOverflow 10
num: 10
num < 100

$ java IntegerOverflow 100000
num: 100000
num > 100

$ java IntegerOverflow 1000000000000000
Exception in thread "main" java.lang.NumberFormatException: For input string: "1000000000000000"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:459)
at java.lang.Integer.valueOf(Integer.java:553)
at IntegerOverflow.main(IntegerOverflow.java:9)




Krampo en Kriptopolis proporciona una par de enlaces interesantes. En el primero se documenta el comportamiento de shortValue():


http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Number.html#shortValue()


En un, muy interesante, segundo enlace se habla del tema:
http://mindprod.com/jgloss/overflow.html



Why does Java ignore overflow? Most computer hardware has no ability to automatically generate an interrupt on overflow. And some hardware has no ability to detect it. Java would have to explicitly test for it on every add, subtract and multiply, greatly slowing down production. Further ex-C programmers are very used to this cavalier ignoring of overflow, and commonly write code presuming that high order bits will be quietly dropped.

The Pentium has hardware overflow detect but no hardware interrupt. So if Java were to support overflow detect, inside the JVM implementation would need to add a JO *jump on overflow" instruction after every add and subtract, and special code to look at the high order bits of the 32x32->64 bit multiply. 64/32->32 bit division might need special handling.



Para finalizar, veamos un ejemplo ficticio donde se podría explotar este overflow. Supongamos un programa en Java que se utiliza para realizar recargas a teléfonos móviles. Este programa cargará una comisión de un euro en la recarga, y posteriormente, realizará dicha recarga.





public class RecargaMovil
{
public static void main(String args[])
{
if(args.length!=2)
{
System.out.println("Uso: prog [telefono] [importe]");
return;
}

// Aqui se encuentra el error del programador (o vulnerabilidad)
short importe = Integer.valueOf(args[1]).shortValue();
if(importe < = 10)
{
// Quitamos 1 euro de comision;)
importe -= 1;

if(importe<3)
{
System.out.println("Importe demasiado pequeño");
return;
}

System.out.println("Realizando recarga de "+importe+
" euros a "+args[0]);
// Realizar recarga ...
}
else
{
System.out.println("No se permite hacer recargas de mas de 10 euros");
}
}
}



$ java RecargaMovil 93123123 9
Realizando recarga de 8 euros a 93123123

$ java RecargaMovil 93123123 15
No se permite hacer recargas de mas de 10 euros







Si observamos con detenimiento el programa veremos la existencia de un overflow en "importe", al pasar de int a short. Dado que un short permite el almacenamiento de 2^16=65536 valores, o 2^15=32768 por admitir valores con signo, veamos como desbordarlo.



$ java RecargaMovil 98234948 -98302
Importe demasiado pequeño

$ java RecargaMovil 98234948 -98303
Importe demasiado pequeño

$ java RecargaMovil 98234948 -98304
Realizando recarga de 32767 euros a 98234948



Donde "importe" se desborda y nos permite hacer una recarga de 32767 euros:)


4 comentarios:

Anónimo dijo...

mmm... pues no me parece que sea un error de jvm mas bien parece un error de programacion (a proposito) por un lado y por otro no veo como poder explotar algo asi por lo tanto no creo que sea una vulnerabilidad

Anónimo dijo...

Algún ejemplo *practico*?

Anónimo dijo...

Las vulnerabilidades son errores de programación. Lo que hace falta ver es si este tipo de errores de programación se producen y si son explotables.

Anónimo dijo...

> Algún ejemplo *practico*?
He añadido un ejemplo ficticio.
Aunque lo interesante sería encontrar un error en software real.