elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Usando Git para manipular el directorio de trabajo, el índice y commits (segunda parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Scripting
| | |-+  Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3  (Leído 3,667 veces)
Drakaris

Desconectado Desconectado

Mensajes: 286


Todo lo que puedas imaginar, lo puedes crear.


Ver Perfil
Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3
« en: 20 Julio 2020, 19:21 pm »

Buenas, estoy haciendo un servidor proxy con Python3. La idea es la siguiente:

1 . Primero, capturo el puerto especificado en el primer argumento:

Código:
if len(sys.argv) == 2:
        pass
else:
        sys.exit()


# CONSTANTS GLOBALS
global PORT_LISTEN, BUFFER_RECV, CONNECTIONS
PORT_LISTEN = int(sys.argv[1])
BUFFER_RECV = 8192
CONNECTIONS = 5

2. Después con el puerto especificado, creo una conexion socket servidor bind(), y obtengo los datos recibidos, con ello, ejecuto un hilo, donde paso los datos de la conexion y datos a la funcion get_data.
Código:
print("[*] Listening Port "+str(PORT_LISTEN)+"....")
lister = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
lister.bind(("",PORT_LISTEN))
lister.listen(CONNECTIONS)
while True:
        try:
                conn, addr = lister.accept()
                data = conn.recv(BUFFER_RECV)
                thread_data = threading.Thread(target=get_data, name="get_data", args=(conn, addr, data))
                thread_data.start()
        except KeyboardInterrupt:
                conn.close()
                lister.close()
                print("\n[\033[1;31m!\033[0m] Exiting Proxy Server....")
                sys.exit()

3. En la funcion get_data, obtengo la url, donde cogeré dominio donde estoy visitando
Código:
        #get url
        url = str(data).split('\\n')[0]
        url = str(url).split(' ')[1]

        #get FQDN
        website = url.replace("http://","")
        website = website.split("/")[0]

4. Una vez sé en que dominio estoy haciendo la petición, si el dominio contiene ":" cogeré el puerto especificado. Por defecto el puerto es 80, si no se detecto ":" en el dominio.
Código:
        port = 80
        if website.count(":") == 1:
                list = website.split(":")
                website = list[0]
                port = list[1]
                port = int(port)
        print("\n\033[1;32mwebsite\033[0m: "+str(website)+" \033[1;32mport\033[0m: "+str(port))

5. Finalmente ejecuto la funcion send_data con sus argumentos, para que me haga una conexion socket cliente al servidor y envie los datos, con un bucle.
Código:
send_data(website, int(port), conn, data, addr, True)

def send_data(site, port, conn, data, addr):
    sender = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sender.connect((site,port))
    sender.send(data)
    while True:
            reply = sender.recv(8192)
            if len(reply) > 0:
                conn.send(reply)
            else:
                break
    sender.close()
    conn.close()

Esto funciona correctamente en sitios web HTTP, pero como cabe esperar, el HTTPS no, por los certificados ssl.

Así que modifiqué el apartado 5. Y con una condicional, si el puerto es 443, ejecutará lo siguiente:
Código:
        if port == 443:
                send_data(website, int(port), conn, data, addr, True)
        else:
                send_data(website, int(port), conn, data, addr, False)

def send_data(site, port, conn, data, addr, SSL):
        sender = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sender.connect((site,port))
        sender.send(data)
        while True:
                reply = sender.recv(BUFFER_RECV)
                if len(reply) > 0:
                        conn.send(reply)
                else:
                        break
        sender.close()
        conn.close()


En send_data añadí un argumento booleano llamada SSL, que antes de hacer nada, si este es True, sería https, lo que no sé muy bien, es que hacer aquí.


Me he mirado el funcionamiento de los sitios HTTPS en youtube.


En el minuto 2:16, explica con más profundidad como trabaja este protocolo. He pensado, si hay alguna forma de obtener el certificado que me da el servidor para obtener la llave publica.... pero no se muy bien.

Miré este enlace: https://soursop-dev.blogspot.com/2020/05/creacion-de-un-servidor-web-proxy-en.html

Donde te explica como hacer una conexion ssl con el modulo ssl.

Así que modifique la funcion send_data:
Código:
def send_data(site, port, conn, data, addr, SSL):
        if SSL == True:
                server_cert = "./ssl/sechome.csr"
                client_cert = "./ssl/sechome.crt"
                client_key = "./ssl/sechome.key"
                context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=server_cert)
                context.load_cert_chain(certfile=client_cert, keyfile=client_key)
                s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                sender = context.wrap_socket(s, server_side=False, server_hostname=site)
        else:
                sender = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sender.connect((site,port))
        sender.send(data)
        while True:
                reply = sender.recv(BUFFER_RECV)
                if len(reply) > 0:
                        conn.send(reply)
                else:
                        break
        sender.close()
        conn.close()
:

Creé los certificados autofirmados:
Código:
$ cd ssl
ssl/$ openssl req -newkey rsa:2048 -nodes -keyout sechome.key -out sechome.csr -sha256
ssl/$ openssl x509 -signkey sechome.key -in sechome.csr -req -days 365 -out sechome.crt

