Shodan – Python API

Durante el pasado Black Friday el portal de consulta de cualquier dispositivo conectado a Internet Shodan puso de oferta el upgrade de las cuentas free por  49  5 €, oferta que aproveché para actualizar mi cuenta actual.

Con esta cuenta se obtienen una serie de beneficios a la hora de realizar consultas, evitando algunas limitaciones que tiene la versión gratuita. Estas ventajas, a su vez, se limitan a unos umbrales anuales, a diferencia de las cuentas de pago mensual cuyos contadores se reinician todos los meses.

Con esta actualización podemos probar ciertas funcionalidades, tanto en la web como desde cualquiera de sus APIs. Desde la web oficial podemos acceder a la información de las APIs (pincha aquí). Me he centrado en la API de Python, publicado en el enlace https://shodan.readthedocs.io/en/latest/, y estos son los comandos que me resultaron de más interés.


Instalación

Para instalar la librería de Shodan usamos el comando pip.

# pip search shodan
shodan (1.10.4)   - Python library and command-line utility for Shodan (https://developer.shodan.io)
  INSTALLED: 1.10.4 (latest)
RoboShodan (1.2)  - Robot Framework Library for Shodan

En mi caso ya lo tenía instalado, de lo contrario ejecutaríamos el siguiente comando.

pip install shodan

Conectar con la cuenta

Para conectar con la cuenta debemos de consultar la API_KEY publicada en la sección MyAccount de Shodan.

Ahora importamos la librería y cargamos la aplicación con la API_KEY.

import shodan
SHODAN_API_KEY = "XXX"
api = shodan.Shodan(SHODAN_API_KEY)

Información de la cuenta

Una vez conectado con la cuenta podemos conocer su información actual con el métido info().

api.info()

Resultado.

{u'https': False,
 u'plan': u'dev',
 u'query_credits': 100,
 u'scan_credits': 98,
 u'telnet': False,
 u'unlocked': True,
 u'unlocked_left': 100}

Búsqueda básica

Para realizar una búsqueda debemos de usar el método search e iterar con todos los resultados usando un bucle for. Por ejemplo, vamos a realizar una búsqueda por el servidor web Apache.

try:
  results = api.search('apache')
  print("Resultados encontrados: {}' . format(results['total']))
  for result in results['matches']:
    print('IP: {}' . format(result['ip_str']))
    print(result['data'])
    print('')

except shodan.APIError, e:
  print('Error: {}' . format(e))

Resultado.

Resultados encontrados: 25504866
 IP: 213.226.249.184
HTTP/1.1 302 Moved Temporarily
Date: Fri, 30 Nov 2018 11:36:15 GMT
Server: Apache
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Location: http://213.226.249.184:5000/
Content-Length: 0
Content-Type: text/plain



IP: 1.51.246.132
HTTP/1.1 200 OK
Date: Fri, 30 Nov 2018 11:27:18 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
Last-Modified: Thu, 19 Jul 2018 08:41:31 GMT
ETag: "2a0-5715625581d4b"
Accept-Ranges: bytes
Content-Length: 672
Content-Type: text/html

...

Búsqueda de host

Para buscar información de un host concreto usamos el método host y la función get.

results = api.host('8.8.8.8')

# Mostramos la informacion.
print("""
  IP: {}
  Oganization: {}
  Operative Sistem: {}
""" . format(results['ip_str'], results.get('org', 'n/a'), results.get('os', 'n/a')))

Resultado.

IP: 8.8.8.8
Organization: Google
Operative Sistem: None

Mostrar Banners

Usando el comando anterior podemos obtener los Banners mostrados por los servicios cuando se realiza una conexión, y registrados en Shodan.

for item in results['data']:
  print("""
    Port: {}
    Banner: {}
    """.format(item['port'], item['data']))

Resultado.

  Port: 53
  Banner:
Recursion: enabled

Como el resultado anterior es muy simple, a continuación se muestra el resultado tras consultar otra IP diferente.

  Port: 443
  Banner: HTTP/1.1 200 OK
Date: Wed, 28 Nov 2018 07:09:50 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=10
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.38-0+deb8u1
Server: RainLoop
Strict-Transport-Security: max-age=15768000




  Port: 993
  Banner: * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN AUTH=LOGIN
A001 OK Pre-login capabilities listed, post-login capabilities have more.
* ID ("name" "Dovecot")
A002 OK ID completed.
A003 BAD Error in IMAP command received by server.
* BYE Logging out
A004 OK Logout completed.


Script búsqueda básica

El siguiente script, que llamaremos busquedaBasica.pypermite realizar una búsqueda básica con el método search y mostrar las IPs registradas con el patrón indicado. Se debe de indicar algún parámetro tras ejecutar el script.

#!/usr/bin/env python
#
# Buscador para imprimir una lista de IPs resultantes de una consulta.

import shodan, sys

API_KEY = "XXX"

# Validacion de entrada
if len(sys.argv) == 1:
        print 'Uso: %s <search query>' % sys.argv[0]
        sys.exit(1)

try:
        # Setup de API
        api = shodan.Shodan(API_KEY)

        # Perform the search
        query = ' '.join(sys.argv[1:])
        result = api.search(query)

        # Loop through the matches and print aeach IP
        for service in result['matches']:
                print service['ip_str']

except Exception as e:
        print 'Error: %s' % e
        sys.exit(1)

Resultados.

# ./basicSearch.py
Uso: ./basicSearch.py <search query>

# ./basicSearch.py postfix
110.164.188.212
107.170.209.96
194.49.99.178
107.6.142.228
91.134.112.234
....

Búsqueda con FACETS

Los FACETS son propiedades de los resultados, como pueden ser organización, dominio, puerto, …, muy útiles para organizar los resultados de una búsqueda. Nos permite, entre otras cosas, conocer qué versiones son las más populares, o qué países son los que más resultados muestran en función de lo especificado en la búsqueda.

Para ello usamos el método count() para buscar en Shoddan sin devolver cualquier resultado. Preguntamos por información concreta, acotando la búsqueda, y por consiguiente el resultado. Por defecto se muestra el top 5 del resultado de cada facet, a no ser que indiquemos lo contrario como vamos a poder ver en el siguiente ejemplo (facet country).

En el siguiente script, que llamaremos query-search.py indicamos el patrón por el que queremos buscar.

#!/usr/bin/env python
#
# Busqueda en Shodan e impresion del resultado de la query.

import shodan, sys

# Configuracion
API_KEY = "XXX"

# Lista de propiedades que queremos mostrar.
# Por defecto se muestran los primeros 5 resultados.
FACETS = [
        'org',
        'domain',
        'port',
        'asn',
        # Solo queremos mostrar el top 3 de paises.
        # Por defecto se muestran 5!!!
        # Si queremos mostrar mas de 5 indicamos ('country', 1000) para mostrar 1000, por ejemplo.
        ('country', 3),
]

FACET_TITLES = {
        'org': 'Top 5 Organizaciones',
        'domain': 'Top 5 Dominios',
        'port': 'Top 5 Puertos',
        'asn': 'Top 5 Sistemas Autonomos',
        'country': 'Top 3 Paises',
}

# Validacion de entrada.
if len(sys.argv) == 1:
        print 'Usage: %s <search query>' % sys.argv[0]
        sys.exit(1)

try:
        # Setup API
        api = shodan.Shodan(API_KEY)

        # Generamos la variable de la query con los argumentos.
        query = ' '.join(sys.argv[1:])

        # Usamos el metoddo count() porque no devuelve resultados y no requiere un plan de pago.
        # Y es mas rapido que ejecutar un search().
        result = api.count(query, facets=FACETS)

        print 'Shodan Summary Information'
        print 'Query: %s' % query
        print 'Total Results: %s\n' % result['total']

        # Imprimer la informacion de los facets.
        for facet in result['facets']:
                print FACET_TITLES[facet]

                for term in result['facets'][facet]:
                        print '%s: %s' % (term['value'], term['count'])

                # Imprimerr una linea en blanco entre el resumen y la info.
                print ''

except Exception, e:

        print 'Error: %s' % e
        sys.exit(1)

Resultado.

./query-summary.py apache
Shodan Summary Information
Query: apache
Total Results: 34612043

Top 5 Organizations
Amazon.com: 808061
Ecommerce Corporation: 788704
Verio Web Hosting: 760112
Unified Layer: 627827
GoDaddy.com, LLC: 567004

Top 5 Domains
secureserver.net: 562047
unifiedlayer.com: 494399
t-ipconnect.de: 385792
netart.pl: 194817
wanadoo.fr: 151925

Top 5 Ports
80: 24118703
443: 8330932
8080: 1479050
81: 359025
8443: 231441

Top 5 Autonomous Systems
as32392: 580002
as2914: 465786
as26496: 414998
as48030: 332000
as8560: 255774

Top 3 Countries
US: 13227366
DE: 2900530
JP: 2014506

Listado de métodos

En el siguiente enlace se pueden consultar todos los métodos publicados para la API. Dependiendo de la licencia adquirida algunos métodos no podrán ser utilizados.

https://shodan.readthedocs.io/en/latest/api.html


Fuentes

Shodan oficial pincha aquí.

Shodan API reference pincha aquí.

Shodan Python GitHub pincha aquí.

Shodan Python doc pincha aquí.

2 Replies to “Shodan – Python API

Comments are closed.