| ESTUDIO COLECTIVO DE DESPROTECCIONES Última Actualizacion: 25/10/2001 |
Programa | Access 97. Fase 3 | W95 / W98 / NT
Descripción | Motor de bases de datos
Tipo | Aplicación comercial
Tipo de Tutorial | [X]Original, []Adaptación, []Aplicación, []Traducción
Url | http://www.microsoft.com
Protección | Contraseña de acceso a bases de datos
Dificultad | 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas | SoftIce v3.25, W32dasm v8.9
Objetivo | Desencriptar la contraseña de acceso a un base de datos de Access
Cracker | Mr.Blue
Grupo | Whiskey Kon Tekila
Fecha | 26 de Octubre de 1999
INTRODUCCION
Vamos a demostrar la "seguridad" de los datos en Access 97. Todo lo que se va explicar ha sido probado sobre Access 97, aunque sospecho que es válido para cualquier base de datos en formato Access (al menos desde el Access 95). He dividido nuestro proyecto en varias fases, cada una más difícil que la anterior, pero todas muy fáciles. Ya veréis. Parchear una base de datos para eliminar la contraseña de acceso. Parchear el Access 97 para poder abrir bases de datos protegidas con contraseña de acceso (sin saberla, claro). Diseño de un gestor de contraseñas para bases de datos de Access que permita descubrir la contraseña de una base de datos, modificarla o borrarla.
RESUMEN DE LO ANTERIOR
En la primera parte comenzamos por identificar el mecanismo utilizado por Acess97 para almacenar las contraseñas de acceso a bases de datos. Descubrimos en que lugar exacto de los ficheros .mdb se almacenan dichas contraseñas, así como una forma de eliminar dichas contraseñas. Ya disponemos de una herramienta para poder acceder a cualquier base de datos protegida con contraseña de acceso, eliminándola. En la segunda parte "destapamos" donde el Access decide si una base de datos tiene contraseña o no, y se mostró como era posible confundir a las rutinas del Access para que pensaran que las bases de datos no tienen contraseña de acceso y por tanto, poder abrirlas sin necesidad de conocerla. Aquí vamos a poner la guinda al pastel. Buscaremos cual es el proceso de desencriptación de la contraseña e intentaremos, una vez comprendido el funcionamiento de las rutinas de desencriptación, determinar el proceso inverso. Una vez conocido los procesos de encriptación, desencriptación y el lugar donde se almacena la contraseña (ya se vió en la primera parte), nada impediría realizar una aplicación que mostrase la contraseña de una base de datos y permitiera modificarla. Engrasad las neuronas que las vamos a necesitar.
CRIPTOLANDIA
Iniciando En la fase anterior encontramos el lugar donde se desencriptaba la contraseña de acceso. Nos pillamos el W32dasm, desensamblamos 'MsJet35.dll' para echarle un ojo a la rutina completa. Como recordaréis del tutorial anterior, encontramos la rutina de desencriptación en la dirección 4035793. Nos vamos ahí con el W32dasm y buscamos el inicio de la rutina. El principio está en 4035768, y además vemos que es llamada desde muchos sitios. Umm, parece que es una rutina "muy popular" ... Echando un vistazo por encima a la rutina desde el W32dasm parece que consta de un bucle que se repite el número de veces indicado por el registro EBP. Este bucle va desde la dirección 403578F hasta el salto en 40357AB. Los registros ESI y EDI son utilizados para apuntar a zonas de memoria, al igual que EDX, EBX y EAX que parecen actuar como "índices" de la zona apuntada por ESI. Lógicamente, una de estas zonas de memoria será el inicio del fichero de la base de datos, donde se encuentra encriptada la contraseña. Cargamos el SoftIce para ver el funcionamiento de la rutina. Igual que en tutorial precedente, colocamos un punto de ruptura en los accesos a CreateFileA, y procedemos a abrir una de nuestras bases de datos de prueba, por ejemplo 'bd2.mdb', que tenía 'WKT!' como contraseña de acceso. En cuanto nos salte el acceso a nuestra base de datos pulsamos F11 y debemos aparecer en el código de 'MsJet35.dll'. Plantamos el punto de ruptura en 4035768 y ejecutamos.
`:04035768` | `55` | `push ebp`
`:04035769` | `8BEC` | `mov ebp, esp`
`:0403576B` | `83EC04` | `sub esp, 00000004`
`:0403576E` | `53` | `push ebx`
`:0403576F` | `894DFC` | `mov dword ptr [ebp-04], ecx`
`:04035772` | `56` | `push esi`
`:04035773` | `57` | `push edi`
`:04035774` | `33DB` | `xor ebx, ebx`
`:0403577C` | `33D2` | `xor edx, edx`
`:0403577E` | `8A5904` | `mov bl, byte ptr [ecx+04]`
`:04035781` | `8A5105` | `mov dl, byte ptr [ecx+05]`
`:04035784` | `FEC3` | `inc bl`
`:04035786` | `55` | `push ebp`
`:04035787` | `33C0` | `xor eax, eax`
`:04035789` | `8B6D0C` | `mov ebp, dword ptr [ebp+0C]`
`:0403578C` | `8A0C1E` | `mov cl, byte ptr [esi+ebx]`
Antes de empezar a curiosear, buscamos en la pila a ver de dónde venimos (ya he dicho en otras ocasiones que es muy importante saber a dónde vamos, de dónde venimos, quienes somos, ....). La pila nos indica que cuando termine la rutina, a no ser que ocurra una catástrofe (o que la rutina modifique la dirección de retorno de la pila), volveremos a la dirección 4036028 (sí novatillo, ese número de cuatro bytes al que apunta el contenido del registro ESP). Volvamos a esa dirección o no, lo que nos importa es que se nos ha llamado desde el 'call' que hay justo delante. Esta información es útil por si se nos hace necesario "volver al pasado". Nos encontramos en la zona de inicialización de las variables que va a utilizar la rutina durante la desencriptación. Interesa conocer quién es quién. Los registros ESI y EDI no se inicializan dentro de la rutina, por lo que han debido ser inicializados por la rutina que llama a la de desencriptación. Uno de ellos, EDI, apunta al inicio del fichero de la base de datos, concretamentamente a la posición 18h. El otro, ESI, apunta a una zona que ahora mismo nos suena a chino. Se inicializan a cero EBX y EDX para cargar enseguida los bytes apuntados por ECX+4 y ECX+5 (dos ceros). El registro ECX apunta delante de la zona "en chino" apuntada por ESI, exactamente 6 bytes antes. En esos 6 bytes, encontramos el valor 6B39DAC7 seguidos de dos bytes a cero. Se incrementa EBX (ahora es uno), se inicializa EAX a cero, se carga en EBP un 7Eh (126 en decimal) y en CL el byte apuntado por ESI+EBX = ESI+1. Barajando bytes Ya estamos en el inicio del bucle que vamos a intentar comprender. Apuntamos las condiciones iniciales: EDI apunta a la zona a desencriptar. ESI apunta a "algo". Este algo, ya se verá que es una especie de tabla de desencriptación. CL es el segundo byte de la tabla de desencriptación. EBX = 1, EDX = 0, EAX = 0 EBP = 7E. Tal como vimos en nuestra ojeada inicial a la rutina, EBP hará las veces de contador, por lo que el bucle se va a repetir 126 veces. Esperemos que antes de que se acabe hayamos comprendido su funcionamiento ;-)
`:0403578F` | `02D1` | `add dl, cl`
`:04035791` | `8A2F` | `mov ch, byte ptr [edi]`
`:04035793` | `47` | `inc edi`
`:04035794` | `8A0416` | `mov al, byte ptr [esi+edx]`
`:04035797` | `880C16` | `mov byte ptr [esi+edx], cl`
`:0403579A` | `88041E` | `mov byte ptr [esi+ebx], al`
`:0403579D` | `02C1` | `add al, cl`
`:0403579F` | `FEC3` | `inc bl`
`:040357A1` | `322C06` | `xor ch, byte ptr [esi+eax]`
`:040357A4` | `4D` | `dec ebp`
`:040357A5` | `886FFF` | `mov byte ptr [edi-01], ch`
`:040357A8` | `8A0C1E` | `mov cl, byte ptr [esi+ebx]`
`:040357AB` | `75E2` | `jne 0403578F`
`:040357AD` | `5D` | `pop ebp`
Cogemos un folio en blanco, lápiz, papel y cerramos la puerta para que no molesten.
`1. Sumamos DL+CL.` | `DL = DL + CL`
`2. Cargamos en CH el primer byte de nuestra cadena a desencriptar.` | `CH = (EDI)`
`3. Apuntamos al siguiente byte de nuestra cadena a desencriptar.` | `EDI = EDI + 1`
`4. Cargamos en AL el byte tabla apuntado por EDX.` | `AL = (ESI+EDX)`
`5. Escribimos en esa misma posición, el contenido de CL.` | `(ESI+EDX) = CL`
`6. Escribimos AL en el byte de la tabla apuntado por EBX.` | `(ESI+EBX) = AL`
`7. Sumamos AL y CL.` | `AL = AL + CL`
`8. Incrementamos BL.` | `BL = BL + 1`
`9. "Xoreamos" CH con el byte de la tabla apuntado por EAX.` | `CL ^ (ESI+EAX)`
`10. Decrementamos el contador.` | `EBP = EBP - 1`
`11. Guardamos CH en la cadena a desencriptar.` | `(EDI-1) = CH`
`12. Cargamos en CL el byte de la tabla apuntado por EBX.` | `CL = (ESI+EBX)`
`13. Repetimos hasta que EBP sea cero.` | `Rep. si EBP != 0`
Aquí está todo el meollo de la desencriptación. En síntesis, lo que se hace es recorrer todo la cadena a desencriptar. Cada byte encriptado (se carga en 2), es sustituído por el correspondiente desencriptado (se sustituye en 11), que no es más que el encriptado "xoreado" con otro byte (en 9). Lo más complicado de explicar es de dónde sale ese otro byte que nos realizar la desencriptación. Como se ha dicho, disponemos de una tabla de desencriptación. El contenido de esta tabla de desencriptación, se va barajando a medida que se va desencriptando. Vamos a ver si soy capaz de explicarme... Fijándonos en la rutina, el registro EBX se utiliza como índice para recorrer toda la tabla de desencriptación. En todo momento, se cumple que si estamos desencriptando el n-ésimo byte de la cadena encriptada, EBX estará apuntando al (n+1) byte de la tabla de desencriptación. En cada iteración, el byte de la tabla apuntado por EBX se carga en CL (paso 12) y sumado a DL (1), donde se va acumulando la suma de todos los bytes que se van cargando en CL. Como consecuencia de esta suma, el byte de la tabla apuntado por EDX (4) es sumado con CL (7), sustituye al que estamos apuntando con EBX (6) y es sustituído a su vez por el contenido de CL (5), que es el que estaba apuntando EBX. Lo que se está realizando en un intercambio entre dos bytes de la tabla, el apuntado por EBX y el apuntado por EDX. El byte apuntando por la suma realizada en (7), es el que se "xorea" con el que queremos desencriptar. Si alguien no se ha enterado ya sabe, papel, lápiz y paciencia ;-) Y una cosa importante. Puesto que para desencriptar tan sólo se realiza un "xor" lógico sobre el byte encriptado para obtener el byte desencriptado, si realizamos sobre el desencriptado la misma operación con el mismo operando, volveremos a obtener el byte encriptado. Las operaciones de encriptado y desencriptado son idénticas, siempre usando la misma tabla de encriptación. Esto significa que si tenemos una cadena encriptarda, si aplicamos sobre ella esta rutina, obtenemos la cadena desencriptada, y si volvemos a aplicarla, volvemos a obtener la encriptada. Retrocediendo Ya sólo nos queda encontrar esa tabla de desencriptación. ¿Y dónde puede estar?. La tabla, puede encontrarse almacenada en 'MsJet35.dll'. Usando los primeros bytes de dicha tabla, realizamos una búsqueda con un editor hexadecimal en la .dll. Lo primero que hacemos es buscarla con un editor hexadecimal en la .dll. Vemos que no aparece en 'MsJet35.dll'. También puede estar en el mismo fichero de la base de datos. Así, cada base de datos tendría una tabla de desencriptación distinta. Repetimos la búsqueda en 'bd2.mdb' y tampoco. Pueden ocurrir dos cosas, o se encuentra almacenada pero encriptada, o es generada en tiempo de ejecución. No puede estar en ningún otro fichero porque no nos salta el punto de ruptura en CreateFileA en un fichero distinto de la base de datos que estamos abriendo. Tenemos que ir retrocediendo en la ejecución del programa y ver donde se genera la tabla. Para eso, nos será de gran ayuda el W32dasm. Como recordareis, al comenzar a ejecutar la rutina de desencriptación (que ya hemos visto que es la misma para encriptación), la dirección de retorno que encontramos en la pila era 4036028. La buscamos en el listado del W32dasm:
`:04036013` | `6A7E` | `push 0000007E`
`:04036015` | `8D4518` | `lea eax, dword ptr [ebp+18]`
`:04036018` | `8D8C241C010000` | `lea ecx, dword ptr [esp+0000011C]`
`:0403601F` | `50` | `push eax`
`:04036020` | `8D7542` | `lea esi, dword ptr [ebp+42]`
`:04036023` | `E840F7FFFF` | `call 04035768`
`:04036028` | `803E00` | `cmp byte ptr [esi], 00`
En esta porción de código se está inicializando los parámetros que va a utilizar la rutina de desencriptación. Lo que nos interesa es la carga del registro ESI con la dirección de la tabla de desencriptación, que se realiza en 4036020 con el valor EBP+42. Ahora tendremos que buscar donde se carga el registro EBP con el valor correspondiente. Subimos un poco más en el listado y encontramos esto:
`:04035FCD` | `68C7DA396B` | `push 6B39DAC7`
`:04035FD2` | `E817F7FFFF` | `call 040356EE`
`:04035FD7` | `8B6E04` | `mov ebp, dword ptr [esi+04]`
EBP se carga en 4035FD7, justo después de la llamada a la rutina 40356EE, por lo que parece que esta es la rutina que carga en memoria la tabla de desencriptación. Además, justo antes de la llamada a esa rutina, se le pasa como parámetro en la pila el valor 6B39DAC7, que es el valor que encontramos justo delante de la tabla de desencriptación. Saltamos a la dirección 40356EE con el W32dasm, y vemos que esta rutina es también llamada desde un montón de sitios. Volvemos al SoftIce, y colocamos un punto de ruptura en 40356EE, para ver como se crea la famosa tabla. Ejecutamos y nos encontramos en 40356EE. Lo primero que se hace es cargar el 6B39DAC7 en EDX y almacenarlo en memoria (bucle 40356FA-4035704) a partir de la dirección apuntada por ECX. Durante todo el proceso, ECX estará simepre apuntando al inicio de la tabla que se va a contruir. Después, a partir de ECX+6, se almacena una tabla de 256 bytes (bucle 4035708-4035712). La cadena que se almacena es 00 01 02 .... FE FF. A continuación se almacenan dos bytes cero en ECX+4 y ECX+5, se inicializa a cero (ESP+10) y (ESP+0F), y se comienza con un bucle (4035724-403575B) que barajará la tabla de 256 bytes que hemos creado. La forma que se barajan los bytes, es idéntica a la que se utiliza para barajar la tabla durante el proceso de encriptación o desencriptación. (ESP+10) se utilizará como contador, y marcará cuando se ha terminado el bucle. Quien quiera conocer a fondo como se crea la tabla que se busque igual que antes lápiz y papel y que tracee la rutina. Para no enrrollarme, os lo pongo en pseudo-código. 'tabla' estará apuntando al inicio de la tabla y el resto de la variables están inicializadas a cero:
`FOR contador=0 TO 0FFh`
`op1 = (tabla+acu)`
`op2 = (tabla+contador+6)`
`res = res + op1 + op2`
`acu = (acu + 1) AND 3`
`(tabla+contador+6) = (tabla+res+6)`
`(tabla+res+6) = op2`
END FOR
En el código, 'res' es el byte apuntado por (ESP+0F), 'acu' es AL, 'tabla' es ECX y 'contador' es el dword apuntado por (ESP+10). La tabla generada dependerá del valor inicial que se haya utilizado. Así, para la contraseña de acceso, se utiliza una tabla generada a partir del valor 6B39DAC7. Si buscamos con el W32dasm el resto de direccciones desde la que se invoca la rutina para la creación de la tabla, veremos que el valor que se le pasa como parámetro es distinto. Esto significa que estas mismas rutinas de desencriptación son utilizadas por Access para encriptar y desencriptar otros datos de la base de datos aparte de la contraseña de acceso. Como veis, hay muchas mas cosas por investigar... Al retornar esta rutina, se hacen una serie de comprobaciones para asegurar que la base de datos que se está abriendo es una base de Access válida. Para ello, se comprueba que la primera palabra del fichero sea 0100h, la segunda 0, que el DWORD que comienza en el offset 13h sea 0 y que a partir de la posición 4 aparece la cadena 'Standard Jet DB'. Si estás comprobaciones se realizan con éxito, se salta a la rutina de desencriptación. Como nota curiosa, si dejáis el punto de ruptura puesto en la rutina de desencriptación, os dareis cuenta de que al abrir la base de datos, Access desencripta la parte de la contraseña de acceso, mira a vere si la base de datos tiene contraseña, vuelve a encriptar usando las mismas rutina y después es cuando pregunta por la contraseña. Al introducir la contraseña en el cuadro de diálogo, se vuelve a desencriptar la misma para realizar la comparación.
CONCLUSIONES
A la hora de realizar nuestro programa, para la encriptación y desencriptación, solo tendremos que generar una misma función. Está función, generará la tabla y realizará las transformaciones sobre el buffer de memoria que le hayamos pasado. Si el buffer estaba encriptado lo desencriptará, y si ya estaba desencriptado, volverá a encriptarlo. Para conseguir modificar la contraseña de acceso a la base de datos, cargaremos en una zona de memoria la parte del fichero que nos interesa, desencriptaremos, modificaremos la contraseña directamente a partir de la posición 42h (como se vio en el primer tutorial) y volveremos a pasar por la rutina de encriptación para después grabarlo en el fichero. El resto es poner una interfaz más o menos sencilla y atractiva, defenderse en ensamblador para Windows y saldrá algo así como el WKTAccess. Aún quedan muchas otras cosas por "destripar". Como se ha visto, la contraseña se almacena al principio del fichero de la base de datos, y se obtiene desencriptando una porción de 126 bytes. La contraseña de acceso ocupa catorce de esos bytes pero, ¿y los otros? ¿qué contienen? Además, la tabla de desencriptación se genera a partir de un número. Distintos números generan distintas tablas. Se ha examinado la utilizada para desencriptar la zona donde se almacena la contraseña de acceso. Si colocais un punto de ruptura sobre la rutina de creación de la tabla con el SoftIce, veréis que se generan otras tablas que desencriptan otras zonas de la base de datos..... Los que conozcáis Access sabeis que la contraseña de acceso nos es más que un primer nivel de seguridad. Por encima están los grupos de usuarios con sus permisos, además de la posibilidad de codificación de la base de datos, etc.... Quizás se utilicen las mismas rutinas que hemos visto pero con distintos valores y aplicadas sobre otras porciones de la base de datos..... Puede que tengamos las rutinas de encriptación y desencriptación, tan solo queda saber dónde aplicarlas y cómo interpretar los resultados obtenidos para construirnos un "mapa" completo de las bases de datos de Access, dónde y cómo se almacenan cada uno de los parámetros de la base de datos. Buena caza. Mr.Blue / WkT!
[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers. Si necesitas contactar con nosotros, leeestoantes e infórmate de cómo puedes ayudarnos