Como bien me imaginaba esto no funciona, ya que tendría que coger el certificado que me da el dominio al hacer la peticion. No acabo de entender muy bien en las variables de crt y key, que llaves tengo que poner...

Código:
                server_cert = "./ssl/sechome.csr"
                client_cert = "./ssl/sechome.crt"
                client_key = "./ssl/sechome.key"


CODE COMPLETE:
Código:
#!/usr/bin/python3
import socket, threading, re, sys, os

if len(sys.argv) == 2:
pass
else:
sys.exit()


# CONSTANTS GLOBALS
global PORT_LISTEN, BUFFER_RECV, CONNECTIONS
PORT_LISTEN = int(sys.argv[1])
BUFFER_RECV = 8192
CONNECTIONS = 5

def send_data(site, port, conn, data, addr, SSL):
if SSL == True:
server_cert = "./ssl/sechome.csr"
client_cert = "./ssl/sechome.crt"
client_key = "./ssl/sechome.key"
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=server_cert)
context.load_cert_chain(certfile=client_cert, keyfile=client_key)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sender = context.wrap_socket(s, server_side=False, server_hostname=site)
else:
sender = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sender.connect((site,port))
sender.send(data)
while True:
reply = sender.recv(BUFFER_RECV)
if len(reply) > 0:
conn.send(reply)
else:
break
sender.close()
conn.close()


def get_data(conn, addr, data):
#get url
url = str(data).split('\\n')[0]
url = str(url).split(' ')[1]

#get FQDN
website = url.replace("http://","")
website = website.split("/")[0]
port = 80
if website.count(":") == 1:
list = website.split(":")
website = list[0]
port = list[1]
port = int(port)
print("\n\033[1;32mwebsite\033[0m: "+str(website)+" \033[1;32mport\033[0m: "+str(port))
if port == 443:
send_data(website, int(port), conn, data, addr, True)
else:
send_data(website, int(port), conn, data, addr, False)


print("[*] Listening Port "+str(PORT_LISTEN)+"....")
lister = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
lister.bind(("",PORT_LISTEN))
lister.listen(CONNECTIONS)
while True:
try:
conn, addr = lister.accept()
data = conn.recv(BUFFER_RECV)
thread_data = threading.Thread(target=get_data, name="get_data", args=(conn, addr, data))
thread_data.start()
except KeyboardInterrupt:
conn.close()
lister.close()
print("\n[\033[1;31m!\033[0m] Exiting Proxy Server....")
sys.exit()


En línea

Lo increible, no es lo que ves, sino como es
el-brujo
ehn
***
Desconectado Desconectado

Mensajes: 21.580


La libertad no se suplica, se conquista


Ver Perfil WWW
Re: Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3
« Respuesta #1 en: 20 Julio 2020, 22:45 pm »

No tienes que crear ningún certificado para conectarte a una página web que usa https, ni crt, ni key, ni csr, ni nada. Eso es para el servidor web si fuera tuyo, pues para configurar el certificado SSL.

Para navegar o en tu caso, para enviar una petición con datos sólo tienes que usar la librería SSL y ya:

Código:
import ssl

https://docs.python.org/3/library/ssl.html



En línea

@XSStringManolo
Hacker/Programador
Colaborador
***
Desconectado Desconectado

Mensajes: 2.397


Turn off the red ligth


Ver Perfil WWW
Re: Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3
« Respuesta #2 en: 21 Julio 2020, 09:23 am »

Si quieres interceptar tráfico te recomiendo usar mitmproxy.
Yo uso el cli, pero tienen lib para python. Aquí te dejo una implementación:
https://gist.github.com/carpedm20/27f5157c3ff8f2a63a37
En línea

Mi perfil de patrocinadores de GitHub está activo! Puedes patrocinarme para apoyar mi trabajo de código abierto 💖

Drakaris

Desconectado Desconectado

Mensajes: 286


Todo lo que puedas imaginar, lo puedes crear.


Ver Perfil
Re: Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3
« Respuesta #3 en: 22 Julio 2020, 11:50 am »

Buenas! He modificado el código y al detectar que el puerto es 443 he creado la funcion send_data_tls() donde le envio los mismos argumentos.
Código:
if port == 443:
send_data_tls(website, int(port), data, conn)
else:
send_data(website, int(port), conn, data, addr)

funcion send_data_tls
Código:
def send_data_tls(site, port, data, conn):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sender = ssl.wrap_socket(s, do_handshake_on_connect=False, ssl_version=ssl.PROTOCOL_TLS)
sender.connect((site,port))
print("\033[1;32m data sent\033[0m: "+str(data))
sender.send(data)
while True:
reply = sender.recv(BUFFER_RECV)
sender.send(reply)
print("\033[1;32m data sent X2\033[0m: "+str(reply))


Cuando lo ejecuto me imprime lo siguiente en la pantalla:
imagenes:https://drive.google.com/drive/folders/1S4GZBjBUziGXvo_wNpVon8EkyoEs1tue?usp=sharing

Como se puede ver en las imagenes, la salida me imprime bién los datos a enviar:
Citar
b'CONNECT platzi.com:443 HTTP/1.1\r\nUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: platzi.com:443\r\n\r\n'

Pero cuando recibo la respuesta, recibo un erro 400 Bad Request:
Citar
b'HTTP/1.1 400 Bad Request\r\nServer: cloudflare\r\nDate: Wed, 22 Jul 2020 09:30:08 GMT\r\nContent-Type: text/html\r\nContent-Length: 155\r\nConnection: close\r\nCF-RAY: -\r\n\r\n<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n<center><h1>400 Bad Request</h1></center>\r\n<hr><center>cloudflare</center>\r\n</body>\r\n</html>\r\n'

Además me sale el siguiente error:
Citar
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:2162)

El error 400 indica que la petición que fue enviada no a podido ser procesada por algún tipo de error. Que puede estar causando este error?

En realidad no acabo de entender porque, yo me conecto a un sitio web por el puerto 443, y envio los datos envidos con data() a otro servidor, en este caso a Cloudflare. Es aquí que yo no entiendo que datos tengo que enviar o que debo de hacer. ¿Este es el paso donde se hace el handshake antes de conectarse al sitio web?
« Última modificación: 22 Julio 2020, 19:50 pm por Drakaris » En línea

Lo increible, no es lo que ves, sino como es
Drakaris

Desconectado Desconectado

Mensajes: 286


Todo lo que puedas imaginar, lo puedes crear.


Ver Perfil
Re: Como envia datos a un sitio web HTTPS via socket? | Servidor Proxy python3
« Respuesta #4 en: 25 Julio 2020, 18:19 pm »

Buenas!! Cuando he hecho la parte del proxy HTTP entendí su funcionamiento y todo, pero ahora en la parte de HTTPS. no acabo de entender muy bien.... Modifiqué el código, para ver mejor lo que esta pasando...

Código:
#CODE
class Send_data_tls:
def send_data(self, site, port, conn, data, addr):
context = ssl.create_default_context()
sender = context.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname=site)
sender.connect((site,port))
HEAD = "HEAD / HTTP 1.1\r\nHost: "+str(site)+"\r\n\r\n"
sender.send(HEAD.encode())
print("\033[1;32m data sent\033[0m: "+str(data))
receive = sender.recv(BUFFER_RECV)
print("\n\n"+str(receive))

def get_data(conn, addr, data):
#CODE
if port == 443:
SSL = Send_data_tls()
SSL.send_data(website, int(port), conn, data, addr)
else:
send_data(website, int(port), conn, data, addr)
#CODE
#CODE

Hice varias pruebas y ninguna me surgió nada interesante... donde pueda partir de alla. Cree la clase Send_data_tls con la funcion send_data (creo una clase por que me imagino que tengo que hacer algo antes de enviar los datos a la web HTTPS).

En la funcion get_data en la condicional cargo la clase en la variable SSL y ejecuto la funcion send_data().

Cuando lo ejecuto, me encuentro con algo MUY lógico...
[IMAGE] =>  https://drive.google.com/file/d/1rdieWydRAgjcHvhZ7qdK6hrk583-V2Hl/view?usp=sharing

Lo que pasa, y cuya cosa ya me dí cuenta es que, al conectarme a la web por 443
Citar
data sent: b'CONNECT platzi.com:443 HTTP/1.1\r\nUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: platzi.com:443\r\n\r\n'

Este se conecta antes con el servidor Cloudflare (creo que la CA para autenticar el certificado), y este por alguna razon me recibe el 400 Bad Request.

El problema en que no entiendo que esta pasando y que debo hacer.... El cloudflare me esta rechazando.

Puedo hacer lo que quiero hacer con unicamente el modulo ssl?

Segun @el-brujo me dijo que con ese modulo me bastava. Hay algun post o sitio donde expliquen como funciona este modulo?
Para navegar o en tu caso, para enviar una petición con datos sólo tienes que usar la librería SSL y ya:

Código:

import ssl


https://docs.python.org/3/library/ssl.html


Gracias
En línea

Lo increible, no es lo que ves, sino como es
Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
Como saber si el puerto https para por proxy isa
Seguridad
calcetin 1 3,226 Último mensaje 26 Diciembre 2011, 13:01 pm
por adastra
probleme de recivir datos completos con servidor socket
Programación General
momo1234 0 1,465 Último mensaje 14 Mayo 2012, 22:36 pm
por momo1234
como Forzar HTTPS en mi sitio?
Seguridad
L0Ko 1 2,561 Último mensaje 20 Noviembre 2012, 09:04 am
por adastra
¿Cómo hacer un tunnel entre socks5 y https proxy?
GNU/Linux
WHK 3 3,213 Último mensaje 28 Octubre 2015, 16:28 pm
por MinusFour
Cómo saltarse el bloqueo a un sitio web sin tener que usar un proxy o VPN
Noticias
wolfbcn 0 7,146 Último mensaje 12 Marzo 2018, 02:22 am
por wolfbcn
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines