Tabla de Contenidos
Extracción de información de servidores DNS
Módulo perteneciente al curso Python avanzado para proyectos de seguridad
La dirección IP se puede traducir en cadenas legibles por humanos llamadas nombres de dominio. En esta sección, veremos como obtener la información de los servidores DNS y crearemos un cliente DNS en Python.
DNS son las siglas de Domain Name Server, servicio de nombres de dominio utilizado para relacionar direcciones IP con nombres de dominio.
El protocolo DNS se utiliza para distintos propósitos. Los más comunes son:
- Se emplea para asignar un rango de ips a un único dominio.
- Resolución de nombres: Dado el nombre completo de un host, obtener su dirección IP.
- Resolución inversa de direcciones: Es el mecanismo inverso al anterior. Consiste en, dada una dirección IP, obtener el nombre de host asociado a la misma.
- Resolución de servidores de correo: Dado un nombre de dominio (por ejemplo gmail.com) obtener el servidor a través del cual debe realizarse la entrega del correo electrónico (por ejemplo,
gmail-smtp-in.l.google.com).
DNS también es un protocolo que los dispositivos usan para consultar a los servidores DNS con el objetivo de resolver nombres de host en direcciones IP (y viceversa).
La herramienta nslookup viene con la mayoría de los sistemas Linux y Windows y nos permite realizar consultas DNS desde la línea de comandos.
En este ejemplo determinamos que el dominio python.org tiene la dirección IPv4 45.55.99.72:
$ nslookup python.org
Esta es la resolución de dirección para el dominio python.org:
Server: 192.168.18.1 Address: 192.168.18.1#53 Non-authoritative answer: Name: python.org Address: 45.55.99.72
Servidores DNS
Los seres humanos recordamos mucho mejor los nombres para relacionar objetos que secuencias largas de números. Para cualquiera es mucho más sencillo recordar el dominio google.com que la dirección ip. Además, la dirección ip puede cambiar por movimientos en la infraestructura de red, mientras que el nombre de dominio puede continuar siendo el mismo.
Su funcionamiento se basa en el uso de una base de datos distribuida y jerárquica en la que se almacenan nombres de dominios y direcciones IP, así como la capacidad de prestar servicios de localización de servidores de correo. Los servidores DNS permiten la consulta de diferentes tipos de registros en los que se incluyen servidores de correo, direcciones IP, nombres de dominios y otros servicios.
Los servidores DNS se ubican en la capa de aplicación y suelen utilizar el puerto 53 (UDP).
Cuando un cliente envía un paquete DNS para realizar algún tipo de consulta, debe enviar el tipo de registro que desea consultar. Algunos de los registros más utilizados son:
A: Permite consultar la dirección IPv4AAAA: Permite consultar la dirección IPv6MX: Permite consultar los servidores de correoNS: Permite consultar el nombre del servidor (Name Server)TXT: Permite consultar información en formato texto
Módulo DNSPython
Python dispone del módulo dnspython que permite realizar operaciones de consulta de registros contra servidores DNS.
Este módulo permite el acceso tanto a alto nivel por medio de consultas a registros DNS, como a bajo nivel permitiendo la manipulación directa de zonas, mensajes, nombres y registros.
La instalación se puede hacer tanto a través del comando pip install dnspython como bajando el código fuente del repositorio de github y ejecutar el comando setup.py install.
La principal utilidad de dnspython con respecto a otras herramientas de consulta de DNS, como nslookup, es que puede controlar el resultado de las consultas desde Python y luego esa información puede usarse para otros fines en un script.
A la hora de hacer uso de este módulo los paquetes necesarios serían los siguientes:
>>> import dns >>> import dns.resolver
La información que podemos obtener de un determinado dominio es:
- Registros para servidores de correo
ansMX = dns.resolver.query("dominio","MX")
- Registros para servidores de nombre
ansNS = dns.resolver.query("dominio","NS")
- Registros para direcciones IPV4
ansA = dns.resolver.query("dominio","A")
- Registros para direcciones IPV6
ansAAAA = dns.resolver.query("dominio","AAAA")
En este ejemplo, estamos haciendo una consulta con respecto a la dirección IPv4 de del dominio python.org con el submódulo dns.resolver:
>>> import dns.resolver >>> respuestas = dns.resolver.query('python.org', 'A') >>> for respuesta in respuestas: >>> print('IP', respuesta.to_text())
Determinar el destino de un registro MX y su preferencia
Con el submódulo dns.resolver también podemos acceder a la información almacenada en los registros de intercambio de correo de ExChange para ver qué hosts tienen prioridad al intercambiar correos electrónicos a través de Internet.
Puede encontrar el siguiente código en el archivo dns_registro_mx.py:
import dns.resolver respuestas = dns.resolver.query('google.com', 'MX') for respuesta in respuestas: print('Host', respuesta.exchange, 'tiene una preferencia de ', respuesta.preference)
Este podría ser el resultado del script anterior:
Host alt3.aspmx.l.google.com. tiene una preferencia de 40 Host alt2.aspmx.l.google.com. tiene una preferencia de 30 Host alt4.aspmx.l.google.com. tiene una preferencia de 50 Host aspmx.l.google.com. tiene una preferencia de 10 Host alt1.aspmx.l.google.com. tiene una preferencia de 2
Implementar un cliente consulta de registros DNS
En este ejemplo práctico utilizaremos dnspython para ejecutar consultas en varios tipos de registros DNS, como IPv4 (A), IPv6 (AAAA), servidores de nombres (NS) e intercambio de correo (MX).
Puede encontrar el siguiente código en el archivo registros_dns_python.py:
#!/usr/bin/env python import dns import dns.resolver import dns.query import dns.zone import dns.name import dns.reversename import sys if len(sys.argv) != 2: print("[-] uso python DNSPythonExample.py <nombre_dominio>") sys.exit() dominio = sys.argv[1] respuestaRegistroA,respuestaRegistroMX,respuestaRegistroNS,respuestaRegistroTXT=(dns.resolver.query(dominio,'A'), dns.resolver.query(dominio,'MX'), dns.resolver.query(dominio, 'NS'), dns.resolver.query(dominio, 'TXT')) print("Servidores de correo") print("--------------------") print(respuestaRegistroMX.response.to_text()) print("\nServidores de nombre") print("--------------------") print(respuestaRegistroNS.response.to_text()) print("\nDirecciones IPV4") print("--------------------") print(respuestaRegistroA.response.to_text()) print("\nRegistros DNS") print("--------------------") print(respuestaRegistroTXT.response.to_text())
En la salida el anterior script podemos ver los diferentes tipos de registros para el dominio python.org:
Servidores de correo -------------------- id 56661 opcode QUERY rcode NOERROR flags QR RD RA ;QUESTION python.org. IN MX ;ANSWER python.org. 532200 IN MX 50 mail.python.org. ;AUTHORITY python.org. 1050 IN NS ns-2046.awsdns-63.co.uk. python.org. 1050 IN NS ns-484.awsdns-60.com. python.org. 1050 IN NS ns-981.awsdns-58.net. python.org. 1050 IN NS ns-1134.awsdns-13.org. ;ADDITIONAL ns-1134.awsdns-13.org. 69879 IN A 205.251.196.110 ns-1134.awsdns-13.org. 68501 IN AAAA 2600:9000:5304:6e00::1 ns-484.awsdns-60.com. 50041 IN A 205.251.193.228 ns-484.awsdns-60.com. 50002 IN AAAA 2600:9000:5301:e400::1 ns-981.awsdns-58.net. 67318 IN A 205.251.195.213 ns-981.awsdns-58.net. 162880 IN AAAA 2600:9000:5303:d500::1 Servidores de nombre -------------------- id 26653 opcode QUERY rcode NOERROR flags QR RD RA ;QUESTION python.org. IN NS ;ANSWER python.org. 1050 IN NS ns-1134.awsdns-13.org. python.org. 1050 IN NS ns-2046.awsdns-63.co.uk. python.org. 1050 IN NS ns-484.awsdns-60.com. python.org. 1050 IN NS ns-981.awsdns-58.net. ;AUTHORITY ;ADDITIONAL ns-1134.awsdns-13.org. 69879 IN A 205.251.196.110 ns-1134.awsdns-13.org. 68501 IN AAAA 2600:9000:5304:6e00::1 ns-484.awsdns-60.com. 50041 IN A 205.251.193.228 ns-484.awsdns-60.com. 50002 IN AAAA 2600:9000:5301:e400::1 ns-981.awsdns-58.net. 67318 IN A 205.251.195.213 ns-981.awsdns-58.net. 162880 IN AAAA 2600:9000:5303:d500::1 Direcciones IPV4 -------------------- id 35266 opcode QUERY rcode NOERROR flags QR RD RA ;QUESTION python.org. IN A ;ANSWER python.org. 575142 IN A 45.55.99.72 ;AUTHORITY ;ADDITIONAL Registros DNS -------------------- id 1524 opcode QUERY rcode NOERROR flags QR RD RA ;QUESTION python.org. IN TXT ;ANSWER python.org. 3600 IN TXT "_globalsign-domain-verification=MK_ZKmss4D_DdzGOsssHxxBOK6hJc6LGycFvNOESdZ" python.org. 3600 IN TXT "google-site-verification=9852CbTRhQ51-9gCUayPbGYqJeBle_MXLb6E4AL_qQk" python.org. 3600 IN TXT "google-site-verification=QALZObrGl2OVG8lWUE40uVSMCAka316yADn9ZfCU5OA" python.org. 3600 IN TXT "google-site-verification=dqhMiMzpbkSyEhgjGKyEOMlEg2tF0MSHD7UN-MYfD-M" python.org. 3600 IN TXT "google-site-verification=w3b8mU3wU6cZ8uSrj3E_5f1frPejJskDpSp_nMWJ99o" python.org. 3600 IN TXT "status-page-domain-verification=9y2klhzbxsgk" python.org. 3600 IN TXT "v=spf1 mx a:mail.wooz.org ip4:188.166.95.178/32 ip6:2a03:b0c0:2:d0::71:1 include:stspg-customer.com include:_spf.google.com include:mailgun.org ~all" python.org. 3600 IN TXT "888acb5757da46ad83b7e341ec544c64" ;AUTHORITY python.org. 1049 IN NS ns-2046.awsdns-63.co.uk. python.org. 1049 IN NS ns-484.awsdns-60.com. python.org. 1049 IN NS ns-1134.awsdns-13.org. python.org. 1049 IN NS ns-981.awsdns-58.net. ;ADDITIONAL ns-1134.awsdns-13.org. 69878 IN A 205.251.196.110 ns-1134.awsdns-13.org. 68500 IN AAAA 2600:9000:5304:6e00::1 ns-484.awsdns-60.com. 50040 IN A 205.251.193.228 ns-484.awsdns-60.com. 50001 IN AAAA 2600:9000:5301:e400::1 ns-981.awsdns-58.net. 67317 IN A 205.251.195.213 ns-981.awsdns-58.net. 162879 IN AAAA 2600:9000:5303:d500::1
Añadiendo tratamiento de excepciones a la consulta de registros DNS
En este ejemplo práctico utilizaremos dnspython para ejecutar las consultas del ejemplo anterior, con la diferencia de que en este caso estamos realizando un tratamiento y en caso de que no se pueda obtener información para un tipo de registro concreto, informar al usuario con un mensaje de error.
Puede encontrar el siguiente código en el archivo registros_dns_excepciones.py:
#!/usr/bin/env python import argparse import dns.zone import dns.resolver import socket def main(dominio): # Regstros DNS IPv4 try: respuestaIPV4 = dns.resolver.query(dominio, 'A') for i in range(0, len(respuestaIPV4)): print("IPV4: ", respuestaIPV4[i]) except dns.resolver.NoAnswer as error: print("Error al obtener los registros IPv4:", error) # Regstros DNS IPv6 try: respuestaIPV6 = dns.resolver.query(dominio, 'AAAA') for i in range(0, len(respuestaIPV6)): print("IPV6: ", respuestaIPV6[i]) except dns.resolver.NoAnswer as error: print("Error al obtener los registros IPv6:", error) # Registros de correo MX (Mail Exchanger) try: mx = dns.resolver.query(dominio, 'MX') for i in range(0, len(mx)): print("MX: ", mx[i]) except dns.resolver.NoAnswer as error: print("Error al obtener los registros MX:", error) # Registros de CNAME try: cname_answer = dns.resolver.query(dominio, 'CNAME') print("CNAME: ", cname_answer) except dns.resolver.NoAnswer as error: print('Error al obtener los registros CNAME', error) # Registros de NS try: ns_answer = dns.resolver.query(dominio, 'NS') print(ns_answer) except dns.resolver.NoAnswer as error: print("Error al obtener los registros NS:", error) if __name__ == '__main__': parser = argparse.ArgumentParser(description='DNS Python') parser.add_argument('-dominio', action="store", dest="dominio", default='python.org') given_args = parser.parse_args() dominio = given_args.dominio main(dominio)
En la salida el anterior script podemos ver los diferentes mensajes de error en caso de poder obtener información sobre los registros:
IPV4: 45.55.99.72 Error al obtener los registros IPv6: The DNS response does not contain an answer to the question: python.org. IN AAAA MX: 50 mail.python.org. Error al obtener los registros CNAME The DNS response does not contain an answer to the question: python.org. IN CNAME <dns.resolver.Answer object at 0x7f5981d4a2e0>
Actividad práctica: Completar el código para la extracción de información de un servidor DNS mediante el módulo DNSPython
Completar el código para la extracción de información de un servidor DNS mediante el módulo DNSPython. Sustituir las xxx por variables y métodos que permitan completar la funcionalidad de obtener información sobre los diferentes registros DNS.
Para ello hacemos uso del módulo dnspython y el submódulo dns.resolver para realizar la consulta a los diferentes registros para obtener información relativa a direcciones ip y nombres de servidores de nombres.
import dns.resolver def main(xxx): registros = ['A','AAAA','NS','SOA','MX','MF','MD','TXT'] for registro in xxx: try: respuestas = dns.xxx.xxx(xxx, xxx) print("Respuestas del registro ",xxx) print("-----------------------------------") for respuesta in xxx: print(xxx) except: print("No pude resolver la consulta para el registro ",xxx) if __name__ == '__main__': try: main('google.com') except KeyboardInterrupt: exit()
Solución
import dns.resolver def main(dominio): registros = ['A','AAAA','NS','SOA','MX','MF','MD','TXT'] for registro in registros: try: respuestas = dns.resolver.query(dominio, registro) print("Respuestas del registro ",registro) print("-----------------------------------") for respuesta in respuestas: print(respuesta) except: print("No pude resolver la consulta para el registro ",registro) if __name__ == '__main__': try: main('google.com') except KeyboardInterrupt: exit()
Otras operaciones con el módulo dnspython
Con el módulo dnspython podemos comprobar si un dominio es subdominio de otro a través del método is_subdomain():
>>> import dns.resolver >>> dominio1 = dns.name.from_text('dominio1') >>> dominio2 = dns.name.from_text('dominio2') >>> dominio1.is_subdomain(dominio2)
En el siguiente script utilizamos el método anterior para comprobar si un dominio es subdominio o superdominio de otros las direcciones ip a partir de una lista de dominios.
Puede encontrar el siguiente código en el archivo comprobar_dominios.py:
#!/usr/bin/env python import argparse import dns.name def main(dominio1, dominio2): dominio1 = dns.name.from_text(dominio1) dominio2 = dns.name.from_text(dominio2) print("dominio1 is subdomain of dominio2: ", dominio1.is_subdomain(dominio2)) print("dominio1 is superdomain of dominio2: ", dominio1.is_superdomain(dominio2)) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Comprobar 2 dominios con dns Python') parser.add_argument('--dominio1', action="store", dest="dominio1", default='www.python.org') parser.add_argument('--dominio2', action="store", dest="dominio2", default='python.org') given_args = parser.parse_args() dominio1 = given_args.dominio1 dominio2 = given_args.dominio2 main (dominio1, dominio2)
Obtener un nombre de dominio a partir de una dirección IP:
>>> import dns.reversename >>> dominio = dns.reversename.from_address('direccion_ip')
Obtener una dirección IP a partir de un nombre de dominio:
>>> import dns.reversename >>> ip = dns.reversename.to_address('dominio')
Actividad práctica: Completar el código para la obtención de las direcciones ip a partir de una lista de dominios
Completar el código para la obtención de las direcciones ip a partir de una lista de dominios. Sustituir las xxx por variables y métodos que permitan completar la funcionalidad para realizar esta consulta.
import dns.xxx dominios = ["google.com", "microsoft.com", "python.org"] for dominio in xxx: print('Direcciones ip del dominio',xxx) #utilizamos el registro A para obtener direcciones ip direcciones = xxx.xxx.xxx(xxx, xxx) for direccion_ip in xxx: print(xxx)
Solución
import dns.resolver dominios = ["google.com", "microsoft.com", "python.org"] for dominio in dominios : print('Direcciones ip del dominio',dominio ) #utilizamos el registro A para obtener direcciones ip direcciones = dns.resolver.query(dominio, "A") for direccion_ip in direcciones : print(direccion_ip )
Búsqueda inversa
Si desea realizar una búsqueda inversa, debe usar el submódulo dns.reversename, como se muestra en el siguiente ejemplo:
>>> import dns.reversename >>> name = dns.reversename.from_address("ip_address") >>> print(dns.reversename.to_address(name))
En el siguiente script utilizamos el método anterior para obtener el nombre dns a partir de la ip y viceversa.
Puede encontrar el siguiente código en el archivo busqueda_inversa.py:
import dns.reversename nombre = dns.reversename.from_address("8.8.8.8") print(nombre) print(dns.reversename.to_address(nombre))
Servicios DNS
Robtex se trata de un servicio considerado la navaja suiza de internet.
Permite obtener, sin dejar rastro alguno en el objetivo, consultas sobre los dominios, subdominios, servidores DNS.
Este tipo de consultas suelen categorizarse como footprinting activo, aunque, al realizarlo a través de este servicio, en realidad se lleva a cabo de manera pasiva.
Robtex utiliza varias fuentes para recopilar información pública sobre números de IP, nombres de dominio, nombres de host, rutas, etc. Posteriormente indexa los datos en una base de datos y proporciona acceso gratuito a los mismos. El objetivo es conseguir la herramienta de búsqueda de DNS gratuita más rápida y completa en Internet.
Robtex proporciona una gran cantidad de información sobre un dominio. Por ejemplo puede ver aquellos dominios relacionados.
Disponemos también de una API que devuelve los datos de geolocalización y de red de una dirección ip.
https://freeapi.robtex.com/ipquery/<dirección_ip>
Ejemplo de consulta:
https://freeapi.robtex.com/ipquery/8.8.8.8
{"status":"ok","act":[{"o":"dns.google","t":1643550746}],"acth":[{"o":"google-public-dns-a.google.com","t":1643550747}],"pas":[{"o":"easymacao.com","t":1502146206},{"o":"73ldvunq.com","t":1501351840},{"o":"opon.com.cn","t":1501352406},{"o":"dc-20363e99abf1.skgrader.tk","t":1497273068},{"o":"aqb029.com","t":1555678182},{"o":"texwff.net","t":1527997086},{"o":"phreeek.de","t":1506111284},{"o":"itnovo.com","t":1491719020},{"o":"n23999.com","t":1576462182},{"o":"vns66jj.com","t":1505282792}],"pash":[{"o":"beautifulagony.info","t":1498868389},{"o":"easymacao.com","t":1613254599},{"o":"73ldvunq.com","t":1589141225},{"o":"nugraha.com","t":1521410905},{"o":"bzly.gov.cn","t":1488104799},{"o":"xn--cnq17rc0ni4a.cn","t":1506096329},{"o":"meumlaboratorium.net","t":1581563249},{"o":"craftbeerhq.com","t":1497272993},{"o":"50661.red","t":1505943755},{"o":"flowerpowermosquito.com","t":1493294473}],"city":"Mountain View","country":"United States","as":15169,"asname":"Google Google, Inc","asdesc":"NeuStar NeuStar, Inc","whoisdesc":"Google LLC (GOGL)","routedesc":"SP_BEEKSFX","bgproute":"8.8.8.0/24"}
Podríamos utilizar el servicio de dns-lookup para obtener más información sobre un dominio: https://www.robtex.com/dns-lookup/
¿Qué tipos de información proporciona Robtex?
- Búsqueda de DNS inversa. Permite buscar un número de IP para averiguar qué nombres de host lo apuntan. Los registros DNS inversos funcionan no solo para la dirección IP, sino también para los registros MX (servidor de correo) y los registros NS (servidor de nombres).
- Buscar un número de IP y obtener qué nombres de host lo apuntan. Los registros DNS inversos funcionan no solo para la dirección IP, sino también para los registros MX (servidor de correo) y los registros NS (servidor de nombres).
- Información Whois. Permite realizar búsquedas para un dominio registrado en varias bases de datos de whois. En esta base de datos es posible encontrar información de contacto del registro de dominio junto con la fecha de registro y la fecha de vencimiento. Se trata de un protocolo TCP que permite realizar consultas a bases de datos. Entre los principales datos que se pueden obtener podemos destacar el propietario del dominio, la dirección ip, direcciones de correo, fechas de creación y actualización de dominios.
DNSLookup
Un dominio tiene una cantidad de registros asociados, se puede consultar un servidor DNS para determinar la dirección IP del dominio principal (registro A), servidores de correo (registros MX), servidores DNS (servidores de nombres NS) y otros elementos como registros TXT.
El comando más común que se utiliza es nslookup que está disponible en muchos sistemas operativos, incluidos Windows y la mayoría de las distribuciones de Linux. Otra herramienta que se encuentra en sistemas basados en Linux es la herramienta dig. En general, esta es una herramienta más avanzada que tiene una serie de características que nslookup no posee.</p>
Los siguientes servicios web permiten realizar una consulta del tipo dns-lookup:
FAQ
¿Qué es Shodan?
Shodan corresponde al acrónimo de Sentient Hyper Optimized Data Access Network. A diferencia de los motores de búsqueda tradicionales que rastrean el sitio web para mostrar los resultados, Shodan ayuda a encontrar los servicios vulnerables en un servidor web.
Shodan es un motor de búsqueda para encontrar dispositivos específicos que funciona escaneando todo Internet y analizando los banners que devuelven los dispositivos. Con esa información, Shodan puede decirle cosas como qué servidor web es más popular, o cuántos servidores FTP anónimos existen en una ubicación determinada.
Funciona escaneando todo Internet y analizando los banners que devuelven los dispositivos. Utilizando esa información, Shodan puede devolver datos como qué servidor web (y versión) es más popular, o cuántos servidores FTP anónimos existen en una ubicación determinada.
Es de particular utilidad para la investigación de seguridad en Internet de las cosas, ya que actualmente podemos encontrar miles de millones de dispositivos conectados a Internet que tienen vulnerabilidades específicas que deben corregirse, y pueden identificarse rápidamente mediante su información de banner.
