Python aplicado a seguridad y redes - José Manuel Ortega Candel - E-Book

Python aplicado a seguridad y redes E-Book

José Manuel Ortega Candel

0,0
24,99 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.
Mehr erfahren.
Beschreibung

Descubra el poder de Python en la seguridad informática y la administración de redes Si ya posee unos conocimientos previos de programación, principalmente de Python, y quiere ir más allá en la seguridad informática y redes de ordenadores, ha llegado al libro indicado. A través de una exploración detallada y práctica, Python aplicado a seguridad y redes explora cómo la programación en Python puede transformar y mejorar la gestión de la seguridad y las redes. Gracias a su lectura, aprenderá acerca de la automatización de tareas y de la implementación de herramientas de detección y defensa. También será capaz de: - Utilizar Python para automatizar tareas de seguridad y administración de redes. - Explorar el uso de bibliotecas populares como Scapy, Nmap y Socket para desarrollar herramientas de análisis de tráfico, escaneo de puertos y detección de vulnerabilidades. - Implementar técnicas de análisis forense y auditorías de seguridad utilizando Python. - Dominar la creación de scripts para interactuar con API y servicios web, así como para desarrollar aplicaciones de seguridad customizadas. Asimismo, con este libro disfrutará de proyectos y ejemplos prácticos para consolidar los conceptos aprendidos y fomentar la experimentación. Podrá descargar el material adicional gratuito con el código de la primera página desde www.marcombo.info. Este libro ha sido diseñado para estudiantes, profesionales de la seguridad informática, administradores de redes e interesados en aprender cómo aplicar Python en el contexto de la seguridad y las comunicaciones en red. Con él, estará equipado para enfrentar los desafíos del mundo digital de hoy y aprovechará la flexibilidad y el poder de Python para proteger y optimizar sus sistemas de redes. No deje escapar la oportunidad de aprender sobre este tema, de la mano de todo un profesional del ámbito de la programación. Seguro que no le defraudará.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 359

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Python aplicado a seguridad y redes

Módulos y herramientas para proteger sus redes y aplicaciones

José Manuel Ortega Candel

Acceda a www.marcombo.info

para descargar gratis

el contenido adicional

complemento imprescindible de este libro

Código: PYTHON65

Python aplicado a seguridad y redes

Módulos y herramientas para proteger sus redes y aplicaciones

José Manuel Ortega Candel

 

 

Python aplicado a seguridad y redes

© 2024 José Manuel Ortega Candel

Primera edición, 2024

© 2024 MARCOMBO, S. L.

www.marcombo.com

Ilustración de cubierta: Jotaká

Corrección: F. Xavier Timoneda

Directora de producción: M.a Rosa Castillo

Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra.

ISBN del libro en papel: 978-84-267-3821-9

ISBN del libro electrónico: 978-84-267-3861-5

Producción del ePub: booqlab

Dedicado a los lectores, cuya curiosidad y pasiónpor el conocimiento hacen que la escritura cobre vida.

Antes de comenzar a leer este libro

En este libro se utiliza la tipografía Calibri en los casos en los que se hace referencia a código o acciones por realizar en el ordenador, ya sea en un ejemplo o cuando se refiere a alguna función mencionada en el texto. También se usa para indicar menús de programas, teclas, URL, grupos de noticias o direcciones de correos electrónicos.

Los términos y definiciones que se utilizan mayormente en lengua inglesa se mantienen en este libro en dicho idioma, y en cursiva.

El código fuente de los ejemplos, así como todos los recursos didácticos y de programación que se utilizan en este libro, podrán descargarse a medida que se avanza en la lectura.

Estos recursos están disponibles en www.marcombo.info con el código PYTHON65.

Contenido

Introducción

CAPÍTULO 1

Trabajando con sockets en Python

1.1. Introducción a Python para proyectos de seguridad

1.2. Introducción a los sockets

1.2.1. Sockets de red en Python

1.2.2. Módulo socket en Python

1.3. Recopilación de información con sockets

1.3.1. Obtener información de un servidor y dirección IP

1.4. Implementar un escáner de puertos con sockets

1.4.1. Escáner de puertos con sockets

1.4.2. Escáner de puertos avanzado

1.4.3. Escáner de puertos a partir de un dominio

1.5. Implementar en Python un servidor HTTP

1.5.1. Implementación del servidor

1.5.2. Implementación del cliente

1.6. Aplicaciones clientes-servidor con sockets en Python

1.6.1. Implementación del cliente

1.6.2. Administrar excepciones de socket

1.6.3. Creando un cliente y un servidor TCP con sockets

1.6.4. Método para aceptar conexiones

1.6.5. Enviar y recibir datos del socket

1.6.6. Implementando el servidor TCP

1.6.7. Implementando el cliente TCP

1.7. Shell inversa con sockets

1.8. Conclusiones

CAPÍTULO 2

Módulos para realizar peticiones con Python

2.1. Introducción

2.2. Protocolo HTTP y creación de clientes HTTP en Python

2.2.1. Introducción al protocolo HTTP

2.2.2. Módulo http.client

2.3. Construyendo un cliente HTTP con urllib.request

2.3.1. Usando el método urlopen()

2.3.2. Objeto respuesta y códigos de estado

2.3.3. Comprobación de cabeceras HTTP con urllib.request

2.3.4. Personalización de cabeceras HTTP con urllib.request

2.3.5. Obtener correos electrónicos y enlaces de una URL

2.3.6. Obtener imágenes de una URL con urllib.request

2.4. Construyendo un cliente HTTP con requests

2.4.1. Introducción al módulo requests

2.4.2. Obtener número de palabras de un fichero

2.4.3. Obtener cabeceras con el módulo requests

2.4.4. Realizar peticiones GET a una API REST

2.4.5. Realizar peticiones POST a una API REST

2.4.6. Realizar peticiones mediante un proxy

