Análisis del parche LNK

De todos es ya conocida la famosa vulnerabilidad de Microsoft Windows con los accesos directos, en la que un atacante podía ejecutar código arbitrariamente con el único requerimiento de que el sistema operativo renderizara un fichero LNK, simplemente navegando por una carpeta a través de SMB o WebDAV.

Inicialmente esta vulnerabilidad fue descubierta en redes privadas contra sistemas SCADA de Siemens, aunque pronto fueron apareciendo métodos de explotación remota añadiéndose exploits a diversos exploit kits (valga la redundancia) e incluso al framework de Metasploit.

La aparición de estos nuevos métodos de explotación y la escalada de ataques intentando explotar esta vulnerabilidad, forzaron a Microsoft a lanzar una actualización de seguridad fuera de ciclo, la MS10-046.

Microsoft debía lanzar urgentemente un parche que previniera estos ataques pero también que no afectara a los applets legítimos (CPL) de su Panel de Control. A continuación estudiaremos un poco su funcionamiento.

Para empezar tenemos que tener en cuenta que la librería shell32.dll construye una tabla desde tres localizaciones:

• C:\Windows\control.ini bajo el bloque MMCPL
• Todos los ficheros CPL en C:\Windows\system32\*.cpl
• Las claves de registro HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls y HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls.

Antes de instalar el parche, el CPL es renderizado sin chequear esta tabla y por lo tanto sin verificar que el CPL es legítimo. Una vez instalado el parche, si el CPL no existe en una de esas tres localizaciones, el fichero no es renderizado. Ese es el gran cambio del parche.

Adentrándonos un poco más en el parche, si realizamos un diff de la vieja y nueva versión del binario shell32.dll, veremos que se han añadido algunas nuevas rutinas. Las más interesantes son las siguientes:

7cb04e14    5    6    2 IsRegisteredCPLApplet(x,x)                                                     
7cb053cd 7 9 5 CControlPanelFolder::_IsRegisteredCPLApplet(ushort const *)

CControlPanelFolder::_IsRegisteredCPLApplet() es esencialmente un wrapper sobre IsRegisteredCPLApplet(), el cual es llamado por CControlPanelFolder::GetUIObjectOf().

La rutina IsRegisteredCPLApplet() es muy simple. Toma dos argumentos: el primero un string con la ruta del applet CPL a cargar y el segundo un DSA (dynamic structure array) que contiene una lista de CPLs ya registrados.

Si decompilamos el código podremos obtener esta pequeña función:


int __stdcall IsRegisteredCPLApplet(LPCWSTR lpCPLPath, int pDSA)
{
int lZero; // ebx@1 constant of 0
int lcount; // esi@1 loop counter
LPCWSTR *lpcwstrTestPath; // eax@2 pointer to current path to test against

lcount = **(pDSA + 4); // Grab the DSA size
lZero = 0;
while ( 1 )
{
--lcount; // Decrement the loop counter
if ( lcount < 0 ) // If we've reached the last entry, ret 0
break;
lpcwstrTestPath = DSA_GetItemPtr(*(pDSA + 4), lcount);// Obtain the next string to test from the DSA
if ( CompareStringW(0x7Fu, 1u, *lpcwstrTestPath, -1, lpCPLPath, -1) == CSTR_EQUAL )
return 1; // If the strings are equal, return 1
}
return lZero;
}

Como podemos ver, la función parsea cada entrada en el DSA y lo compara con el primer argumento, es decir con el CPL que se está intentando cargar. Sólo si existe en el DSA, la rutina devuelve 1 y el applet CPL es procesado.

Antes de la llamada a la función, el DSA siempre es cargado con las entradas de ficheros CPL conocidos. El DSA es creado y rellenado en la rutina CPLD_GetModules():


.text:7CA678F4 push    4               ; cItemGrow
.text:7CA678F6 push 0Ch ; cbItem
.text:7CA678F8 call edi ; DSA_Create(x,x) ; DSA_Create(x,x)
.text:7CA678FA test eax, eax
.text:7CA678FC mov [esi], eax ; Store DSA in &pp_arg0
.text:7CA678FE jnz short loc_7CA6

El código luego mira en las tres diferentes áreas en búsqueda de los ficheros CPL conocidos.

Primero en control.ini, en la que cada una de sus entradas es
insertada en el DSA y tratada como un fichero CPL legítimo:

...
memset(&pitem, 0, 0x1Cu); //
// Stage 1: Control.ini
//
GetSystemDirectoryW(&pszDir, 0x104u); // Obtain system folder (e.g. C:\Windows\system32)
GetPrivateProfileStringW(L"MMCPL", 0, &c_szNULL, &KeyName, 0x200u, L"control.ini");// Look for MMCPL block in control.ini (since the
// path isn't full, it uses C:\Windows\control.ini
pKeyName = &KeyName;
if ( KeyName ) // If the MMCPL block exists, parse through each entry
{
do
{
GetPrivateProfileStringW(L"MMCPL", pKeyName, &c_szNULL, &psz, 0x104u, L"control.ini");
if ( IsValidCplKey(pKeyName) )
_InsertModuleName(arg1, &psz, &pitem);
pKeyName += lstrlenW(pKeyName) + 1;
}
while ( *pKeyName );
}

La segunda fase es buscar ficheros CPL en la carpeta system32,
verificar si existen ficheros, y añadirlos al DSA:

if ( PathCombineW(&lpcwstrCurFile, &pszDir, L"*.CPL") )// C:\Windows\system32\*.cpl to lpcwstrCurFile
{
v8 |= 1u;
lSearchHandle = FindFirstFileW(&lpcwstrCurFile, &FindFileData);
if ( lSearchHandle != -1HINSTANCE_ERROR|HANDLE_FLAG_PROTECT_FROM_CLOSE|HANDLE_FLAG_INHERIT )
{
do
{
if ( !(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )// Make sure we aren't dealing with a directory
{
if ( !IsOS(0x1Eu)
|| (GetWindowsDirectoryW(
&lpcwstrCurFile,
0x104u), // Build the path and ensure the file exists
!PathCombineW(&lpcwstrCurFile, &lpcwstrCurFile, L"system32"))
|| !PathCombineW(&lpcwstrCurFile, &lpcwstrCurFile, FindFileData.cFileName)
|| !PathFileExistsW(&lpcwstrCurFile) )
{
if ( PathCombineW(&lpcwstrCurFile, &pszDir, FindFileData.cFileName) )
{
fdwLowDateTime = FindFileData.ftCreationTime.dwLowDateTime;
fdwHighDateTime = FindFileData.ftCreationTime.dwHighDateTime;
fnFileSizeHigh = FindFileData.nFileSizeHigh;
fnFileSizeLow = FindFileData.nFileSizeLow;
_InsertModuleName(arg1_DSA, &lpcwstrCurFile, &pitem);// Add it to the DSA
}
}
}
}
while ( FindNextFileW(lSearchHandle, &FindFileData) );
FindClose(lSearchHandle);
}
}

Finalmente la rutina busca en el registro utilizando a su vez la rutina _AddItemsFromKey:

  _AddItemsFromKey(arg1_DSA, HKEY_CURRENT_USER);
_AddItemsFromKey(DSA, HKEY_LOCAL_MACHINE);

Esta rutina abre las claves de registro HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls y HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls, abre los ficheros para asegurarse que existen, e inserta la ruta del CPL dentro del DSA.

En conclusión y con lo visto hasta ahora, podemos decir que este parche podría llegar a afectar a algunos applets del panel de control del usuario, dependiendo de cómo el fabricante los haya distribuido pues recordemos que, para que Windows procese los accesos directos una vez instalado el parche, será necesario que hayan sido añadidos previamente en alguna de las tres localizaciones anteriormente comentadas.

Aún así, no hay duda de que es recomendable la instalación del parche ya que la vulnerabilidad está siendo explotada activamente en Internet…

Comentarios

Publicar un comentario