Alternate Data Streams en NTFS

 

 

Por Andy

En este artículo se tratará el tema de los "Alternate Data Streams", una característica del sistema de archivos NTFS de Microsoft. Se estudiarán sus ventajas e inconvenientes, se mostrarán formas básicas de trabajar con los mismos y por último se utilizarán herramientas de desarrollo gratuitas para crear algunos pequeños programas que permitan gestionar más en profundidad a estos componentes injustamente desconocidos de uno de los sistemas de ficheros más utilizados del mundo.

A los amantes de las interfaces gráficas os pido disculpas ya que en éste artículo se trabajará con la línea de comandos, excepto algún ejemplo puntual para ilustrar algún concepto.

Por último, haré uso experimental de una capacidad de HTML que Drupal deja pasar, e introduciré comentarios adicionales dentro de trozos de texto en itálica para no incrementar la longitud (bastante excesiva ya) del artículo. Sólo tenéis que dejar el cursor unos segundos sobre el texto y si tiene comentarios aparecerán. Probad con el de este mismo párrafo...

 

Introducción

Básicamente, los "Alternate Data Streams" o "Flujos Alternativos de Datos" son ficheros dentro de un fichero. Tradicionalmente pensamos en un fichero simplemente como en una secuencia ordenada de bytes accesibles por medio de un nombre. En NTFS (el filesystem por defecto de Windows) esto no es más que el flujo de datos que se obtiene por defecto al abrir el fichero de forma usual. Sin embargo NTFS tiene la capacidad de guardar flujos alternativos de datos (Alternate Data Streams o "ADS" para abreviar) junto al principal con sólo especificar un nombre interno para los mismos.

La sintaxis para referenciar flujos (streams) en ficheros de NTFS es la siguiente:

fichero[:flujo[:tipo]]

Donde, como es usual, lo que está entre "[" y "]" es opcional.

Para los ADS, uno de los varios tipos de flujos que puede haber dentro de un fichero en NTFS, el tipo correspondiente es "$DATA".

Obviamente si sólo se especifica fichero se obtendrá el flujo principal de datos, que es el que conocemos todos. Pero las cosas se ponen interesantes al utilizar la sintaxis fichero:flujo o fichero:flujo:$DATA. Curiosamente, el flujo principal de datos no tiene un nombre "de flujo", por lo que en NTFS, los siguientes son equivalentes:

fichero
fichero::$DATA

Lamentablemente la política de Microsoft con respecto a los ADS es que los mismos no necesitan o no deberían ser accedidos por el usuario final, sino sólo por aquellas aplicaciones que los utilicen. Por ende, no hay interfaces de usuario que reconozcan la sintaxis de los ADS y permitan trabajar directamente con ellos. Esto hace algo difícil acceder a los ADS sin escribir programas para manipularlos. Por ejemplo: el comando TYPE no sirve para ver por pantalla un ADS, ni el comando DEL permite borrar ADS, etc. Sin embargo cualquier programador puede acceder a los mismos con un simple open("fichero:flujo", O_RDONLY); o unlink("fichero:flujo");.

La elección del ":" como separador entre el nombre de fichero y el nombre de flujo es como mínimo desafortunada. Hay situaciones (por ejemplo J:TEXTO.TXT) en las cuales no se puede decidir a priori si nos estamos refiriendo a un fichero en la unidad de disco J: o a un flujo dentro de del fichero "J" en el directorio actual. El sistema siempre se decide por la primera opción. Para desambiguar es recomendable especificar la ubicación completa del fichero (por ejemplo C:\Prueba\J:TEXTO.TXT) cuando estamos tratando con ADS.

Hay que tener presente que los ADS son, en Windows, una característica exclusiva de NTFS. Si copiamos un fichero con ADS a una memoria USB con formato FAT32 o a un disquette, por ejemplo, sólo se copiará el flujo principal de datos y los demás flujos se perderán.

 

Trabajando con Alternate Data Streams

Si bien como se mencionó anteriormente la sintaxis de los ADS no es reconocida por las interfaces de usuario de la mayoría de los programas, hay algunas formas de experimentar sin demasiado esfuerzo.

Una cosa que *si* funciona son los operadores de redirección "<" y ">". Veamos algunos ejemplos abriendo una consola CMD:

C:\>mkdir Prueba

C:\>cd Prueba

C:\Prueba>echo Este es un fichero de texto > texto.txt

C:\Prueba>echo Este es un flujo alternativo de datos > texto.txt:flujo1.txt

C:\Prueba>type texto.txt
Este es un fichero de texto

C:\Prueba>type texto.txt:flujo1.txt
The filename, directory name, or volume label syntax is incorrect.

C:\Prueba>more < texto.txt:flujo1.txt
Este es un flujo alternativo de datos

C:\Prueba>notepad texto.txt:flujo1.txt
     (se abre Notepad en la pantalla gráfica)

Como habíamos visto antes, no puede utilizarse el comando TYPE para ver el contenido de un ADS. Sin embargo vemos que los operadores de redirección nos han permitido crear un ADS y luego verlo.

En el último ejemplo vemos que se puede utilizar NOTEPAD para editar normalmente el contenido de un ADS de texto. Es interesante notar que si queremos abrir el flujo dentro del Notepad utilizando File / Open, éste rechazará el nombre como erróneo. Esto es porque se analiza la cadena ingresada para verificar que sea un nombre de archivo válido (aunque no un nombre de ADS válido), mientras que cuando se invoca a Notepad pasándole un nombre de fichero como parámetro no se ejecuta este análisis, sino que se intenta abrir la cadena pasada como parámetro con open() que, como habíamos dicho antes, funciona bien con ADS. Otro punto interesante a notar es que se puede crear un nuevo ADS (no sólo editar uno ya existente) utilizando Notepad de la manera indicada.

No hace falta que los ADS sean de texto, sino que pueden ser cualquier cosa:

C:\Prueba>dir | find "texto.txt"
24/05/2009  20:25                30 texto.txt

C:\Prueba>type c:\windows\system32\calc.exe > texto.txt:calculadora.exe

C:\Prueba>dir | find "texto.txt"
24/05/2009  20:37                30 texto.txt

C:\Prueba>dir c:\windows\system32 | find "calc.exe"
04/08/2004  06:00           114.688 calc.exe

C:\Prueba>c:\Prueba\texto.txt:calculadora.exe
The filename, directory name, or volume label syntax is incorrect.

C:\Prueba>start texto.txt:calculadora.exe
Access is denied.

C:\Prueba>start c:\Prueba\texto.txt:calculadora.exe
     (la calculadora se ejecuta en la pantalla gráfica)

C:\Prueba>tasklist | find "calculadora"
texto.txt:calculadora.exe   3700                         0      2.672 K

Los ejemplos anteriores nos enseñan varias cosas:

  • Primero, que se puede "meter" un fichero binario como un ADS dentro de otro fichero con sólo usar el comando TYPE.
  • Segundo, que un mismo fichero puede tener varios ADS, cada uno con su nombre.
  • Tercero, que el tamaño de los ADS no se ve reflejado en los listados de directorio (ni en el Windows Explorer). Se puede apreciar que el tamaño reportado del fichero texto.txt se mantiene en 30 bytes antes y después de agregar el flujo correspondiente a la calculadora, que tiene más de 100Kb. Sin embargo la fecha del fichero si que se ve correctamente modificada (y el atributo "A" de "fichero modificado" también se actualiza correctamente, aunque no se vea en los listados).
  • Cuarto, que aunque no se puede ejecutar un ADS directamente, se puede utilizar el comando START para ejecutar un programa guardado como ADS dentro de un inocente fichero de texto. Sin embargo, el comando START "se confunde" si no se especifica el path completo al fichero como puede verse en el primer intento de ejecución. Moraleja: si un comando con ADS falla, volver a intentar con la ruta completa hasta el fichero.
  • Y quinto, que cuando se ejecuta un programa guardado como ADS dentro de un fichero, aparece la especificación completa en el TASKLIST, al igual que en el Task Manager. Esto es una mejora con respecto a Windows 2000, donde aparecía sólo el nombre del fichero, sin ninguna referencia al nombre del flujo.

 

Operaciones con Alternate Data Streams

El programa de backup de Windows, NTBACKUP, resguarda correctamente los ADS y obviamente los regenera correctamente en caso de un restore sobre un disco con formato NTFS.

Al copiar un fichero con ADS, los mismos se conservan siempre y cuando el fichero de destino resida en un disco con formato NTFS. Todos los métodos comunmente utilizados (COPY, ROBOCOPY, Windows Explorer, etc) copian correctamente los ADS. El fichero de destino no necesita estar en la misma máquina. Copiar ficheros utilizando la red por cualquiera de los métodos indicados anteriormente también conserva los ADS:

C:\Prueba>copy texto.txt \\server\share\copia.txt
        1 file(s) copied.

C:\Prueba>more < \\server\share\copia.txt:flujo1.txt
Este es un flujo alternativo de datos

Transferir un fichero por otros medios (FTP, email, etc) en general elimina los ADS, aunque esto depende del programa utilizado para la transferencia.

Lo mismo pasa si se comprime el archivo (7zip, Rar, Zip) y luego se vuelve a descomprimir.

Finalmente, y como se ha mencionado antes, copiar un fichero con ADS a un sistema de ficheros que no sea NTFS hace que se pierdan los mismos. Si se intenta la operación desde Windows Explorer saldrá una advertencia como la siguiente:

Si se intenta dicha copia desde la línea de comandos, los ADS se perderán sin ninguna advertencia.

 

El "lado oscuro" de los Alternate Data Streams

Los ADS son una facilidad más brindada por el sistema. No aumentan ni disminuyen la seguridad por ellos mismos ni tampoco son una vulnerabilidad del sistema. Sin embargo lo que si atenta contra la seguridad es el desconocimiento general que hay sobre los ADS, aún entre los expertos en informática.

Como puede verse, es muy fácil "esconder" un ejecutable dentro de otro fichero. Esto, unido a la política de Microsoft de que los ADS no necesitan ser accedidos por el usuario, y que el tamaño del fichero no refleja el tamaño de los ADS que contiene, hace difícil detectar y desactivar este tipo de tácticas para un usuario normal. Claro que crear ficheros normales con atributo HIDDEN, por ejemplo, también esconde los mismos de la vista de un usuario no experto.

Para crear un ADS dentro de un fichero, es necesario contar con permisos de escritura sobre el mismo. Por ende, es como siempre recomendable la utilización de usuarios no privilegiados para acceder al sistema.

A partir de Windows Vista y de Windows Server 2008, el comando DIR cuenta con el modificador /R para listar los ADS dentro de un fichero. Pero para los usuarios de sistemas operativos anteriores es bastante más difícil saber si quiera, si un fichero contiene ADS o no. A no desesperar: ya veremos algunas soluciones más adelante.

No hay una forma directa (en un Windows recién instalado, por ejemplo) de eliminar ADS de un fichero, por lo que aunque sepamos de la existencia de algún malware guardado como ADS dentro de un archivo de nuestro sistema, para eliminarlo deberíamos eliminar el fichero o quizás probar alguno de lo métodos de copia que no conservan los ADS vistos en el apartado anterior (moverlo a una partición o dispositivo en formato FAT/FAT32, por ejemplo). Esto a veces es imposible, como en el caso de ADS dentro de C:\Windows\System32\kernel32.dll por ejemplo.

Pero se pone peor: no sólo los ficheros pueden tener ADS... los directorios también. Por ejemplo:

C:\Prueba>mkdir subdir

C:\Prueba>dir | find "subdir"
24/05/2009  20:45    <DIR>          subdir

C:\Prueba>echo Flujo alternativo de datos en un subdirectorio > subdir:flujo.txt

C:\Prueba>more < subdir:flujo.txt
Flujo alternativo de datos en un subdirectorio

Para eliminar este flujo (sin programar) hay que utilizar las mismas tácticas que para un fichero (eliminar el directorio, moverlo a una partición o dispositivo no-NTFS, comprimirlo con Zip, etc) lo cual es tedioso si el mismo contiene muchos ficheros dentro. También se complica notablemente si alguien crea un ADS (quizás ejecutable) dentro de %ProgramFiles%, %SystemRoot% o %HOMEPATH% ya que no se pueden eliminar ni mover estas carpetas tan alegremente.

Aún se pone peor: el directorio raíz de un disco con formato NTFS también puede contener ADS. Por ejemplo (no lo intentéis en casa o quizás no lo podáis borrar):

C:\>echo Flujo alternativo de datos en el directorio raiz > c:\:flujo1.txt

C:\>more < c:\:flujo1.txt
Flujo alternativo de datos en el directorio raiz

También puede utilizarse la sintaxis :flujo para referirse a un ADS en el directorio actual.

Para un usuario normal, no es posible borrar un ADS en el directorio raíz de un disco sin formatear el mismo.

Más adelante os dejaré un pequeño programa que permite borrar ADS de ficheros o directorios, incluído el directorio raíz de una unidad NTFS.

Como se ha mencionado antes, crear ADS dentro de ficheros o directorios requiere permisos de escritura sobre estos últimos. Por eso es que normalmente un usuario no privilegiado o un proceso ejecutándose con las credenciales de este usuario no deberí­a poder crear ADS en ficheros o directorios del sistema.

Además de "esconder" un ejecutable malicioso dentro de un fichero o directorio "inocente", un atacante podría utilizar ADS con la esperanza de no ser detectado, y ocupar todo el espacio en disco en una especie de ataque de denegación de servicio. Si bien es relativamente fácil detectar y eliminar un fichero de varios Gb, es mucho más difícil encontrar un fichero de tamaño modesto, pero con uno o más ADS de varios Gb que llenen el disco. Por no hablar de cómo eliminar los ADS si el fichero en cuestión es necesario para el funcionamiento del sistema y no se puede mover ni borrar. Esto puede mitigarse con las prácticas habituales: acceder con usuarios no privilegiados, tener particiones distintas para el sistema y para los datos, utilización de cuotas de espacio en disco por usuario, etc.

Al final del artículo os dejaré un pequeño programa que he escrito para listar todos los flujos de un fichero o directorio, asi como su tipo y tamaño.

Otro posible problema relacionado con los ADS es la utilización maliciosa de la sintaxis fichero:flujo:$DATA, especialmente el uso de fichero::$DATA para acceder al flujo principal de datos de un fichero. Esta sintaxis puede utilizarse para "engañar" a sistemas que gestionan ficheros basados en su extensión, ya que agregando "::$DATA" al final del nombre del fichero hace que su extensión ya no se vea como antes, y si la aplicación no es consciente de la existencia de los ADS, seguramente se "confundirá".

Un famoso ejemplo de este último truco fue una vulnerabilidad en el mismísimo IIS (arreglado hace varias versiones) donde un usuario exteno en vez de pedir, por ejemplo, por la cadena http://www.microsoft.com/default.asp usaba su navegador para pedir por la cadena http://www.microsoft.com/default.asp::$DATA. El parser del IIS no era capaz de reconocer que se estaba invocando a un fichero .ASP (el fichero pedido no termina en ".ASP" sino en "DATA") y en vez de ejecutarlo, simplemente mostraba su contenido, con lo que el usuario externo tenía acceso al código fuente de un script de servidor. Como algunos programadores solían (y aún suelen) codificar usuarios y claves en scripts CGI o ASP (para acceder a Bases de Datos, por ejemplo), un atacante podía obtener los mismos con un simple navegador.

Aunque con IIS esto ya no es un problema, no estoy al tanto de cómo reaccionan otros servidores frente a los ADS. Verificad el vuestro para evitar este tipo de ataques y también otros problemas derivados de posibles fugas de información relacionadas con ADS. Por ejemplo: dentro de una organización un usuario malintencionado podría pasarle a un administrador un fichero "normal" ("te he dejado el fichero en el servidor en tal y tal sitio"), con una foto por ejemplo, conteniendo uno o más ADS con ficheros confidenciales. Si luego el administrador, que no ha leído éste artículo, cuelga la foto en una Web pública utilizando un webserver que no entienda ADS y que abra ciegamente la cadena pedida utilizando open(), cualquiera podría acceder a la información confidencial sabiendo los nombres de los flujos. Por ejemplo: http://www.ejemplo.com/images/foto.jpg:confidencial.doc. Lo mismo para servidores FTP u otras formas de publicación de ficheros.

Y no, la afirmación de que se pueden pasar ADS por FTP no entra en conflicto con la afirmación anterior de que pasar un fichero por FTP elimina los ADS. Si se hace un GET del nombre del fichero, se pasará sólo el contenido del flujo principal de datos. Si se hace un GET del nombre del ADS, se transferirá el contenido de dicho ADS.

 

Usos comunes de los Alternate Data Streams

En su origen, los ADS fueron creados para soportar los forks nativos de los sistemas Macintosh, los extended attributes de OS/2 y para guardar información de permisos estilo Unix para programas POSIX. Estos usos aún continúan vigentes.

Dentro de la plataforma Windows, los distintos programas utilizan los ADS de distintas maneras.

A partir de Windows XP, cuando se descarga un fichero ejecutable desde Internet (con IE, FireFox, Outlook, Messenger, etc), se crea un ADS llamado "Zone.Identifier" que contiene el identificador de la zona de Internet desde donde se ha descargado el fichero. Esta información es utilizada luego por Windows Explorer para decidir si muestra o no una advertencia al tratar de ejecutar el programa. Por ejemplo descargando la última versión de Pidgin (o vuestro programa preferido) se puede ver lo siguiente:

C:\Prueba>more < pidgin-2.5.6.exe:Zone.Identifier
[ZoneTransfer]
ZoneId=3

Es interesante notar que desde Windows Explorer, pulsando el botón derecho del ratón sobre el ejecutable y seleccionado "Properties", en la pantalla "General" aparece al final una advertencia de seguridad y un botón de "Unblock". Presionando este botón y luego "Ok" o "Apply" Windows Explorer simplemente borra el ADS:

C:\Prueba>more < pidgin-2.5.6.exe:Zone.Identifier
The system cannot find the file specified.

También se puede ver qué pasa grabando el ejecutable en un sistema de ficheros tipo FAT, que no soporta ADS. En este caso, Windows Explorer no muestra ninguna advertencia al ejecutarlo, ya que no encuentra el ADS de identificador de zona.

Es posible cambiar el indicador de zona editando el ADS con NOTEPAD como se vió anteriormente. Las zonas aceptadas son:

0: Local Machine
1: Intranet
2: Trusted
3: Internet
4: Untrusted
1000 o superior: User Defined

Otro ejemplo de usos constructivos de ADS es para agregar comentarios a un fichero por medio del Windows Explorer. Pulsando el botón derecho del ratón sobre un fichero no ejecutable y seleccionado "Properties", pero ahora en la pestaña "Summary", se puede agregar información de título, autor, comentarios, etc. Esta información se guarda en distintos ADS llamados "\005DocumentSummaryInformation" y "\005SummaryInformation". El "\005" representa que el primer caracter del nombre del ADS es un 5 binario. La estructura de estos flujos no es de texto plano y esta documentada aquí y aquí

El servicio de Indexing agrega vistas en miniatura como ADS a los ficheros de imagen.

Los programas de Microsoft Office, como Word y Excel, y otros programas de Windows, como Paint, crean flujos de otras clases (no ADS) dentro de los ficheros de documentos. Estos flujos no llevan nombre y son del tipo 7 (OBJECT_ID), por lo que no se pueden acceder utilizando los métodos vistos anteriormente.

C:\Prueba>more < Book1.xls::$OBJECT_ID
Access is denied.

El hecho que el error sea Access is denied. indica que la especificación de flujo es correcta y existe, ya que de lo contrario hubiésemos obtenido The system cannot find the file specified.

Otros paquetes de software también utilizan ADS para fines propios. Por ejemplo, Kaspersky Antivirus solía guardar información propia en ADS en cada fichero verificado. Cuando el software antivirus estaba activo, bloqueaba el acceso a dichos flujos, por lo que eran invisibles. Tengo entendido (aunque no estoy 100% seguro) que han dejado de hacer esto porque complicaba mucho desinstalar el antivirus, ya que había que borrar todos los ADS creados por todo el sistema.

 

Algunas herramientas

Como ya se ha mencionado, los ADS son perfectamente accesibles por medio de programación convencional. Cualquier antivirus moderno, por ejemplo, revisa no solo el flujo principal de datos en busca de virus, sino también los ADS.

Hay varias herramientas freeware que permiten descubrir y eliminar ADS, algunas de solo texto y otras con interfaces gráficas. Los que no os sintáis inclinados a programar, podéis hacer uso de las mismas y adelantar rápidamente hasta "Epílogo".

Los más osados podéis utilizar el compilador C/C++ gratuito de Microsoft para hacer programas a medida para gestionar ADS a gusto. Los siguientes ejemplos hacen justamente esto. Por favor, utilizad estos programas bajo vuestra propia cuenta y riesgo.

El siguiente programa puede utilizarse para eliminar cualquier ADS (y también elimina ficheros: usar con muchísimo cuidado que no pide confirmación!):


/* EraseStream.c
 * by Andy
 */
#include <stdio.h>
#include <errno.h>

int main(int argc, char *argv[], char *envp[])
{
   if (argc == 2) {
      printf("%s: trying to delete \"%s\"", argv[0], argv[1]);
      if (unlink(argv[1]) != -1) {
         printf(", Ok.\n");
      }
      else  // el unlink() ha fallado, imprimir el error
         printf("\n%s\n", strerror(errno));
   }
   else   // Se ha invocado al programa sin parametros o con muchos parametros
      printf("usage: %s stream\n"
             "       erases the named stream\n", argv[0]);
   return(0);
}

Utilizamos el compilador C/C++ gratuito de Microsoft mencionado anteriormente para compilar el programa:

C:\Prueba>"%VS90COMNTOOLS%\vsvars32"
Setting environment for using Microsoft Visual Studio 2008 x86 tools.

C:\Prueba>cl EraseStream.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

EraseStream.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:EraseStream.exe
EraseStream.obj

C:\Prueba>EraseStream c:\:flujo1.txt
EraseStream: trying to delete "c:\:flujo1.txt", Ok.

La primera línea es para poder trabajar con el compilador desde la línea de comandos. Claro que también dispone de un entorno gráfico que podéis utilizar si os apetece. Si, por el contrario, preferís los retos más difíciles, os gustará saber que también tenéis instalado un compilador Macro Assembler accesible desde la línea de comandos por medio de "ml".

Una vez compilado el programa con "cl", se lo puede utilizar para borrar cualquier ADS o fichero pasándoselo como parámetro. En el ejemplo lo he utilizado para borrar el ADS del directorio raíz que había creado anteriormente.

Se pueden hacer programas similares al anterior para crear, escribir, leer o hasta ejecutar ADS. Si a alguien le interesa experimentar y necesita ayuda, que no dude en consultarme.

El nombre de un ADS no puede cambiarse ni aún utilizando programación. Pero puede lograrse el mismo efecto creando otro ADS, copiando los datos del atiguo al nuevo, y borrando el original. Todo dentro del mismo fichero.

Para terminar, mencionar que descubrir mediante programación si hay flujos dentro de un fichero y cuáles son sus nombres (para poder verlos o borrarlos) no es algo trivial. Para poder hacerlo hay que acceder mediante la API de backup. Abriendo un fichero en modo backup, el sistema operativo lo presenta de una manera distinta a como está físicamente en el disco. El fichero se accede entonces como una serie de [encabezado + datos], uno por cada flujo. El primer flujo siempre será el flujo de datos principal sin nombre, a menos que el mismo no exista (fichero de longitud 0 o directorio). Cada encabezado tiene una parte fija y una variable. La parte fija describe el tipo de flujo, su tamaño, atributos y longitud del nombre del flujo. La parte variable, si existe, sigue inmediatamente a la parte fija y es el nombre del flujo codificado en Unicode y *no* terminado en 0 binario. Justo detrás de esto vienen los datos correspondientes a ese flujo con la longitud indicada en el encabezado.

El siguiente programa enumera todos los flujos de un fichero o directorio. No sólo los ADS, sino los flujos de cualquier tipo. El programa lista el nombre del flujo, su tipo (ID) y su tamaño en bytes. El flujo principal de datos de un fichero tiene ID=1 (DATA), mientras que los ADS tienen ID=4 (ALTERNATE_DATA). Aunque no he incluído muchos comentarios, creo que se entiende. Cualquier duda consultarme.

Perdón por separar en varias líneas algunas invocaciones a funciones y estructuras de datos, pero es necesario para que el listado se vea correctamente en la Web (y aún así no estoy seguro que se vaya a ver bien).


/* EnumStreams.c
 * by Andy
 */
#include <stdio.h>
#include <wchar.h>
#include <windows.h>

BYTE buffer[65536];   // Area de trabajo de 64Kb

struct {              // Encabezado de flujo, parte fija
   DWORD StreamId ;
   DWORD StreamAttributes;
   DWORD SizeHigh;
   DWORD SizeLow;
   DWORD StreamNameSize ;
} stream_header;

char *stream_IDs[] = { "INVALID",      // Para darse una idea de los
                       "DATA",         // tipos de flujo que existen
                       "EA_DATA",
                       "SECURITY_DATA",
                       "ALTERNATE_DATA",
                       "LINK",
                       "PROPERTY_DATA",
                       "OBJECT_ID",
                       "REPARSE_DATA",
                       "SPARSE_BLOCK",
                       "TXFS_DATA" };

int main(int argc, char *argv[], char *envp[])
{
   HANDLE Handle;
   DWORD readcount, seeklow, seekhigh;
   LPVOID context = NULL;

   if (argc == 2) {

      if ((Handle = CreateFile(argv[1],    // Abrir el fichero en modo backup
                               GENERIC_READ,
                               FILE_SHARE_READ,
                               0,
                               OPEN_EXISTING,
                               FILE_FLAG_BACKUP_SEMANTICS,
                               0)) != INVALID_HANDLE_VALUE) {

         while (BackupRead(Handle,         // hasta que no se acabe el fichero
                           (BYTE *) &stream_header,
                           sizeof(stream_header),
                           &readcount,
                           0,
                           0,
                           &context) &&
                (readcount == sizeof(stream_header))) {

             if (stream_header.StreamNameSize > 0) {  // Si el flujo tiene nombre

                BackupRead(Handle,   // Leer el nombre
                           buffer,
                           stream_header.StreamNameSize,
                           &readcount,
                           0,
                           0,
                           &context);

               *(WCHAR *)&(buffer[readcount]) = 0;  // Terminar el nombre con \0 
                wprintf(L"%s", buffer);    // El nombre del flujo esta en Unicode
             }
             else               // El flujo no tiene nombre
                printf("::");   // Imprimir "::" indicando falta de nombre

             printf(" (ID=%d:%s, Size=%llu)\n",  // Imprimir informacion del flujo
                    stream_header.StreamId,
                    stream_IDs[stream_header.StreamId <= 10 ? stream_header.StreamId : 0],
                    *(ULONGLONG *)&(stream_header.SizeHigh));

             BackupSeek(Handle,   // Saltear el contenido del flujo
                        stream_header.SizeLow,
                        stream_header.SizeHigh,
                        &seeklow,
                        &seekhigh,
                        &context);
          }
          BackupRead(Handle,  // Es necesario finalizar el "backup"
                     NULL,
                     0,
                     NULL,
                     1,       // AbortBackup
                     0,
                     &context);

          CloseHandle(Handle);
      }
      else {   // No se pudo abrir el fichero

         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       GetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       buffer,
                       sizeof(buffer) - 1,
                       NULL);

         printf("%s: Error while trying to open \"%s\".\n%s\n", argv[0], argv[1], buffer);
      }
   }
   else   // Se ha invocado al programa sin parametros o con muchos parametros
      printf("usage: %s file\n"
             "       enumerates the alternate data streams of a file.\n", argv[0]);
}

Compilando el programa se obtiene:

C:\Prueba>cl EnumStreams.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

EnumStreams.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:EnumStreams.exe
EnumStreams.obj

C:\Prueba>EnumStreams texto.txt
:: (ID=1:DATA, Size=30)
:calculadora.exe:$DATA (ID=4:ALTERNATE_DATA, Size=114688)
:flujo1.txt:$DATA (ID=4:ALTERNATE_DATA, Size=40)

C:\Prueba>EnumStreams subdir
:flujo.txt:$DATA (ID=4:ALTERNATE_DATA, Size=49)

C:\Prueba>EnumStreams Book1.xls
:: (ID=1:DATA, Size=13824)
:: (ID=7:OBJECT_ID, Size=64)

Puede modificarse el programa para analizar el contenido de los flujos "interesantes" (el programa de arriba sólo analiza los encabezados y se salta el contenido de todos los flujos utilizando BackupSeek()). Si alguien está interesado en ésto, puedo publicar las modificaciones necesarias para hacerlo.

 

Epílogo

Hemos visto los secretos de los "Alternate Data Streams" o "Flujos Alternativos de Datos" en NTFS. Este conocimiento debe servirnos no sólo para darles algún uso creativo o entender mejor como funcionan los sistemas que administramos, sino también para poder securizar mejor los mismos.

La "seguridad mediante oscuridad" no es seguridad, dicen los expertos. Pero lo que si existe es la INseguridad por oscuridad, que es lo que este artículo ha tratado de mitigar.

 

Comentarios

Selecciona arriba tu forma preferida de visualizar los comentarios y pulsa el botón para guardar tu elección para próximas visitas (sólo si eres usuario registrado).
Alenog's picture

Muchas gracias


Muchas gracias. Resulta muy completo. ¡Incluso incorpora el código! :)

Otra utilidad similar es 'streams', de Mark Russinovich de sysinternals (ahora comprada por Microsoft) que permite encontrar y borrar los ADS http://technet.microsoft.com/en-us/sysinternals/bb897440.aspx LADS parece muy similar.

vierito5's picture

Genial


Felicidades por el artículo. Applause!

CaStarCo's picture

Hacía falta algo así


No conocía los ADS la verdad es que no está de más conocer estos detalles para controlar mejor nuestros sistemas informáticos :) .

Andy's picture

Gracias por el link


La verdad es que no me había puesto a indagar si desde Linux se podía trabajar con ADS. Parece ser que si. Puede ser útil en algunos escenarios.

Dicho esto, mi opinión es que NTFS-3G debería ser una herramienta de migración para pasar los datos a alguna partición con soporte nativo, no algo para utilizar a largo plazo y menos aún para utilizar en servidores Linux de producción.

Pelias's picture

Muy interesante


Estupendo Articulo y muy interesante.

Sabia que en NTFS se podian guardar propiedades extendidas en los ficheros que no eran directamente accesibles pero con este artículo me ha quedado muy claro el funcionamientos.

Estoy impaciente por llegar a casa y ponerme con el C++ !!

-Pelias.

Andy's picture

Con cuidado


Me alegra que quieras experimentar. Pero con mucho cuidado al tratar el código. Yo lo he probado bastante, pero como comprenderás, es imposible probarlo todo. Además, el código hace lo que hace y si no tienes cuidado pueden pasar desastres. El programita que borra ADS, por ejemplo, en realidad borra cualquier cosa que le pongas delante, asi que no me culpes si experimentando con él borras el kernel o el documento de 40 páginas que tenías que entregar mañana a primera hora.

infosniper's picture

De todo lo dicho se deduce que el mejor sistema de archivos es


tatacháaaan...

¡FAT32!

En fin, se les diga como se les diga, los ADS son un problema de seguridad. Pero los usuarios de Windows tragan con ellos solamente si se empeñan en trabajar con NTFS, cosa que parece ser no ocurre con los usuarios de Linux desde hace tiempo puesto que por lo visto se ven obligados a convivir con ellos (Extended file attributes), tal y como puede verse en el link de la wikipedia que facilitas:

http://en.wikipedia.org/wiki/Extended_attributes

Felicidades por el artículo. Hacía falta.

infosniper
http://infosniper.googlepages.com

eb4bgr's picture

Buena aportación


Muchas gracias Andy por el artículo. Es muy interesante y seguro que voy a sacarle buen provecho en un futuro próximo.

Un saludo.

FAQsimil's picture

Añadir pestaña Streams al explorador de archivos


Microsoft dispone de una libreria (StrmExt.dll) que añade una practica pestaña al explorador de Windows, donde veremos si un fichero tiene o no ADS.

El proceso de instalacion viene comentado en este articulo:

http://windowstips.wordpress.com/2007/01/29/alternate-data-streams-ads-q...

--
La primera regla de seguridad es NO trabajar con cuenta de Administrador.

--
La primera regla de seguridad es NO trabajar con cuenta de Administrador