2.4.7. Gestionar excepciones con el módulo requests

2.5. Conclusiones

CAPÍTULO 3

Recolección de información de servidores con Python

3.1. Introducción

3.2. Utilizando Shodan para la obtención de información

3.2.1. Filtros en Shodan

3.3. Utilizando Python para realizar búsquedas en Shodan

3.3.1. Acceso a Shodan desde Python

3.3.2. Búsquedas de Shodan en Python

3.3.3. Obtención de información de un servidor FTP

3.4. Utilizando el protocolo WHOIS para obtener información de un servidor

3.4.1. Servicio WHOIS

3.4.2. Consultas al servicio WHOAPI.com

3.4.3. Consultas con el módulo Python-whois

3.4.4. Consultas con el módulo ipwhois

3.5. Extracción de información de servidores DNS

3.5.1. Servidores DNS

3.5.2. Módulo DNSPython

3.5.3. Otras operaciones con el módulo dnspython

3.6. Servicios DNS

3.7. Conclusiones

CAPÍTULO 4

Extracción de metadatos con Python

4.1. Introducción

4.2. Obtener información de geolocalización

4.3. Módulos de geolocalización en Python

4.3.1. Geolocalización con geoip2-python

4.3.2. Geolocalización con maxminddb-geolite2

4.3.3. Geolocalización con python-geoip-python3

4.4. Extracción de metadatos en documentos PDF

4.4.1. Obtención de metadatos con PdfReader

4.4.2. Extraer texto e imágenes de documentos PDF

4.5. Extracción de metadatos en imágenes

4.5.1. Extracción de metadatos con el módulo PIL.ExifTags

4.6. Conclusiones

CAPÍTULO 5

Web Scraping con Python

5.1. Introducción

5.2. Parsers XML y HTML

5.2.1. Extraer etiquetas de un sitio web

5.2.2. Extracción de documentos PDF con el módulo lxml

5.3. Extraer contenido y etiquetas con BeautifulSoup

5.3.1. Extraer nombres de dominio con BeautifulSoup

5.3.2. Extracción de contenido mediante expresiones regulares

5.3.3. Extracción de imágenes y enlaces con el módulo bs4

5.4. Web Scraping con Scrapy

5.4.1. Características de Scrapy

5.4.2. Arquitectura de Scrapy

5.4.3. Instalación y comandos de Scrapy

5.4.4. Extrayendo información mediante Scrapy Shell

5.4.5. Scrapy como framework de desarrollo de spyders

5.4.6. Fichero de configuración settings.py

5.4.7. Exportación de resultados en formatos json, csv, xml

5.5. Conclusiones

CAPÍTULO 6

Escaneo de puertos y redes con Python

6.1. Introducción

6.2. Nmap como herramienta de escáner de puertos

6.2.1. Tipos de escaneo con nmap

6.3. Escaneo de puertos con Python-nmap

6.3.1. Escaneo síncrono con Python-nmap

6.3.2. Guardar el resultado del escaneo en un fichero JSON

6.3.3. Usando PortScanner Yield

6.3.4. Usando nmap con otros módulos de Python

6.3.5. Escaneo asíncrono

6.4. Ejecutar scripts de nmap para detectar servicios y vulnerabilidades

6.4.1. Ejecutar scripts de nmap

6.4.2. Obtener subdominios con script de nmap

6.4.3. Analizar el servicio FTP con scripts de nmap

6.5. Obtener las máquinas activas de un segmento de red

6.6. Scanless

6.7. Conclusiones

CAPÍTULO 7

Conexión con servidores FTP, SFTP y SSH desde Python

7.1. Introducción

7.2. Conexiones con servidores FTP utilizando el módulo ftpLib

7.2.1. Conexiones con servidores FTP

7.2.2. Descarga de ficheros de servidores FTP

7.2.3. Comprobar conexión FTP anónima

7.2.4. Proceso de fuerza bruta para conectarnos con un servidor FTP

7.3. Conexión con servidores SSH utilizando paramiko

7.3.1. Ejecutar comandos con paramiko

7.3.2. Conexión con la clase Transport

7.3.3. Ejecutar comandos con la clase Transport

7.3.4. Obtener algoritmos de cifrado

7.3.5. Operaciones sobre archivos mediante el cliente SFTP

7.3.6. Descarga de ficheros con el cliente SFTP

7.4. Conexión con servidores SFTP utilizando PYSFTP

7.4.1. Descarga de ficheros utilizando PYSFTP

7.5. Conclusiones

CAPÍTULO 8

Análisis de vulnerabilidades en aplicaciones web con Python

8.1. Introducción

8.2. Introducción a la metodología OWASP

8.2.1. Inyección de comandos

8.2.2. SQL Injection

8.2.3. Cross-Site Scripting (XSS)

8.3. Scripts en Python para detectar vulnerabilidades en sitios web

8.3.1. Script en Python para detectar SQL Injection

8.3.2. Script en Python para detectar Cross-Site Scripting XSS

8.4. Introducción a la herramienta SQLmap para detectar vulnerabilidades del tipo SQL Injection

8.4.1. Ejecutar SQLmap sobre un dominio vulnerable

8.4.2. Extracción de tablas y columnas de una base de datos

8.4.3. Acceder a información de una tabla

8.5. Introducción a la herramienta Bandit para detectar vulnerabilidades en proyectos de Python

8.5.1. Instalar y ejecutar Bandit

8.5.2. Análisis de vulnerabilidades con Bandit

8.5.3. Plug-ins de Bandit para el análisis de código estático

8.5.4. Plugin SQL Injection

8.6. Otras herramientas de análisis y detección de vulnerabilidades en Python

8.6.1. Safety

8.6.2. Ejemplo de código para detectar XSS

8.6.3. Escáner de vulnerabilidades XSS para Python

8.7. Conclusiones

CAPÍTULO 9

Análisis del tráfico de red y rastreo de paquetes con scapy

9.1. Introducción

9.2. Captura e inyección de paquetes con scapy

9.3. Envío y recepción de paquetes con scapy

9.3.1. Enviar y recibir paquetes con scapy

9.4. Descubrimiento de redes con scapy

9.4.1. Escaneo de puertos con scapy

9.4.2. Comando traceroute con scapy

9.5. Lectura de ficheros PCAP con scapy

9.6. Rastreo de paquetes con scapy

9.7. Conclusiones

CAPÍTULO 10

Recopilación de información con herramientas OSINT

10.1. Introducción

10.2. Introducción a la Inteligencia de Fuentes Abiertas (OSINT)

10.2.1. Google Dorks y la base de datos de Google Hacking

10.2.2. Maltego

10.2.3. Photon

10.2.4. The Harvester

10.2.5. Censys

10.2.6. crt.sh

10.2.7. DnsDumpster

10.2.8. Web Check

10.2.9. WaybackMachine

10.2.10. OSINT framework

10.2.11. El motor de búsqueda Shodan

10.2.12. El motor de búsqueda BinaryEdge

10.3. Obtener información con Google Dorks

10.3.1. Google Dorks

10.3.2. Katana: una herramienta Python para Google Hacking

10.3.3. Dorks Hunter

10.4. Obtener información con SpiderFoot

10.4.1. Módulos de SpiderFoot

10.5. Obtener información sobre servidores DNS con DNSPython y DNSRecon

10.5.1. Protocolo DNS

10.5.2. Módulo DNSPython

10.5.3. DNSRecon

10.6. Obtención de servidores vulnerables con fuzzing

10.6.1. El proceso de fuzzing

10.6.2. Webfuzzing

10.6.3. Introducción del proyecto FuzzDB

10.6.4. Identificación de páginas de inicio de sesión predecibles con el proyecto FuzzDB

10.6.5. Identificación de inyección SQL con el proyecto FuzzDB

10.7. Conclusiones

CAPÍTULO 11

Criptografía y ofuscación de código

11.1. Introducción

11.2. Introducción a la criptografía

11.3. Cifrar y descifrar información con pycryptodome

11.3.1. Cifrar y descifrar con el algoritmo DES

11.3.2. Cifrar y descifrar con el algoritmo AES

11.3.3. Cifrado de archivos con el algoritmo AES

11.3.4. Generación de firmas RSA con pycryptodome

11.4. Cifrar y descifrar información con cryptography

11.4.1. Cifrado simétrico con el paquete fernet

11.5. Generación segura de claves con los módulos secrets y hashlib

11.5.1. Generar claves de forma segura con el módulo hashlib

11.5.2. Comprobar la integridad de un fichero con el módulo hashlib

11.6. Herramientas de Python para la ofuscación de código

11.6.1. Ofuscación de código con pyarmor

11.7. Conclusiones

Glosario

Introducción

Python es un lenguaje interpretado muy utilizado como herramienta de pentesting, sobre todo para la creación de herramientas que permiten recolectar información y detectar fallos de seguridad en aplicaciones web. Desde un enfoque teórico-práctico, estudiaremos Python como lenguaje orientado para investigadores de seguridad interesados en la parte defensiva y ofensiva, y se utiliza para proyectos, incluyendo programación web, herramientas de seguridad, scripting y automatización de tareas.

El objetivo del libro es capacitar a aquellos interesados en ampliar los conocimientos sobre Python y librerías y módulos de los que dispone para realizar tareas relacionadas con realizar peticiones, obtener información y conectarse con servidores o testear la seguridad de un sitio web.

El objetivo sería aprender a utilizar Python como lenguaje de programación no solo para poder construir programas sino también para automatizar y especificar muchas de las tareas que se realizan durante un proceso de pentesting. Entre los principales objetivos de aprendizaje que trata el libro, podemos destacar:

• Aprender a crear scripts en Python con el objetivo de automatizar tareas de pentesting.

• Aprender las principales librerías disponibles en Python a la hora de desarrollar herramientas enfocadas a la seguridad.

• Aprender una metodología que permita escribir código en Python para realizar un proceso de pentesting.

• Aprender a desarrollar mediante programación en Python sus propias herramientas, que se utilizan en un proceso de Ethical Hacking.

• Aprender a automatizar tareas de análisis y extracción de información de servidores.

• Fomentar el interés por la investigación y la seguridad informática.

El libro está destinado a aquellos desarrolladores con conocimientos básicos en el lenguaje de programación Python. Es recomendable que el lector tenga unas bases del lenguaje y unos conocimientos básicos de programación orientada a objetos, estructuras de datos y manejo de ficheros.

El libro trata de seguir un enfoque teórico-práctico, con el objetivo de afianzar los conocimientos mediante la creación y ejecución de scripts desde la consola de Python. Además, se provee un repositorio donde se pueden encontrar los ejemplos que se analizan a lo largo del libro para facilitar al lector las pruebas y la asimilación de los contenidos teóricos.

A lo largo del libro, el lector encontrará ejemplos de código fuente en el lenguaje de programación Python. Los scripts y proyectos implementados se pueden encontrar en el material adicional organizados por capítulos:

•www.marcombo.info

• Código: PYTHON65

Se recomienda al lector seguir los ejemplos prácticos del libro, junto con el código fuente, para practicar los conceptos que se analizan teóricamente. Todos los scripts se pueden probar con versiones recientes de Python. En este punto se recomienda al lector probar los scripts con una versión de Python >= 3.10. En la página https://www.python.org/downloads se pueden descargar diferentes versiones, incluida la última.

CAPÍTULO 1

TRABAJANDO CON SOCKETS EN PYTHON

1.1. Introducción a Python para proyectos de seguridad

Python es un lenguaje de programación que se creó a principios de los años noventa por parte de Guido Van Rossum. Entre las cualidades más particulares del lenguaje destacan que cuenta con una sintaxis muy limpia, es potente, dinámico y fácil de aprender. Con los años, Python se convirtió en un lenguaje muy adoptado por la industria de la seguridad informática, por su simpleza, su practicidad y un lenguaje interpretado y de scripting.

Entre las principales características que proporciona el lenguaje, podemos destacar:

• Lenguaje multiplataforma y open source.

• Lenguaje sencillo, rápido, robusto y potente.

• Muchas de las librerías, módulos y proyectos enfocados a la seguridad informática se encuentran escritos en Python.

• Hay mucha documentación y una comunidad de usuarios muy grande.

• Es un lenguaje diseñado para realizar programas robustos con pocas líneas de código, algo que en otros lenguajes solo es posible después de incluir muchas características propias de cada lenguaje.

• Es ideal para realizar prototipos y pruebas de concepto (PoC) rápidas.

Python puede convertirse en una herramienta práctica para evitar tener que realizar tareas repetitivas como detectar vulnerabilidades, análisis de puertos y otras listas de tareas que iremos viendo a medida que avancemos.

1.2. Introducción a los sockets

Los sockets son el componente principal que nos permite aprovechar las capacidades del sistema operativo para interactuar con la red. Podemos pensar en los sockets como un canal de comunicación punto a punto entre un cliente y un servidor. Los sockets de red son una manera fácil de establecer una comunicación entre procesos que están en la misma máquina o en máquinas distintas.

El concepto de un socket es muy similar al de los descriptores de archivos UNIX. Los comandos como read() y write() que nos permiten trabajar con el sistema de archivos funcionan de manera similar a los sockets. Una dirección de socket de red consta de una dirección IP y un número de puerto. El objetivo de un socket es comunicar procesos a través de la red.

1.2.1.Sockets de red en Python

La comunicación entre distintas entidades en una red se basa en el clásico concepto de sockets. Los sockets son un concepto abstracto con el que se designa el punto final de una conexión. Los programas utilizan sockets para comunicarse con otros programas, que pueden estar situados en computadoras distintas.

Un socket queda definido por la dirección IP de la máquina, el puerto en el que escucha y el protocolo que utiliza. Los tipos y funciones necesarios para trabajar con sockets se encuentran en Python en el módulo socket. Los sockets se clasifican en sockets de flujo TCP (socket.SOCK_STREAM) o sockets de datagramas UDP (socket.SOCK_DGRAM), dependiendo de si el servicio utiliza TCP, que es orientado a conexión y fiable, o UDP, respectivamente. En este capítulo nos centraremos en los sockets orientados a conexión TCP, que cubren un 90 % de las necesidades comunes.

Para crear un socket se utiliza el constructor socket.socket(), que puede tomar como parámetros opcionales la familia, el tipo y el protocolo. Por defecto se utilizan la familia AF_INET y el tipo SOCK_STREAM. La sintaxis general del método de socket es la siguiente:

Estos argumentos representan las familias de direcciones y el protocolo de la capa de transporte. Dependiendo del tipo de socket, los sockets se clasifican en sockets de flujo (socket.SOCK_STREAM) y sockets de datagramas (socket.SOCK_DGRAM), en función de si el servicio utiliza TCP o UDP. socket.SOCK_DGRAM se usa para comunicaciones UDP, y socket.SOCK_STREAM, para conexiones TCP.

Los sockets también se pueden clasificar según la familia. Tenemos sockets UNIX (socket.AF_UNIX), que se crearon antes de la concepción de las redes y se basan en ficheros, sockets socket.AF_INET, que son los que nos interesan, sockets socket.AF_INET6 para IPv6, etc. En la siguiente salida del comando help(socket) vemos el constructor de la clase socket con la mención de los diferentes tipos de sockets:

1.2.2. Módulo socket en Python

Los tipos y funciones necesarios para trabajar con sockets se pueden encontrar en Python en el módulo de sockets. El módulo de socket expone todas las piezas necesarias para escribir rápidamente clientes y servidores TCP y UDP.

El módulo de socket tiene casi todo lo que necesita para construir un servidor o cliente de socket. En el caso de Python, el socket devuelve un objeto al que se le pueden aplicar los métodos de socket. Este módulo viene instalado por defecto cuando instala la distribución Python. Para verificarlo, podemos hacerlo desde el intérprete de Python.

