Ir al contenido principal

El otro día encontré un programa que me permitía seleccionar entre un conjunto predefinido de consultas y luego se comunicaba con un servidor para recuperar la respuesta a la consulta seleccionada.  Empecé a preguntarme por qué el programa nunca pedía una contraseña para autenticarse en el servidor. Resultó que la contraseña estaba incrustada en el programa. El autor explicó que no había ningún problema porque 1) era un archivo binario, por lo que nadie podía leerlo, y 2) aunque pudieran leerlo, no podrían encontrar la cadena de la contraseña.

Ninguna de estas razones es válida y me gustaría utilizar esta publicación para demostrarlo. Mi programa de ejemplo solo escribe la «contraseña» en el terminal, pero la única diferencia entre este y un programa de producción real es el número de cadenas que un usuario malintencionado tendría que buscar.

#include <stdio.h>
#include <string.h>

main ()
{
char password [9] = {"secret"};

printf ("This is the password: %sn", password);

}
Figura 1: Programa básico con contraseña integrada.

Cuando se ejecuta, muestra

x1
Esta es la contraseña: secreto
listo  14:12:16
Figura 2 – Ejecución del programa básico

Para mostrar las cadenas en el módulo del programa, nuestro usuario malintencionado puede utilizar el comando strings del directorio >system>gnu_library>bin. El parámetro -n5 limita la salida a cadenas de 5 caracteres o más. He truncado la salida (. . . .) para acortar la visualización, pero se puede ver que la contraseña es una de las primeras cadenas que se muestran. Tener la contraseña muy cerca de palabras clave como «login» o «password» o de un ID de usuario identificable como «admin», «root» o «sql» facilita la búsqueda.

>sistema>gnu_library>bin>strings -n5 x1.pm
bind, versión 17.1.beta.be
phx_vos#
Noah_Davids.CAC
Pre-lanzamiento
Esta es la contraseña: %s
secreto
s$start_c_program
_preemption_cleanup
_exit.
 . . . .
Figura 3: Uso del comando strings de GNU para encontrar todas las cadenas de más de 4 caracteres en el módulo del programa.

Si el comando strings no está disponible, nuestro usuario malintencionado puede simplemente mostrar el módulo del programa. Sospecho que todos lo hemos hecho alguna vez por accidente y, aunque no es muy elegante, mostrará la contraseña. He vuelto a truncar la salida y, de nuevo, se puede ver la contraseña.

d x1.pm

%phx_vos#m16_mas>SysAdmin>Noah_Davids>x1.pm  11-01-13 14:13:59 mst

`00`01`00`1Abind, versión 17.1.beta.be`00`00`00`00`00`00`00`04ctp
`00`00`00`00`0
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`0
0phx_vos
+#`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`
00`00Noa.
 . . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`C
D'`8B`C0
+`00`00`00`00Esta es la contraseña: %s
`00`00`00`00`00`00`00secret`00`00`00`00`00`00`00`04`B0`00`00`00`00`01`00
`00`00,`
+`00`00`04`00`00`00`03@`00`FF`F8`FF`FE`00`04main`00`00`83`EC`2C`C7D$$`00.
 . . .
Figura 4: Visualización del módulo del programa para encontrar la contraseña.

Existen varias alternativas para integrar la contraseña en el programa.

La contraseña se puede guardar en un archivo y el programa puede leerlo. Solo las personas con «necesidad de ejecutar» tienen acceso de lectura al archivo, mientras que el resto no tiene acceso alguno. Por supuesto, la «necesidad de ejecutar» puede no equivaler a la «necesidad de conocer la contraseña». Además, siempre existe el riesgo de que alguien cambie el acceso por error.

Una alternativa es mantener la cadena de contraseña en el programa y limitar el acceso al mismo. Esta solución también adolece del problema de que «es fácil cambiar una lista de acceso». Si el programa se mantiene en el mismo directorio que otros programas que pueden ejecutar cualquier persona, la probabilidad de que se produzca un cambio en la lista de acceso aumenta.

Por último, puede guardar la contraseña en el módulo del programa, pero ocultando la cadena de alguna manera. El siguiente programa muestra una forma sencilla de hacerlo.

#include <stdio.h>
#include <string.h> 

main ()
{
char obfuscate [9] = {0x0c, 0x81, 0xe2, 0x94, 0xe6, 0x9c};
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char password [9];
int i;

for (i = 0; i < strlen (obfuscate); i++)
    password [i] = obfuscate [i] ^ key [i];
password [i] = 0x0;
printf ("This is the password: %sn", password);
}
Figura 5: Programa con cadena de contraseña ofuscada.

Como puede ver, lo único que hace es tomar los caracteres ofuscados y aplicarle una operación XOR con otro conjunto de caracteres clave para crear la contraseña. Al ejecutar el programa, se obtiene el mismo resultado que con el primer programa.

x2
Esta es la contraseña: secreto
listo  14:12:51
Figura 6: Ejecución del programa ofuscado.

Pero una búsqueda de cadenas no muestra nada que se parezca a la contraseña.

>sistema>gnu_library>bin>strings -n5 x1.pm
bind, versión 17.1.beta.be
phx_vos#
Noah_Davids.CAC
Versión preliminar
Esta es la contraseña: %s
s$start_c_program
_preemption_cleanup
_exit.
 . . .
Figura 7: el comando strings ya no muestra la contraseña.

Y aunque al mostrar el archivo se verán los caracteres ofuscados de la contraseña y los caracteres clave, hay que saber exactamente dónde buscar para encontrarlos y también cómo se combinan para crear la contraseña.

d x2.pm

%phx_vos#m16_mas>SysAdmin>Noah_Davids>x2.pm  11-01-13 14:15:27 mst

. . . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`CD'`8B`
C0`00`0
+0`00`00Esta es la contraseña: %s

`00`00`00`00`00`00`00`0C`81`E2`94`E6`9C`00`00`00`00`00`00`00`00`00`00`7F
`E4`81`
+E6`83`E8`85`EA`87`00`00`00`00`04`B0`00`00`00`00`01`00`00`00`00`00`00`0
4`01`B0
+`00`03@`00`FF`F0`00`08`FF`FE`00`04main`00`00`83`EC`89$X`89t$T`89|$P`C
7D$L`00
Figura 8: al mostrar el módulo del programa, ya no aparece la contraseña reconocible.

Esta solución aún no es perfecta, ya que el módulo del programa se puede desmontar para averiguar cómo se genera la contraseña. Sin embargo, desmontar el programa requiere mucha más sofisticación que simplemente mostrar el módulo del programa.

Tengo dudas sobre si poner la cadena de contraseña ofuscada en un archivo y luego proteger el archivo mediante una lista de acceso. Por un lado, si alguna vez hay que cambiar la contraseña, esta es una solución mucho mejor que incrustar la cadena de contraseña ofuscada en el programa. Por otro lado, sospecho que, aunque haya otras opciones de configuración en el archivo, poner la cadena en el archivo le dará a nuestro usuario malintencionado menos cadenas de contraseña potenciales que investigar.  Por supuesto, seguirá necesitando acceder al archivo y luego tendrá que averiguar la técnica de ofuscación y la clave.

Una última cosa: ¿cómo generé los caracteres de la contraseña ofuscada? Lo bueno de la operación XOR es que ((A XOR B) XOR B) es igual a A, así que simplemente escribí un pequeño programa para tomar la contraseña original, hacer XOR con los caracteres de mi clave e imprimir el resultado.

#include <stdio.h>
#include <string.h>

main ()
{
char password [9];
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char newPW [9];
int i;

strcpy (password, "secret");
for (i = 0; i < strlen (password); i++)
    {
    newPW [i] = password [i] ^ key [i];
    printf ("%xn", newPW [i]);
    }
}
Figura 9: Programa para generar contraseñas ofuscadas.