Análisis Del Proceso De Ejecución De Código Remoto En Windows Mediante Archivos .LNK

Durante el mes de febrero Microsoft lanzó parches para nada más y nada menos que 99 CVEs. Entre estas vulnerabilidades una de ellas destacó particularmente: la identificada con CVE-2020-0729, basada en la ejecución de código remoto.

Uno de los aspectos que convierte a esta vulnerabilidad en algo tan llamativo es que, históricamente, los exploits para vulnerabilidades encontradas en archivos .LNK han sido usados para distribuir malware (como, por ejemplo, Stuxnet) y en la mayoría de los casos, el mero hecho de abrir una carpeta que contenga un archivo .LNK malicioso es suficiente para explotar la vulnerabilidad.

Inicio del análisis

Al iniciar el análisis los investigadores de TrendMicro se dieron cuenta de que al lanzar los parches, Microsoft no había incluido ninguna de las actualizaciones que le correspondían a las DLL normalmente asociadas al procesamiento de archivos LNK, como por ejemplo shell32.dll y windows.storage.dll. No obstante, sí que se podía encontrar la DLL StructureQuery.dll, en base a la que se han nombrado explícitamente vulnerabilidades como la que lleva el identificador CVE-2018-0825. Los investigadores descubrieron que los archivos LNK y esta DLL están relacionados en cuanto a que esta última es usada por Windows Search, que es precisamente donde los archivos LNK y la DLL StructureQuery vienen a encontrarse.

Características de los archivos .LNK

Los archivos .LNK se conocen principalmente por contener estructuras binarias que crean un atajo a un archivo o una carpeta, pero una funcionalidad menos conocida es que pueden contener una búsqueda almacenada. Normalmente, cuando un usuario busca un archivo en Windows 10, se muestra la opción «Buscar herramienta» para permitir al usuario afinar la búsqueda y seleccionar opciones avanzadas para la misma. Esta opción también permite a los usuarios guardar la búsqueda existente para reutilizarla en un futuro, lo cual genera un archivo XML con la extensión «.search-ms» al guardarlo, un formato de archivo que no se encuentra totalmente documentado.

Fuente de la imagen: Zero Day Initiative

Sin embargo, esta no es la única forma de guardar una búsqueda. Si se hace clic y se arrastra a otra carpeta el icono de resultados de búsqueda de la barra de direcciones (resaltado en la siguiente imagen), se crea un archivo .LNK que contiene una versión serializada de los datos que, de otra manera, almacenaríamos en un archivo XML, específicamente del tipo antes mencionado: «search-ms«.

Fuente de la imagen: Zero Day Initiative

Considerando estos aspectos, los investigadores realizaron un análisis del parche para StructureQuery usando BinDiff.

Fuente de la imagen: Zero Day Initiative

Tan solo una de las funciones cambia: StructuredQuery1::ReadPROPVARIANT(), y parece hacerlo de manera considerable, según se puede observar en las gráficas de flujo de la siguiente imagen:

Fuente de la imagen: Zero Day Initiative

Para saber lo que hace esta función en un archivo .LNK es necesario analizar las estructuras que podemos encontrar en un archivo .LNK de una búsqueda almacenada.

Los archivos link de Windows shell tienen múltiples componentes esenciales y opcionales. Cada archivo link de la shell debe tener, al menos, una cabecera de shell link (Shell Link Header), la cual tiene el siguiente formato:

Fuente de la imagen: Zero Day Initiative

El campo LinkFlags clarifica la ausencia de estructuras opcionales así como varias opciones como, por ejemplo, si las cadenas del archivo link shell están codificadas en Unicode o no. Lo que aparece a continuación es un ejemplo del campo LinkFlags:

Fuente de la imagen: Zero Day Initiative

La flag HasLInkTargetIDList, configurada en la mayoría de las ocasiones, se representa con la posición «A», el bit menos significativo del primer byte del campo LinkFlags. Si se configura, la estructura de LinkTargetIDList debe seguir a la cabecera de shell link. La estructura LinkTargetIDList especifica el objetivo del link y tiene la siguiente forma:

Fuente de la imagen: Zero Day Initiative

La estructura IDList contiene el formato de una lista de ID de objetos persistente:

Fuente de la imagen: Zero Day Initiative

El ItemIDList tiene la misma función que la ruta de un archivo, donde cada estructura de ItemID se corresponde con el componente de una ruta en una jerarquía. ItemID puede hacer referencia a sistemas de archivos de carpetas reales, carpetas virtuales como el Panel de Control o Búsquedas almacenadas, u otras formas de datos insertados que sirven como «atajo» para ejecutar funcionalidades específicas. Para la vulnerabilidad de la que hablamos resulta de particular importancia las estructuras ItemIDList e ItemID, presentes en un archivo .LNK que contiene una búsqueda almacenada.

Cuando un usuario crea un atajo que contiene información sobre una búsqueda, el archivo resultante contiene una estructura IDList que comienza con una Carpeta ItemID Delegada, seguida de lo denominado como «User Property View ItemID» para hacer búsquedas. En general, ItemID comienza de la siguiente manera:

Fuente de la imagen: Zero Day Initiative

El valor de los dos bytes al comienzo de 0x0004 es usado en combinación con ItemSize e ItemType para ayudar a determinar ItemID. Por ejemplo, si ItemSize es 0x14 e ItemType es 0x1F, los dos bytes de 0x0004 son comprobados para ver si su valor es mayor que el de ItemSize. De ser así, se asume que lo que queda de ItemID será un Identificador Único global de 16 bytes (GUID, por sus siglas en inglés). Esta es la estructura típica del primer ItemID encontrado en un archivo .LNK que apunta a otro archivo o carpeta. Si ItemSize es mayor que el tamaño requerido para contener una GUID pero menor que los bytes de 0x0004, los datos restantes tras el GUID se consideran ExtraDataBlock, el cual tiene un campo con un tamaño inicial de 2 bytes seguido por dicha cantidad de bytes.

En el caso de una carpeta ItemID delegada, esos 2 mismos bytes se corresponden con otro campo para el resto de la estructura, llevando a lo mostrado en la siguiente imagen:

Fuente de la imagen: Zero Day Initiative

Todos los GUID en los archivos .LINK son almacenados usando la representación RPC IDL para GUID. Dicha representación se traduce en que los tres primeros segmentos del GUID se almacenan como pequeñas representaciones finales del fragmento en su conjunto, mientras que cada byte en los dos últimos segmentos se considera que son individuales. Por ejemplo, el GUID {01234567-1234-ABCD-9876-0123456789AB} se representa de la siguiente manera en formato binario: \x67\x45\x23\x01\x34\x12\xCD\xAB\x98\x76\x01\x23\x45\x67\x89\xAB.

La carpeta ItemID delegada es seguida por User Property View ItemID, que tiene una estructura similar a la de la carpeta ItemID delegada:

Fuente de la imagen: Zero Day Initiative

De particular importancia resulta el campo PropertyStoreList, el cual, si está presente, contiene uno o más objetos serializados PropertyStore, cada uno de los cuales tiene la siguiente estructura:

Fuente de la imagen: Zero Day Initiative

El campo Property Store Data es una secuencia de propiedades. Todas las propiedades de cada uno de los Property Store Data pertenecen a la clase identificada por el GUID Property Format. Cada propiedad específica es identificada con un ID numérico conocido como Property ID o PID, el cual, cuando se combina con el GUID Property Format, es nombrado clave de propiedad o PKEY. La PKEY se determina de una forma ligeramente diferente si el GUID Property Format es igual a {D5CDD505-2E9C-101B-9397-08002B2CF9AE}. Cada propiedad, entonces, se considera que forma parte de una «bolsa de propiedades» y tiene la siguiente estructura:

Fuente de la imagen: Zero Day Initiative

Las bolsas de propiedades generalmente contienen elementos con los nombres “Key:FMTID” y “Key:PID”, identificando a la PKEY específica que determina la interpretación del resto de elementos. Las implementaciones de la Bolsa de Propiedades Específicas también requerirá que otros elementos estén presentes en orden para ser válidos.

Si el GUID Property Format no es igual al valor previamente mencionado para la bolsa de propiedades, cada propiedad será identificada con un valor íntegro del PID y tendrá la siguiente estructura: