Cada día aparecen nuevas amenazas en la red y ésta es una de las mas modernas y peligrosas.
Se define un rootkit como un programa, generalmente de carácter malicioso, que se esconde a si mismo o a otros camuflando su presencia mediante diversas técnicas; siempre añadiendo código al kernel del sistema; ya sea en forma de drivers o de módulos.
Existe una tercera forma, de la que daremos un ejemplo; que consiste en inyectar código dentro de un archivo JAVA que se instala al descargar y ejecutar una distribución cualquiera.
Lo que se propone aquí es inyectar nuestro código dentro de una de las clases de JAVA para modificar su comportamiento. El ejemplo de este artículo será inocuo; pero no obstante se podría ,incluso, ejecutar una consola remota y mandar comandos de distinta índole, con lo que la máquina afectada se vería secuestrada por el atacante. Hay diferentes tipos de rootkits.
Para este artículo se requieren unas nociones mínimas del lenguaje JAVA y un conocimiento de como funciona dicho lenguaje al ejecutar y compilar código. No obstante, si dichos requerimientos no se cumplen, el lector podrá disfrutar de los ejemplos y ver su funcionamiento.
Para empezar, escogeremos una clase que se encuentra en cualquier paquete JAVA de cualquier versión; como por ejemplo la clase Math o la clase PrintStream. La primera se utiliza para realizar operaciones matemáticas y la segunda para imprimir por consola.
– El primer paso sería localizar la clase; que se debería encontrar en los paquetes típicos de JAVA.
– El segundo paso sería desensamblarlo para obtener el bytecode.
– El tercer paso sería modificar dicho bytecode para agregar código y provocar la funcionalidad no deseada por el usuario final (la consola remota, la ejecución de troyanos, etc…).
– El cuarto paso, volver a ensamblarlo.
– Y el quinto y último paso copiarlo a su lugar original dentro su paquete.
Todos estos pasos serán detallados convenientemente e ilustrados con un ejemplo.
Pasos previos:
Es necesario tener instalado el JDK1 .6.0_18 de JAVA.
Realizar una copia de seguridad del paquete rt.jar de JAVA. Dicha copia es necesaria, porque con lo que se realiza en este manual vamos a sobreescribir dicho paquete con una clase con código inyectado.
Herramientas necesarias:
Necesitaremos 3 herramientas:
-Un editor de texto plano (el notepad de Windows sirve, pero desde REDESZone.net recomendamos Notepad++ o Scite
Descarga Notepad++
Descarga Scite
–Jasper Dissasemble. Este programa lo utilizaremos para obtener el bytecode del archivo .class (PrintStream.class).
–Jasmin assembler. Este otro programa lo utilizaremos para, una vez cambiado el código e inyectado, crear el .class final.
Primer paso: Localizar la clase.
La clase que en este caso necesitaremos utilizar se encuentra dentro del paquete rt de JAVA y se llama PrintStream. ¿Por qué hemos elegido esta clase?. La respuesta es sencilla: esta clase es ejecutada en un 99% de las aplicaciones JAVA de escritorio y web; si bien en aplicaciones web sólo tiene sentido en depuración; será utilizada igualmente, es una clase muy común. El motivo de que esa tan común es que se utiliza para imprimir por consola.
El paquete rt suele estar dentro de C:Archivos de programaJavajdk1.6.0_18jrelib ( en sistemas windows).
Cogemos el rt.jar y lo copiamos. Una vez copiado, nos vamos sobre dicha copia y lo abrimos con winrar o winzip. Nos debería salir algo como esto:
Nos vamos a la ruta java/io y ahí dentro encontraremos el archivo .class que estamos buscando (PrintStream.class) y lo extraemos a una carpeta de nuestra conveniencia.
Segundo paso: Obteniendo el bytecode
Para obtener el bytecode del archivo .class, lo que necesitamos es utilizar el programa Jasper. Este programa se utiliza por línea de comandos y el que nos interesa es:
java -jar Jasper.jar nombreClase.class
Sustituyendo nombreClase.class por el nombre de la clase que nos ocupa, PrintStream.class
Este programa creará una estructura de carpetas idéntica a la del paquete de JAVA donde se encuentre la clase en cuestión; es decir, si la clase se encuentra en el paquete myPaquete.directorio.nombreClase.class; entonces el programa creará la carpeta myPaquete, dentro de esa carpeta creará otra llamada directorio y dentro de esa otra carpeta estará un archivo llamado nombreClase con extensión .j
Con esta captura de pantalla se ve mas fácilmente:
Tercer paso: modificar el bytecode
Este es, quizás, el paso mas complicado de explicar. El bytecode de JAVA es el resultado del funcionamiento de dicho lenguaje: se utiliza este bytecode para ser ejecutado en cualquier máquina virtual de JAVA.
Un bytecode tiene este aspecto:
.method public
.limit stack 1
.limit locals 1
.var 0 is this Linit/PepeJframe; from LABEL0x0 to LABEL0x9
.line 12
LABEL0x0:
aload_0
invokespecial java/lang/Object/
.line 15
aload_0
invokevirtual init/PepeJframe/println()V
.line 16
return
LABEL0x9:
.end method
Es poco intuitivo y no demasiado fácil de modificar, en principio.
Lo que se recomienda en este manual, aunque no es imprescindible, es crear el código que queramos inyectar con cualquier IDE de JAVA (eclipse es el recomendado por redeszone), tomar el class generado por el compilador y descompilarlo con Jasper. Una vez realizado ese paso, podríamos coger el bytecode que nos interesa e inyectarlo en la clase que queramos modificar.
Nuestra modificación creará una simple ventana cuando ejecutemos el constructor de la clase, que inyectaremos nosotros también.
El bytecode de ese proceso es el expuesto arriba y que volvemos a repetir:
.method public
.limit stack 1
.limit locals 1
.var 0 is this Linit/RedesZoneJframe; from LABEL0x0 to LABEL0x9
.line 12
LABEL0x0:
aload_0
invokespecial java/lang/Object/
.line 15
aload_0
invokevirtual init/RedesZoneJframe/println()V
.line 16
return
LABEL0x9:
.end method
Este bytecode crea lo siguiente en código JAVA:
public RedesZoneJframe()
{
JFrame pepe=new JFrame(«Esto ha sido una inyección de código.»);
JPanel panel=new JPanel();
JLabel la=new JLabel(«Esta ventana es la prueba de la inyeccion de codigo»);
panel.add(la);
pepe.add(panel);
panel.setSize(100,100);
panel.setVisible(true);
pepe.setVisible(true);
pepe.setSize(400,400);
pepe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
Y esta captura es el resultado de la ejecución de dicho código:
Como se puede observar, es una simple ventana con un inocente mensaje, pero en este código inyectado podríamos haber programado que se lanzara un socket y se quedara a la escucha para ejecutar comandos; sólo la inventiva del atacante limita las posibilidades.
Cuarto paso: Ensamblando el bytecode para obtener el .class final
En este paso necesitamos utilizar el Jasmin para ensamblar de nuevo. Como el programa Jasper, el programa Jasmin se utliza bajo línea de comandos. El comando que debemos ejecutar es:
java -jar Jasmin.jar nombreClase.j
sustituyendo nombreClase.j por el nombre de nuestra clase, que sería PrintStream.j
El resultado de este programa es la creación de una carpeta con la misma estructura que al de Jasper y el archivo .class, PrintStream.class, que será la clase con el código inyectado.
Una vez realizado esto, sólo hemos de irnos al paquete que cogimos inicialmente, rt.jar, abrirlo con winrar e irnos a la carpeta
java/io/ y arrastrar nuestro PrintStream.class ahí y cerrar el winrar cuando termine. Una vez hayamos realizado esto, vamos a la carpeta
C:Archivos de programaJavajdk1.6.0_18jrelib y lo copiamos.
A partir de ahora, sólo tendríamos que realizar un programa que ejecutara el constructor de la clase PrintStream, en principio completamente inocua, esperar que una supuesta víctima ejecutara ese programa y ver los resultados.
Se pone un ejemplo de dicha clase aquí:
package init;
import java.io.PrintStream;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class RedesZoneJavaRootkit{
public RedesZoneJavaRootkit()
{
System.out.println(«Una prueba»);
}
public static void main (String [] args)
{
new PrintStream();//aquí se lanzaria nuestro codigo inyectado
new RedesZoneJavaRootkit();//comportamiento real del programa
}
Conclusiones
Como se puede observar, el programa que hemos realizado parece completamente válido y seguro: llama a una clase que es propiedad de Oracle y que no parece contener nada raro (PrintStream) pero sin embargo, gracias a nuestro código inyectado, el comportamiento de la clase cambia; en este caso de modo inocente, pero las posibilidades son infinitas.
Desde RedesZone esperamos que el artículo haya resultado instructivo, esperamos vuestros comentarios.