Python 3.10.5 (main, Jun 6 2022, 18:49:26) [GCC 12.1.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import socket >>> dir(socket) ['AF_ALG', 'AF_APPLETALK', 'AF_ASH',... 'gethostbyaddr', 'gethostbyname', 'gethostbyname_ex', 'gethostname','getnameinfo', 'getprotobyname', 'getservbyname', 'getservbyport','has_dualstack_ipv6', 'has_ipv6', 'herror', 'htonl', 'htons', 'if_indextoname','if_nameindex', 'if_nametoindex', 'inet_aton', 'inet_ntoa', 'inet_ntop','inet_pton', 'io', 'ntohl', 'ntohs', 'os', 'recv_fds', 'selectors', 'send_fds','setdefaulttimeout', 'sethostname', 'socket', 'socketpair', 'sys', 'timeout'] >>> socket <module 'socket' from '/usr/lib/python3.10/socket.py'> </module><br/>

En la salida anterior vemos todas las constantes y métodos que tenemos disponibles en este módulo. Las constantes las vemos en primera instancia dentro de la estructura que ha devuelto el objeto. Entre las constantes más utilizadas podemos destacar:

•socket.AF_INET

•socket.SOCK_STREAM

Para abrir un socket en una determinada máquina utilizamos el constructor de la clase socket que acepta por parámetros la familia, el tipo de socket y el protocolo. Una llamada típica para construir un socket que funcione a nivel TCP es pasando como parámetros la familia y el tipo de socket:

1.3. Recopilación de información con sockets

Los métodos útiles para recopilar más información son:

•gethostbyaddr(dirección): nos permite obtener un nombre de dominio a partir de la dirección IP.

•gethostbyname(hostname): nos permite obtener una dirección IP a partir de un nombre de dominio.

Podemos obtener más información sobre estos métodos con el comando de help(socket):

Python 3.10.5 (main, Jun 6 2022, 18:49:26) [GCC 12.1.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import socket >>> help(socket)

Ahora vamos a detallar algunos métodos relacionados con el host, la dirección IP y la resolución del dominio. Para cada uno, mostraremos un ejemplo.

El método socket.gethostbyname(hostname) convierte un nombre de host al formato de dirección IPv4. La dirección IPv4 se devuelve en forma de cadena. Este método es equivalente al comando nslookup que podemos encontrar en muchos sistemas operativos:

>>> import socket >>> socket.gethostbyname('packtpub.com') '83.166.169.231' >>> socket.gethostbyname('google.com') '216.58.210.142'

El método socket.gethostbyname_ex(nombre) devuelve un conjunto de direcciones IP para un nombre de dominio, lo que significa que un dominio puede llevar asociadas múltiples direcciones IP.

>>> socket.gethostbyname_ex('packtpub.com') ('packtpub.com', [], ['83.166.169.231']) >>> socket.gethostbyname_ex('google.com') ('google.com', [], ['216.58.211.46'])

Otro de los métodos de los que disponemos en la clase sockets es el que permite obtener el nombre cualificado de un dominio:

>>> socket.getfqdn('google.com')

El método socket.gethostbyaddr(ip_address) devuelve una tupla con el formato (hostname, name, ip_address_list) donde hostname es el nombre de host que responde a la dirección IP dada, name es una lista de nombres asociados con la misma dirección IP, y address_list es una lista de direcciones IP para la misma interfaz de red en el mismo host.

>>> socket.gethostbyaddr('8.8.8.8') ('google-public-dns-a.google.com', [], ['8.8.8.8'])

El método socket.getservbyname(servicename[,nombre_protocolo]) nos permite obtener el número de puerto del nombre del puerto:

>>> import socket >>> socket.getservbyname('http') 80 >>> socket.getservbyname('smtp','tcp') 25

El método socket.getservbyport(puerto, [nombre_protocolo]) realiza la operación inversa de la anterior, lo que nos permite obtener el nombre del puerto a partir del número de puerto:

>>> socket.getservbyport(80) 'http' >>> socket.getservbyport(23) 'telnet'

1.3.1. Obtener información de un servidor y dirección IP

El script siguiente es un ejemplo de cómo podemos usar estos métodos para obtener información de los servidores de Google. Podemos encontrar el código siguiente en el archivo sockets_metodos.py:

# -*- encoding: utf-8 -*- import socket import sys try:   print("gethostbyname")   print(socket.gethostbyname_ex('www.google.es'))   print("\ngethostbyaddr")   print(socket.gethostbyaddr('216.58.211.228'))   print("\ngetfqdn")   print(socket.getfqdn('www.yahoo.es')) except socket.error as error:   print (str(error))   print ("Error de conexion")   sys.exit()

El ejemplo siguiente permite obtener el nombre de host a partir de la dirección IP. Para esta tarea, podemos usar la función gethostbyaddr(). En este script, obtenemos el nombre de host a partir de la dirección IP 8.8.8.8. Podemos encontrar el código siguiente en el archivo socket_reverse_lookup.py:

#!/usr/bin/env python # --*-- coding: UTF-8 --*-- import sys, socket try :        result=socket.gethostbyaddr("8.8.8.8")        print("The host name is:")        print(" "+result[0])        print("\nAddress:")        for item in result[2]:                print(" "+item) except socket.herror as e: print("error for resolving ip address:",e)

El ejemplo siguiente permite la resolución de dominios con el módulo socket, donde, dado un nombre de dominio introducido por el usuario, permite obtener información relacionada con dicho dominio, como dirección IP, host asociado y nombre cualificado del dominio. Podemos encontrar el código siguiente en el archivo resolver_dominio.py:

En la sección siguiente revisaremos cómo podemos implementar el escaneo de puertos con sockets y cómo podemos administrar las excepciones cuando trabajamos con sockets.

1.4. Implementar un escáner de puertos con sockets

Los sockets son el bloque de construcción fundamental para las comunicaciones de red, y de manera fácil podemos verificar si un puerto específico está abierto, cerrado o filtrado al llamar al método connect_ex().

El método socket.connect_ex(dirección,puerto) se suele usar para implementar un escáner de puertos con sockets. El script siguiente muestra los puertos que están abiertos en la máquina localhost con la interfaz de dirección IP loopback de 127.0.0.1. Podemos encontrar el código siguiente en el archivo socket_ports_open.py:

1.4.1. Escáner de puertos con sockets

Para implementar un escáner de puertos en Python necesitamos importar los módulos socket y sys. El módulo sys lo utilizamos para salir del programa con la instrucción sys.exit() y devolver el control al intérprete en caso de error de conexión.

Podemos tener una función que acepte por parámetros una dirección IP y una lista de puertos y devuelva para cada puerto si está abierto o cerrado. Podemos encontrar el código siguiente en el archivo comprobarListaPuertos.py:

Si ejecutamos la función comprobarListaPuertos(ip,portlist) desde nuestro programa principal, vemos cómo realiza la comprobación para cada uno de los puertos y nos devuelve si está abierto o cerrado para una dirección IP determinada. El primer parámetro podría ser una dirección IP y un dominio, ya que el módulo puede resolver un nombre a partir de una dirección IP y viceversa. La forma de ejecutar esta función es mediante la llamada:

comprobarListaPuertos(dominio|direccion_ip,[lista_puertos])

Si ejecutamos la función con una dirección IP o un nombre de dominio que no exista, nos devolverá un error de conexión junto con la excepción que ha devuelto el módulo socket al no poder resolver la dirección IP.

comprobarListaPuertos("local",[80,8080,443]) [Errno 11004] getaddrinfo failed.Error de conexion

La parte más importante de esta función la encontramos cuando comprueba si el puerto está abierto o cerrado:

En el código anterior vemos cómo utilizamos el método settimeout() para indicarle un tiempo de intento de conexión en segundos.

1.4.2. Escáner de puertos avanzado

El ejemplo siguiente nos permitirá escanear un host local o remoto en busca de puertos abiertos. El programa busca puertos seleccionados a partir de una determinada dirección IP introducida por el usuario y obtiene los puertos abiertos para mostrarlos al usuario. Si el puerto está cerrado, también muestra información sobre el motivo, por ejemplo por timeout de la conexión. Podemos encontrar el código siguiente en el archivo socket_scanner_puertos.py.

El script comienza con información relacionada con la dirección IP y los puertos introducidos por el usuario. En la ejecución del script anterior, podemos ver los puertos que están abiertos y el tiempo en segundos para completar el escaneo de puertos:

$ python socket_scaner_puertos.py Introduce IP : 127.0.0.1 Introduce puerto de inicio : 80 Introduce puerto de fin : 90 Escaneando IP 127.0.0.1 : Probando puerto 80 ... El puerto 80 está abierto Probando puerto 81 ... Probando puerto 82 ... Probando puerto 83 ... Probando puerto 84 ... Probando puerto 85 ... Probando puerto 86 ... Probando puerto 87 ... Probando puerto 88 ... Probando puerto 89 ... Probando puerto 90 ... Escaneo finalizado!

1.4.3. Escáner de puertos a partir de un dominio

El siguiente script de Python nos permitirá escanear una dirección IP con las funciones portScanning y socketScan. El programa busca puertos seleccionados en un dominio específico resuelto a partir de la dirección IP introducida por el usuario como argumento en el programa. Podemos encontrar el código siguiente en el archivo socket_portScan.py

Este podría ser nuestro programa principal, donde obtenemos los parámetros obligatorios de host y puertos para la ejecución del script. Una vez que hayamos obtenido estos parámetros, llamaremos a la función portScanning, que resolverá la dirección IP y el nombre de host, y llamaremos a la función socketScan, que usará el módulo de socket para determinar el estado del puerto:

Si ejecutamos el script anterior con la opción -h, podemos ver los argumentos que son obligatorios, como el host y una lista de puertos, separados por coma.

$ python socket_portScan.py -h

En la ejecución del script anterior, podemos ver el estado de los puertos seleccionados para el dominio indicado:

$ python socket_portScan.py -H www.google.es -P 80,21,22,23

1.5. Implementar en Python un servidor HTTP

Podríamos crear un socket del tipo TCP y vincularlo a un puerto para aceptar conexiones desde la misma máquina. El puerto podría ser 80, pero como necesita privilegios de root, usaremos uno mayor o igual que 8080. Estos son los principales métodos que podemos usar desde el punto de vista del servidor:

•socket.bind(dirección, puerto): Este método nos permite conectar la dirección con el socket en el puerto indicado, con el requisito de que el socket debe estar abierto antes de establecer la conexión con esta dirección.

•socket.listen(numero_conexiones): Este método acepta como parámetro el número máximo de conexiones de los clientes e inicia la escucha TCP para las conexiones entrantes.

•socket.accept(): Este método nos permite aceptar conexiones del cliente. Este método devuelve dos valores: client_socket y la dirección del cliente. client_socket es un nuevo objeto de socket utilizado para enviar y recibir datos. Antes de usar este método, es importante llamar a los métodos socket.bind(dirección) desde la parte del cliente y socket.listen(numero_conexiones) desde la parte del servidor.

1.5.1. Implementación del servidor

De los métodos comentados anteriormente, podríamos utilizar el método socket.bind(direccion_ip,puerto), que acepta como parámetros la dirección IP y el puerto. El módulo socket proporciona el método listen(), que permite poner en cola hasta un máximo de n solicitudes. Por ejemplo, podríamos establecer en 5 el número máximo de peticiones con la instrucción mysocket.listen(5).

Posteriormente, podríamos establecer la lógica de nuestro servidor cada vez que recibe la petición de un cliente. Utilizamos el método accept() para aceptar conexiones por parte de los clientes, leer datos entrantes con el método recv() y responder una página HTML con el método send(). Podemos encontrar el código siguiente en el archivo servidor_http.py.

1.5.2. Implementación del cliente

Si queremos probar el funcionamiento del servidor HTTP, podríamos crearnos otro script que actuaría como cliente y nos permitiría obtener la respuesta enviada al servidor que hemos creado. Podemos encontrar el código siguiente en el archivo cliente_http.py.

Ejecución del servidor y del cliente:

$ python servidor_http.py Waiting for connections HTTP request received: b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n' Waiting for connections $ python cliente_http.py Contacting localhost on port 8080 ... Response from localhost: HTTP/1.1 200 OK <html><body><h1>Hello World!</h1></body></html>

1.6. Aplicaciones clientes-servidor con sockets en Python

En esta sección se presentan algunos conceptos más avanzados para trabajar con sockets, como es la creación de una aplicación cliente-servidor orientada al paso de mensajes.

Empezamos recordando los métodos que tenemos dentro del módulo sockets para crear aplicaciones cliente-servidor. Estos son los métodos de socket generales que podemos usar tanto en clientes como en servidores:

•socket.recv(buflen): este método recibe datos del socket. El argumento del método indica la cantidad máxima de datos que puede recibir.

•socket.recvfrom(buflen): este método recibe datos y la dirección del remitente.

•socket.recv_into(buffer): este método recibe datos en un búfer.

•socket.recvfrom_into(buffer): este método recibe datos en un búfer.

•socket.send(bytes): este método envía datos de bytes al destino especificado.

•socket.sendto(datos, dirección): este método envía datos a una dirección determinada.

•socket.sendall(datos): este método envía todos los datos al búfer.

•socket.close(): este método libera la memoria y finaliza la conexión.

En una arquitectura cliente-servidor, hay un servidor central que proporciona servicios a un conjunto de máquinas que se conectan. Estos son los principales métodos que podemos usar desde el punto de vista del servidor:

•socket.bind(dirección): este método nos permite conectar la dirección con el socket, con el requisito de que el socket debe estar abierto antes de establecer la conexión con la dirección.

•socket.listen(numero_conexiones): este método acepta como parámetro el número máximo de conexiones de los clientes e inicia la escucha TCP para las conexiones entrantes.

•socket.accept(): este método nos permite aceptar conexiones del cliente. Este método devuelve dos valores: client_socket y la dirección del cliente. client_socket es un nuevo objeto de socket utilizado para enviar y recibir datos. Antes de usar este método, debe llamar a los métodos socket.bind(dirección) y socket.listen(número de_conexiones).

Desde el punto de vista del cliente, este es el método de socket que podemos usar para conectarnos con el servidor:

•socket.connect(ip_address): este método conecta al cliente con la dirección IP del servidor.

•socket.connect_ex(ip_address): este método conecta al cliente con la dirección IP del servidor y ofrece la posibilidad de devolver un error.

1.6.1. Implementación del cliente

En este ejemplo, estamos probando cómo enviar y recibir datos de un sitio web. Una vez que se establece la conexión, podemos enviar y recibir datos. La comunicación con el socket se puede hacer muy fácilmente gracias a dos funciones, send() y recv(), que se utilizan para las comunicaciones TCP. Para la comunicación UDP, utilizamos las funciones sendto() y recvfrom() para enviar y recibir, respectivamente.

En el script siguiente, creamos un objeto socket con los parámetros AF_INET y SOCK_STREAM. Luego conectamos el cliente al host remoto y le enviamos algunos datos. El último paso es recibir los datos del servidor e imprimir la respuesta. Podemos encontrar el código siguiente en el archivo socket_cliente.py.

1.6.2. Administrar excepciones de socket

Para manejar excepciones, usaremos los bloques try y except. Se definen diversos tipos de excepciones en la biblioteca de sockets de Python para diferentes errores. Estas excepciones son:

•exception socket.timeout: este bloque captura excepciones relacionadas con el vencimiento de los tiempos de espera.

•exception socket.gaierror: este bloque detecta errores durante la obtención de información sobre direcciones IP; por ejemplo, cuando usamos los métodos getaddrinfo() y getnameinfo().

•exception socket.error: este bloque detecta errores genéricos de entrada y de salida y de comunicación con el servidor.

El ejemplo siguiente muestra cómo manejar las excepciones anteriormente mencionadas. Podemos encontrar el código siguiente en el archivo socket_excepciones.py:

En el script anterior, cuando ocurre una excepción relacionada con el hecho de que se ha superado el tiempo de espera de la conexión con una dirección IP, se devuelve la excepción socket.timeout.

Si intenta obtener información sobre dominios específicos o direcciones IP que no existen, probablemente obtendrá una excepción del tipo socket.gaierror con el error de conexión al servidor: [Errno 11001] mensaje de error getaddrinfo.

Si la conexión con el servidor objetivo no es posible, obtendrá una excepción socket.error con el error de conexión: [Errno 10061] No se pudo establecer una conexión porque la máquina de destino la rechazó.

1.6.3. Creando un cliente y un servidor TCP con sockets

En Python es posible crear un socket que actúe como cliente o como servidor. Los sockets cliente se encargan de realizar una conexión contra un host y un puerto, utilizando un protocolo determinado, mientras que los sockets a nivel de servidor se encargan de recibir conexiones por parte de los clientes en un puerto, utilizando un protocolo determinado.

A continuación analizamos cómo crear un par de scripts cliente y servidor que se comuniquen entre sí. La idea tras la creación de esta aplicación es que un socket cliente puede establecer una conexión con un determinado host, puerto y protocolo. El servidor de socket es responsable de recibir las conexiones de los clientes en un puerto y un protocolo específicos.

Lo primero que tenemos que hacer es crear un objeto socket para el servidor. Para crear un socket, se utiliza el constructor socket.socket(familia, tipo, protocolo), que puede tomar como parámetros la familia, el tipo y el protocolo. Por defecto, se utilizan la familia AF_INET y el tipo SOCK_STREAM. Lo primero que tenemos que hacer es crear un objeto de socket para el servidor:

Desde el punto de vista del servidor tenemos también que indicar en qué puerto se va a mantener a la escucha nuestro servidor utilizando el método bind(). Para sockets IP, como es nuestro caso, el argumento de bind() es una tupla que contiene los valores de host y el puerto.

El método bind(IP, PORT) le permite asociar un host y un puerto con un socket específico, teniendo en cuenta que los puertos 1-1024 están reservados para los protocolos estándar:

socket_servidor.bind((“localhost”, 9999))

1.6.4. Método para aceptar conexiones

Para hacer que el socket servidor acepte conexiones entrantes por parte de un cliente, el servidor utiliza los métodos listen() y accept(), que preparan al servidor para empezar a comenzar a escuchar peticiones. El método listen() requiere de un parámetro que indica el número de conexiones máximas que queremos aceptar y debe tener al menos el valor 1.

El método accept() se mantiene a la espera de conexiones entrantes, bloqueando la ejecución hasta que llega un mensaje. Cuando llega un mensaje, accept() desbloquea la ejecución, devolviendo un objeto socket que representa la conexión del cliente y una tupla que contiene el host y el puerto de dicha conexión.

Para obtener más información sobre estos métodos lo podemos hacer con el comando de help(socket):

| accept(self)   | accept() -> (socket object, address info)   |   | Wait for an incoming connection. Return a new socket   | representing the connection, and the address of the client.   | For IP sockets, the address info is a pair (hostaddr, port).

1.6.5. Enviar y recibir datos del socket

Una vez que tenemos definido nuestro objeto socket, podemos comunicarnos con el cliente mediante los métodos recv() y send() (o recvfrom y sendfrom en el caso de utilizar el protocolo UDP), que permiten recibir o enviar mensajes, respectivamente.

El método send() toma como parámetros los datos a enviar, mientras que el método recv() toma como parámetro el número máximo de bytes a aceptar.

Para crear un cliente bastaría con crear el objeto socket, utilizar el método connect() para conectarnos al servidor y utilizar los métodos send() y recv() que vimos anteriormente. El argumento del método connect() es una tupla con los valores del host y el puerto, exactamente igual a lo que vimos con el método bind().

1.6.6. Implementando el servidor TCP

En este ejemplo, vamos a crear un servidor TCP multiproceso donde el socket del servidor abre un socket TCP en la dirección localhost:9999 y escucha las solicitudes en un bucle infinito. Cuando reciba una solicitud del socket del cliente, devolverá un mensaje que indica que se ha realizado una conexión desde otra máquina.

El bucle infinito mantiene vivo el programa del servidor y no permite que finalice el código. La instrucción server.listen(10) escucha la conexión y espera una conexión por parte del cliente.

Para implementar un socket servidor se pueden utilizar algunos métodos analizados anteriormente en el módulo socket. En concreto, podemos utilizar el método bind(), que permite asociar un host y un puerto con un determinado socket.

Para aceptar peticiones por parte de un socket cliente habría que utilizar el método accept(). Así, el socket servidor espera recibir una conexión de entrada desde otro host.

El siguiente código lo podemos encontrar en el archivo servidor_tcp.py dentro de la carpeta cliente-servidor-tcp. El socket servidor abre un socket TCP en el puerto 1338 en la dirección localhost y se queda escuchando peticiones en un bucle infinito.

Ejecución:

$ python servidor_tcp.py Servidor tcp escuchando en el puerto ('localhost', 1338) Conexion desde ('127.0.0.1', 41592) Datos recibidos b'Hola mundo'

Cuando reciba una petición desde el socket cliente devolverá un mensaje indicando que se produce una conexión desde otra máquina.

1.6.7. Implementando el cliente TCP

En nuestro ejemplo, configuramos un servidor HTTP en la dirección 127.0.0.1 a través del puerto estándar 1338. Nuestro cliente se conectará a la misma dirección IP y puerto. Al establecer la conexión entre el cliente y el servidor, el cliente recibirá 1024 bytes de datos en la respuesta utilizando el método mysocket.recv(1024) y la almacenará en una variable llamada búfer, para posteriormente mostrar esa variable al usuario.

Puede encontrar el siguiente código en el archivo cliente_tcp.py dentro de la carpeta cliente_servidor_tcp:

En el código anterior, el método mysocket.connect(tupla_host_puerto) conecta al cliente con el servidor, y el método mysocket.recv(1024) recibe los mensajes enviados por el servidor.

Ejecución:

$ python cliente_tcp.py Datos recibidos b'Servidor recibe Hola Mundo\n'

En la ejecución vemos cómo el socket del cliente abre el mismo tipo de socket en el que el servidor está escuchando y envía un mensaje. El servidor responde y finaliza su ejecución, cerrando el socket del cliente.

1.7.Shell inversa con sockets

Una shell inversa es una acción con la que un usuario accede a la shell de un servidor externo. Por ejemplo, si estamos trabajando en una fase de pentesting relacionada con la post-explotación y nos gustaría crear un script que se invoque en ciertos escenarios y que automáticamente hará obtener una shell para acceder al sistema de ficheros de otra máquina, podríamos construir nuestra propia shell inversa en Python.

En este caso estamos utilizando dos nuevos módulos. El módulo de oshttps://docs.python.org/es/3/library/os.html es un módulo de interfaz para interactuar con el sistema operativo. El módulo de subprocesshttps://docs.python.org/3/library/subprocess.html permite que el script pueda ejecutar comandos e interactuar con la entrada y la salida de estos comandos.

En el script siguiente usamos el método sock.connect() para conectarnos a un host correspondiente a una determinada dirección IP y un puerto especificado (en nuestro caso, es localhost).

Una vez hemos obtenido la shell, podríamos obtener un listado del directorio actual, ejecutando el comando /bin/ls, pero antes necesitamos establecer la conexión con nuestro socket a través de la salida del comando.

Esto lo logramos con la instrucción: os.dup2(sock.fileno()). Podemos encontrar el código siguiente en el archivo shell_inversa.py:

Para probar el script anterior, necesitamos además ejecutar la aplicación netcathttp://netcat.sourceforge.net con el siguiente comando ncat -l -v -p 45679 desde nuestra terminal en Linux indicando el puerto que declaramos en el script.

En el caso del sistema operativo Windows, podríamos crear la shell inversa creando una aplicación cliente-servidor donde el servidor envíe comandos al cliente. Podemos encontrar el código siguiente en el archivo servidor.py dentro de la carpeta shell_inversa_windows:

El servidor podría estar escuchando en el puerto localhost:9090 para una nueva conexión. Cuando un cliente solicita una nueva conexión, enviará su nombre de host y el servidor imprimirá el nombre de host y la dirección del cliente que solicitó la conexión. Podemos encontrar el código siguiente en el archivo cliente.py dentro de la carpeta shell_inversa_windows: