Opening minds: Bank tunneling 101 [SPANISH]


Introducción

En Internet diariamente se maneja un gran caudal de información, y es tal la cantidad que día a día las empresas requieren abastecerse de nuevos dispositivos de almacenamiento para poder soportarlo. Por ejemplo, el nuevo servidor de IBM puede procesar el equivalente a todas las transacciones de 100 cyber mondays en un sólo día.

James Hamilton, VP y distinguido Ingeniero en Amazon compartió este año varias curiosidades relevantes. Entre ellas cabe destacar las siguientes dos:
 - Todos los días, AWS agrega la suficiente capacidad en servidores como para soportar toda la infraestructura global que poseía Amazon cuando era una empresa de 7 mil millones de ingresos anuales (en el 2004).
- S3 tiene un 132% de incremento de transferencia de datos año tras año.

Según factshunt, en el 2014 existieron:
14.3 billones - Páginas web en Internet.
48 mil millones - Páginas web indexadas por Google.Inc.
14 mil millones - Páginas web indexadas por Microsoft Bing.
672 Exabytes - 672,000,000,000 Gigabytes (GB) de datos accesibles.
43,639 Petabytes - de tráfico total mundial en Internet en el 2013.
Más de 900,0000 servidores -  En Google.Inc, el datacenter más grande del mundo.
Más de 1 Yotta-byte - Cantidad de datos almacenados en Internet. (Incluye casi todo)

Nota: Actualmente Microsoft lidera el puesto de cantidad de servidores en el munrdo con 1.000.000, 100.000 por arriba de Google.

En diciembre del 2012, IDC y EMC estimaron que el tamaño digital del universo (esto sería, todos los datos digitales creados, replicados y consumidos en ese año) era de 2,837 exabytes (EB) y pronosticaron un crecimiento a 40,000EB para el 2020. Un exabyte equivale a mil petabytes (PB), o a un millón de terabytes (TB), o a mil millones de gigabytes (GB). Entonces, para el 2020, según IDC y EMC, el universo digital va a pesar arriba de los 5,200GB por persona en el planeta.

Ahora mirándolo desde una perspectiva de seguridad, está claro que entre tanta cantidad de información hay muchísimos datos sensibles; nuestros, de nuestros amigos, familiares; distribuidos en distintos lugares. Y cada vez que sale una noticia sobre robo de información no hay nada que nosotros podamos hacer porque los datos ya fueron robados. Los perjudicados a veces se toman días, meses o años en darse cuenta de que fueron hackeados.

Quien quiere robar un bien, para lograr su cometido, necesita tan sólo una falla en el sistema, mientras quienes están en la vereda de en frente necesitan contemplar absolutamente en todos los lugares posibles todas las variaciones de probables ataques, y eso es prácticamente imposible. Siempre hay que mantener un balance entre seguridad vs practicidad vs presupuesto vs intelecto.

La idea de esta serie de investigaciones es poner en evidencia qué se podría hacer, como prevenir y tratar de anticiparse a futuros problemas.


Historia:

No todos los robos de información o de bienes son íntegramente informáticos. Vamos a comenzar dando el ejemplo de uno de los tipos de robos más interesantes, los boquetes, obras maestras que contienen una planificación e inversión muy importante.

¿Por qué? Porque es uno de los robos con mayor impacto desde el lado de la seguridad física que se puede realizar, y con información pública, accesible a todos, vamos a demostrar qué tan difícil puede llegar a ser planificar el comienzo de uno.

Según Wikipedia un boquete es: una apertura o un espacio.

En nuestro contexto vamos a hablar de boquetes, como agujeros que se han utilizado para extraer contenido valioso de distintos lugares, más generalmente bancos o cajas fuertes, principalmente vamos a hablar de algunos países de América, entre ellos Argentina, Brasil y Estados Unidos.

Debajo vamos a listar algunas de las noticias que hemos encontrado, ordenadas por país, en donde se observan casos satisfactorios de robos mediante el uso de boquetes.

Argentina


USA

Colombia

Brasil

Canada

Francia:

Berlin

United Kingdom

Thailand

También encontramos algunos intentos fallidos que no está de más mencionarlos:

Un resumen en forma de resumen visual para todas las noticias mencionadas previamente:

Country
Bank
Year
Distance
Money
Not Money
Argentina
Banco Galicia1976AR$5.000.00050kg en joyas
ArgentinaBanco Mercantil1992200 cajas
ArgentinaBanco Credito199750 mtsAR$5.000.000
ArgentinaBanco Río2006AR$8.000.0008kg+ joyas
ArgentinaBanco Macro2011US$3.000.000+
ArgentinaBanco Provincia201130 mtsAR$10.000.000
ArgentinaBanco Galicia201120 mtsAR$500.000
BerlinCommerzbank1995170 mts$12.400.000
BerlinBerliner Volksbank201330 mtsL$8.300.000
BrasilBanco Central2005200 mtsR$160.000.000
CanadaRoyal Bank19866 mtsUS$196.000
ColombiaBanco de la República de Pasto197750 mtsAR$82.000.000
ColombiaCaja Agraria199120 mts
EEUUCobb Exchange Bank196442 mtsUS$1.000
EEUUFirst Interstate Bank198630 mtsUS$270.000
EEUUBank of Quitman198620 mtsUS$20.000
EEUUBank of America198720 mtsUS$91.000
FranciaSociété Générale bank19768 mtsL$6.000.000joyas
FranciaSociété Générale bank1976$5.000.000
FranciaBanco Río198746 mts300 cajas
FranciaCrédit Lyonnais2010EU$24.000.000
ThailandThai Bank199415 mtsUS$100.000
UKLloyds Bank197115 mtsL$1.500.000260 cajas
UKBlockbusters201212 mtsL$6.000
UKTesco Store201430 mtsL$100.000+

Technical research

Una de las decisiones más importantes en la lógica detrás del robo mediante boquete, es elegir el lugar desde donde se comenzará a realizar el mismo. Las variables que hay que tener en cuenta son: proximidad, movimiento, eliminación de residuos, sonidos, etc.

Desde el lado de la informática se nos ocurrió tratar de ver qué tan difícil podía ser hacer un sistema que recomiende dado un target (coordenada geográfica o dirección) un lugar próximo/óptimo para comenzar a realizar un boquete. Para empezar vamos a basarnos primero en buscar lugares próximos, que sería nuestro cuello de botella. Así que por el momento vamos a discriminar algunas variables.

Para ello necesitaremos lo siguiente:
- Información pública de bancos
- Información de alquiler/venta de inmuebles
- Geolocalización

Resultados obtenidos

El siguiente mapa describe solo algunos de los casos obtenidos luego del investigación:


Datos de bancos

Para la información pública de bancos recurrimos a la búsqueda de sucursales provenientes de los sitios de los bancos más conocidos, y a directorios online. La búsqueda está restringida originalmente para Argentina, Brasil y Estados Unidos pero claramente se puede expandir a cualquier país. Algunos datos fueron extraídos de una manera un poco más manual que otros, pero generalmente todos pasaron por un proceso de scrapping en cierto punto. Los directorios que usamos no van a ser mencionados porque son irrelevantes a la investigación.

Inmuebles

Para la parte de inmuebles, en Argentina teníamos en mente zonaprop, sumavisos y mercadolibre. Estas primeras dos resultaron no tener API (o al menos accesible a terceros), pero MercadoLibre sí, ya que posee una librería en python muy sencilla para hacer requests.

En países latinoamericanos, como Brasil, también se puede usar MercadoLibre aunque no sea el estándar de búsqueda de inmuebles para algunos, pero para esta investigación como prueba de concepto cumple el propósito, así que decidimos usar también su API en Brasil, la cual no alteraba nada más que un par de caracteres en como se usa para Argentina.

Para Estados Unidos fue un poco más complicado, las APIs que existían no daban mucha información, y las que lo hacían estaban muy limitadas. Mostraban cotizaciones estimadas en base a direcciones que uno proveía o información en base a un inmueble por ID (el cual previamente tendría que haber sido obtenido por otro camino), o simplemente precios de hipotecas. Ninguna excepto Zillow nos devolvía datos 'útiles' que nos sirvan para automatizar un poco la búsqueda.

Geolocalización

La respuesta era muy fácil. Google maps provee una función en su API que mediante una dirección se pueden obtener las coordenadas geográficas, algo similar a lo que hacemos comúnmente desde el navegador pero de manera automática.


Aplicación

Google Maps

Como dijimos más arriba, lo único que necesitamos de Google maps es poder calcular las coordenadas geográficas en base a una dirección física. El código para esto es muy sencillo, de hecho no se necesitan muchas líneas:

import json
from urllib2 import urlopen
from urllib import urlencode

GEOAPI_AUTH = "YOUR_API_KEY_AUTH"
GEOAPI_URL = "https://maps.googleapis.com/maps/api/geocode/json?"

def getCoordinates(addr):
    params = urlencode({'sensor' :  'false', 'address': addr, 'key' : GEOAPI_AUTH})
    data = json.loads(urlopen(GEOAPI_URL+params).read())
 
    if data['results']:
        return (data['results'][0]['geometry']['location']['lat'],
    data['results'][0]['geometry']['location']['lng'])

    return None

A esto lo vamos a necesitar para poder calcular las distancias, una vez que se tienen las coordenadas de un target, y de un posible candidato, se evalúa con un cálculo matemático su distancia:

import math
GEO_ERR = -1

def getDistance(coordA, coordB):
    if coordA and coordB:
        R = 6371
        dLat = math.radians((coordB[0] - coordA[0]))
        dLon = math.radians((coordB[1] - coordA[1]))
        lat1 = math.radians(coordA[0])
        lat2 = math.radians(coordB[0])
        a = math.sin(dLat/2) * math.sin(dLat/2) + \
            math.sin(dLon/2) * math.sin(dLon/2) * math.cos(lat1) * math.cos(lat2)
        c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
        d = R * c
        return d * 1000
    return GEO_ERR

Notar que usamos -1 para saber cuándo me está fallando el cálculo de la distancia, ya sea por la API o por algún problema con las coordenadas.

Vamos a probar de medir como ejemplo cuánto mide de largo la plaza que rodea el Obelisco de Buenos Aires.

getDistance([-34.6044185,-58.3815473],[-34.602953,-58.381634]) = 163.1492529420307 mts

Con estos resultados, y basándonos en las distancias que han tenido los boquetes, podemos suponer un límite de interés hasta de 300 metros, deducido empíricamente basado en las noticias que mostramos al comienzo del artículos, más de eso ya sería demasiado.

Zillow API

El problema de la mayoría de las APIs de inmuebles (real state) que encontramos es que la mayoría de los datos son privados, y los que son públicos son escasos.
Sumado a eso, la manera en la que uno puede ver los datos de un inmueble de manera manual es sencilla, pero si se quiere automatizar por medio de la API, no, ya que a ésta le falta información que mediante ella misma no se puede conseguir.

Explicado de una manera más sencilla, la API proporciona datos de un inmueble sólo si se tiene el ID del mismo, pero no permite buscar bajo ningún criterio. Sin embargo, la página en sí permite buscar en los alrededores de un código postal, y ofrece una cantidad "interesante" (o que para el propósito de un PoC basta) de inmuebles en el área.

Así que con una mezcla de scrapping + uso de la API conseguimos lo que queríamos.

La manera en la que se buscan propiedades en el sitio es introduciendo el código postal, la ciudad y el nombre del Estado. Vamos a tomar de ejemplo San Francisco, California, 94121.

El buscador automáticamente formatea (si es correcto), la url a lo siguiente:
http://www.zillow.com/ciudad-estado-codigopostal

Osea que para nuestro ejemplo quedaría así: http://www.zillow.com/san-francisco-ca-94121/

Como se puede observar en la página del ejemplo, a la derecha se listan algunas propiedades recomendadas, y en su link está asociado su Zillow Property ID, que es el que más adelante vamos a usar con la API para obtener datos adicionales, que la página por el momento no nos brinda.

Definimos una regexp para matchear los IDs: "\/[0-9]*_zpid\/" y luego implementamos un request para acceder de manera dinámica, a una ciudad dentro de un Estado, variando su área dependiendo su zipcode.

import re
from urllib2 import urlopen as uopen

ZILLOW_PATTERN = re.compile("\/[0-9]*_zpid\/")

def getCandidates(city, state, zcode):
    domain = "http://www.zillow.com/"
    url = "%s%s%s%s" % (domain, city, state, zcode)
    raw_results = uopen(url).read()

    return set(ZILLOW_PATTERN.findall(raw_results))


¿Por qué elegimos hacerlo de esta manera? Primero porque no había muchas más opciones, y segundo porque la mayoría de los directorios de bancos de USA contienen el zipcode al lado de la dirección física del Banco, lo cual nos sirve muchísimo para mejorar las proximidades a la hora de las búsquedas.

Una vez que se tienen todos los IDS hay que alimentar a la API con ellos, y obtener por ejemplo las direcciones de cada propiedad, que sería lo primordial.

from urllib2 import urlopen as uopen
import math
import xml.etree.ElementTree as ET
import re

ZILLOW_AUTH = "YOUR-API-KEY-AUTH"
ZILLOW_ESTIMATE = "http://www.zillow.com/webservice/GetZestimate.htm?zws-id="
ZILLOW_PATTERN = re.compile("\/[0-9]*_zpid\/")


def getCandidates(bank_coord, city, state, zcode):
    domain = "http://www.zillow.com/"
    url = "%s%s%s%s" % (domain, city, state, zcode)
    raw_results = uopen(url).read()


    candidates = []
    for x in set(ZILLOW_PATTERN.findall(raw_results)):
        xml_response = uopen(ZILLOW_ESTIMATE+ZILLOW_AUTH+"&zpid="+x[1:-6]).read()
        root = ET.fromstring(xml_response)

Hasta aquí obtuvimos todos los datos sobre una propiedad mediante la API en formato xml. Vamos a parsear la parte más importantes y obtener la distancia. A cada candidato lo guardamos en una lista y los ordenamos por proximidad.

        lat = getVal(root.find('response/address/latitude'))
        lon = getVal(root.find('response/address/longitude'))

        if lat != None and lon != None:
            candidates.append([getDistance([float(lat), float(lon)], bank_coord), homedetail])

        candidates = sorted(candidates, key=lambda x: x[0])

Luego solamente mostramos los candidatos que no superan el límite de distancia.

    for distance, details in candidates:
        if distance > LIMIT:
            print "[-] Skipping next candidates, over %s meters" % LIMIT
            break
        print "[!] Found candidate:"
        print "[+] Distance:", distance
        print "[+] Details:", details
        print "-------------"

Y eso sería todo para la parte de USA, lo único que hay que hacer es normalizar los datos de entrada, que no es algo muy difícil, e iterar por banco.

MercadoLibre API

A diferencia de las APIs del estilo Zillow, la única opción que nos da MercadoLibre para buscar propiedades es con 3 filtros (Estado, Ciudad, Vecindario) y no tienen ningún criterio para ordenar los resultados que nos sean útiles. Porque claramente a menos que uno esté buscando cotizaciones sobre un área en especial, para lo cual MercadoLibre no sirve y la mayoría de las APIs "real state" sí, no se puede ordenar con criterio de proximidad. Así que los resultados van a depender de la suerte que tengamos y de lo finita que sea nuestra búsqueda gracias al abuso de los 3 filtros. Básicamente dependemos de una buena data de bancos normalizada.

Para Argentina usamos la provincia de Buenos Aires, algunas ciudades y partes de Capital Federal. Para Brasil usamos el Estado de São Paulo, sus distritos y municipios. El proceso para buscar en ambos países es muy similar.

Particularmente la API de mercadolibre ha sido una de las que me ha resultado más fácil trabajar en general. Es muy sencilla y la parte de desarrollo para realizar testeos con documentación incluida que ellos te proveen en su sitio es suficiente como para entender todo.

Debajo se encuentra un simple, pero completo, ejemplo de como obtener toda la categoría Inmuebles usando una librería en python que se puede descargar gratuitamente.

from meli import Meli

MELI_CID = "1337"
MELI_AUTH = "YOUR-API-KEY-AUTH"

meli = Meli(client_id=MELI_CID, client_secret=MELI_AUTH)

url = "/sites/MLA/search?category=MLA1459"

r = meli.get(url)

if r.status_code == 200:
    print r.content

El response guarda en formato JSON los resultados de la consulta. En ese caso la URL representa los parámetros que recibe la API.

No vamos a utilizar muchas palabras para explicar cómo funciona toda la API porque no es el propósito de ésta redacción y porque ya hay una documentación extensa para ello. Pero vamos a limitarnos a decir que MLX se refiere a la notación usada por cada país, en donde X representa alguna inicial que lo identifique. MLA para Argentina y MLB para Brasil en estos casos. El filtro de categoría está puesto para inmuebles (1459), que es lo que nos interesa.

Luego para filtrar una búsqueda completa, con todos los parámetros que más nos sirven sería de la siguiente forma:

/sites/MLA/search?category=MLA1459&state=Buenos Aires&city=Capital Federal&neighborhood=Microcentro"

En realidad para cada valor de cada filtro hay un ID único en la base de datos, y no es siempre necesario aplicar todos los filtros a la vez.

Los filtros tienen IDs de este estilo:

"capital federal": "TUxBUENBUGw3M2E1"
"belgrano": "TUxBQkJFTDcyNTJa"

Cada ID (generalmente) está asociado implícitamente con su locación geográfica. Por ejemplo si se tienen dos ciudades de distintas provincias con el mismo nombre, cuando se use su ID automáticamente se va a apuntar a la provincia a la cual pertenece. De todos modos no es difícil de obtenerlo, armarse de una buena base de datos para solucionar esto no es un problema, ya que son datos públicos y de libre acceso.

Esto nos favorece porque las búsquedas las podemos hacer aplicando el filtro en la cota más chica, que sería vecindario. Si nosotros sabemos que estamos analizando bancos de Capital Federal, y la dirección del Banco es en Belgrano, aplicamos únicamente el filtro de vecindario sobre él.


/sites/MLA/search?category=MLA1459&neighborhood=TUxBQkJFTDcyNTJa

También con limit y offset se pueden limitar los resultados para ir obteniendo en cuotas.

El resto es interpretar los resultados, obtener las direcciones de cada propiedad, calcular la distancia entre el banco desde el cual se están aplicando los filtros, y fijarse si la distancia nos es útil.


Detalle de resultados obtenidos

Recordemos que las vistas que vamos a proveer desde Google Maps a continuación no son más que para orientarnos un poco, ya que las distancias para realizar un túnel mediante un boquete no necesitan respetar las direcciones de las calles ;)

Algunas proximidades obtenidas sobre Brasil, Sao Paulo:


En el caso del Banco Safra, las coordenadas del anunciante nos envían a 6 metros de un banco, pero en el artículo la dirección esta mucho más lejos. Esto sería un falso positivo pero no por nuestros mecanismos de búsqueda, sino porque dependemos de que los usuarios completen bien los campos.

Y a veces nos encontramos que un mismo inmueble sirve para cubrir varios Bancos:

De menor a mayor algunos de las resultados para algunos de los bancos obtenidos en Argentina, Buenos Aires (poco tiempo de procesamiento):


Algunos ejemplos para USA, San Francisco, aprovechando el límite de requests de la API (poco tiempo de procesamiento):

Plan de contingencia:

Creemos que las Entidades e Instituciones deben plantear nuevos requisitos y controles en la comercialización de inmuebles sobre aquellas ubicaciones cercanas o puntos estratégicos. Inclusive los mismos bancos deberían tomar una actitud proactiva, determinando ellos mismos las posibilidades existentes de sufrir un atentado de este estilo.


Future work

Mercadolibre

Adicionalmente se pueden hacer un montón de mejoras para poder acelerar el proceso automático previamente mencionado, y con más performance.

En el caso de Mercadolibre, como los filtros son lo que más nos condicionan, se pueden organizar los datos de los bancos por filtros que van a aplicar, y por cada resultado por filtro comparar contra cada uno de los bancos dentro de ese grupo, ahorrándonos así varias iteraciones innecesarias.

Un ejemplo para entender esto mejor.

Teniendo los siguientes bancos:
Banco A, Buenos Aires, Capital Federal, Palermo, Mario Bravo 1000
Banco B, Buenos Aires, Capital Federal, Belgrano, Cabildo 100
Banco C, Buenos Aires, Capital Federal, Palermo, Honduras 1000
Banco D, Buenos Aires, Capital Federal, Belgrano, Virrey del Pino 100
Banco E, Buenos Aires, Capital Federal, Palermo, Córdoba 3000

Los bancos A, C y E aplican los siguientes filtros:
State : Buenos Aires
City: Capital Federal
Neighborhood: Palermo

Y los bancos B y D los siguientes:
State : Buenos Aires
City: Capital Federal
Neighborhood: Belgrano

Como las direcciones finales se discriminan, entonces podemos agruparlos por filtros, donde cada filtro es una especie de key única para un conjunto que contendrá todos los bancos que lo compartan. Logrando así, en vez de iterar por cada banco, iteramos por filtros, y frente a cada resultado obtenido se compara la distancia con cada uno de los miembros del conjunto que posee ese filtro.

Zillow

En el caso de Zillow, existe una especie de solución para los límites de la API. La cantidad de peticiones es por AUTH KEY, pero con poner cualquier email que sea capaz de recibir correo (ya que a los datos nos los envían él) basta. No hay límites ni captcha. Después es cuestión de chequear cuando se haya llegado al límite (o poner un contador, por qué no), y rotar de KEY.

Google maps

La API de Google maps también tiene límite, pero apis como mercadolibre no tienen y proveen adicionalmente la posibilidad de obtener las coordenadas del inmueble (aunque no siempre están), así que en caso de que la geoapi de google llegue al límite se pueden usar esas coordenadas.

Servicios

Otra cosa que se puede hacer es pagar por todos los servicios que usamos, para reducir o quitar completamente los límites. La versión paga de la API de Google Maps posee la posibilidad de realizar búsquedas alrededor de una área en especial, y filtrar por categorías. Dentro de las categorías se encuentra una llamada "banco" :), no hace falta decir más nada, ¿no?

Spoiler: Se itera una sola vez por todos los inmuebles disponibles, obteniendo todos los bancos cercanos (filtrando por categoría Banco), logrando así lo más óptimo que existiría en complejidad relacionado a estas búsquedas.


Conclusión

Como han podido ver, comenzar a pensar en la planificación de un robo a un banco en cuestiones de proximidad es mas sencillo de lo que uno puede imaginar.

Vivimos en un mundo pletórico de datos. La percepción que hoy tenemos cada uno de nosotros mismos ha cambiado, como también la apreciación que tenemos de nuestro entorno. Pasamos de nuestros barrios, ciudad o país a un horizonte de carácter planetario.

Creemos que estas investigaciones o trabajos deben ayudar a tomar conciencia de que la información que se encuentra expuesta es abundante, donde no se tiene claro que brindamos y a quienes lo hacemos echando a la suerte su uso para propósitos buenos o malos.

Y ustedes, ¿saben a quién le confían sus datos?

Funny facts

#1.- Durante el robo al Banco Río de Acasuso en el 2006, los boqueteros todo el tiempo que permanecieron adentro del local, pidieron pizza y gaseosas y le cantaron el feliz cumpleaños a una de las rehenes.
Cuando la policía se decidió a entrar al banco, sólo encontraron a los rehenes, el túnel por el cual huyeron los ladrones, armas de juguete y una nota que decía "En barrio de ricachones, sin armas ni rencores. Es sólo plata, no amores".
#2.- Durante el robo al Banco de la República de Pasto en en 1977, los delincuentes tuvieron tiempo hasta para hacer bromas. En la bóveda pintaron un letrero grande que decía: "Chanfle, no contaban con mi astucia".

Links útiles

Sucursales para bancos, y cajas de seguridad, páginas oficiales.


Este es el segundo articulo de una serie demostrando la distintas formas de abusar de la información publica. Agradecemos a Matias A. Ré Medina y Francisco Amato por la enorme contribución al mismo.
Post a Comment
Thanks for your comment