From 22a6f6c2037e0bf1f6b7b4f29c32a7435b09643d Mon Sep 17 00:00:00 2001 From: Tina Johnson Date: Fri, 15 Dec 2023 04:58:18 +0100 Subject: [PATCH] Update SNI branch to Python 3 - Replaced expired CA certificate - Changed certificate sigining algorithm to SHA256 - Added extensions to generated X509 certificates to support interaction with latest browsers Thanks to Nhan Huynh for implementing this feature :) --- fakenet/configs/default.ini | 11 ++- fakenet/configs/fakenet_ca.crt | 19 +++++ fakenet/configs/fakenet_ca.key | 28 +++++++ fakenet/listeners/DNSListener.py | 2 +- fakenet/listeners/HTTPListener.py | 13 ++- fakenet/listeners/ProxyListener.py | 4 +- fakenet/listeners/ssl_utils/__init__.py | 100 +++++++++++++----------- fakenet/listeners/ssl_utils/privkey.pem | 47 ----------- fakenet/listeners/ssl_utils/server.pem | 18 ----- setup.py | 4 +- test/test.py | 38 ++++++--- 11 files changed, 147 insertions(+), 137 deletions(-) create mode 100644 fakenet/configs/fakenet_ca.crt create mode 100644 fakenet/configs/fakenet_ca.key delete mode 100644 fakenet/listeners/ssl_utils/privkey.pem delete mode 100644 fakenet/listeners/ssl_utils/server.pem diff --git a/fakenet/configs/default.ini b/fakenet/configs/default.ini index a88a76e3..7845132e 100644 --- a/fakenet/configs/default.ini +++ b/fakenet/configs/default.ini @@ -199,6 +199,11 @@ BlackListPortsUDP: 67, 68, 137, 138, 443, 1900, 5355 # hostname string, !hostname to insert the actual hostname # of the system, or !random to generate a random hostname # between 1 and 15 characters (inclusive). +# * Static_CA - Set FakeNet to use user provided CA certificate to sign generated certificates. +# * CA_Cert - CA certificate in PEM format to be used when Static_CA config is set. Manually +# add this certificate to Windows trust store before executing FakeNet. +# * CA_Key - CA private key in PEM format to be used when Static_CA config is set. + [ProxyTCPListener] Enabled: True @@ -208,8 +213,8 @@ Port: 38926 Listeners: HTTPListener, RawListener, FTPListener, DNSListener, POPListener, SMTPListener, TFTPListener, IRCListener Hidden: False Static_CA: No -CA_Cert: listeners/ssl_utils/server.pem -CA_Key: listeners/ssl_utils/privkey.pem +CA_Cert: configs/fakenet_ca.crt +CA_Key: configs/fakenet_ca.key [ProxyUDPListener] Enabled: True @@ -286,7 +291,7 @@ Hidden: False # Custom: sample_custom_response.ini [HTTPListener443] -Enabled: False +Enabled: True Port: 443 Protocol: TCP Listener: HTTPListener diff --git a/fakenet/configs/fakenet_ca.crt b/fakenet/configs/fakenet_ca.crt new file mode 100644 index 00000000..4f703acd --- /dev/null +++ b/fakenet/configs/fakenet_ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHzCCAgegAwIBAgIUdwSkHdOM2mMDw094Kha+9Z9/w60wDQYJKoZIhvcNAQEL +BQAwHzELMAkGA1UEBhMCVVMxEDAOBgNVBAMMB2Zha2VuZXQwHhcNMjMxMjE0MDAx +NDUxWhcNMjQwMTEzMDAxNDUxWjAfMQswCQYDVQQGEwJVUzEQMA4GA1UEAwwHZmFr +ZW5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7pGzS8bX8M3SSQ +mk79puvqGBHwWVpDK82T44N/8mXHJ1R/7jMDq2wpkSjliiAE0mxPpkzMbr9mkeP/ +/31GKAszbJYnurrxxYbLyOdRst2VqoXkWTia61lrsRIGcjwzKe2zyMCcuiyRTLcP +BmYd/ie5AzyHxitlS49cub+QkIODUAKTiZT3mPu6Yw2XvYkg+up69NzC0a/XexUv +PvgbBizquKj/YzMSp5X7ieYGv0xHf8Dhf3mqh9oLk35X/qV3LqdnVPjweCR8X9ze +yhfBbDr1VoBnOe2Nb5hlU9MB//A0hgDYj5TrHa4JrbNkv3lYMd4uv/CBW6o18Ba+ +/zjEvyECAwEAAaNTMFEwHQYDVR0OBBYEFD3wRGMPQdWtBCKRy5c9N5YWnki2MB8G +A1UdIwQYMBaAFD3wRGMPQdWtBCKRy5c9N5YWnki2MA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBAGT5rmafrlv8VIXAgc0iazNd7A6rT7xNLkuF2JGK +7NV3yvsWM6SA+DlG7y70om+eKjd+qxzinxnSt6uFJhcdCqot1LU1u3OZDifTTJmk +31yEYp/+A93qjwe1Ag2rsVcztRl88KtsKrKNohv24iiWfIVDnHo0joerXoaGQwo6 +zXl4GVJBEEAhf2GQRgyXcoWkSrsq8UKtVV9dI5QgIS6vZ65oNQEeoAXH56ihFUBX +hS+4Ko2FfUtxbfbw7tpDaNtqhAzJ+LE4RoDUepyCDXPha0Wb4giGOd5EEubYrFKi +DOdAMiiQT1WLK3/UnMlCOV4lne+g9JwBCXL8C0F0W1fFZY8= +-----END CERTIFICATE----- diff --git a/fakenet/configs/fakenet_ca.key b/fakenet/configs/fakenet_ca.key new file mode 100644 index 00000000..6914c8b0 --- /dev/null +++ b/fakenet/configs/fakenet_ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDO6Rs0vG1/DN0k +kJpO/abr6hgR8FlaQyvNk+ODf/JlxydUf+4zA6tsKZEo5YogBNJsT6ZMzG6/ZpHj +//99RigLM2yWJ7q68cWGy8jnUbLdlaqF5Fk4mutZa7ESBnI8Mynts8jAnLoskUy3 +DwZmHf4nuQM8h8YrZUuPXLm/kJCDg1ACk4mU95j7umMNl72JIPrqevTcwtGv13sV +Lz74GwYs6rio/2MzEqeV+4nmBr9MR3/A4X95qofaC5N+V/6ldy6nZ1T48HgkfF/c +3soXwWw69VaAZzntjW+YZVPTAf/wNIYA2I+U6x2uCa2zZL95WDHeLr/wgVuqNfAW +vv84xL8hAgMBAAECggEAAyp6KDPcHCjH5XU4hXGPeGYvkhldQtxqsw2Rr7xpVWrl +dw1q8/dR2kVGVFSlBWe7tIPk0Ew8fD14+xtXG4xhmECAoElTHY+b7VgxkVJPem9h +RD4XOLsP4ba4rlNes+DiUCHKbyHTOox1RXytQZxNbgbVr7tOVNU1nf0rVmzt9Zbw +uPltpCcL+yvnaWmBUgdCLIhIT/HDrp4+uZP+zW7pVm/KMQL0I9OMnm6dCimP88pU +meepDRdxHkHVb347jx0+nWSH2V63wxH6WR8lAb4oAXeQXUgS8Q0lXh70EW/x0Ut3 +neB/mfuaXLVrBcBTbhPTU7PKd/Sc+dkkqhEpNRR3dQKBgQDyLNPyWEJVXwQ6iOom +xxyqm61DZw8IpBYVmfYrOisTUWVC+oNlpsNPzWsgwmWzpcJ8s6ykupWFcWZeJv2h +XPlq+Ai6Ky0v+yESiUtNb+lKuXGvHqTpTxvPgL/20ZMfRIkWs9YBLA+aTh5RC8Vs +i12fRW9JTZTKgyf7PEkj+DjCCwKBgQDauOszqtFs6d36AMVJJ7OlmZnEWaELv9bW +ns7kdeBgr35gzGbDOkyQGBII5SCwpgWbXyxT39T9xuNoWCWXSZdHDco/UlAG47qI +Pq/KmtbrKxvg6yKsPIDV+cexuBrLIvzsaz8qPbfE/W7nxf+FtvDhvnGyqoiYd3DS +XSD5HcwLAwKBgQDdQpG+mF66qx4s8Myl80NAqQ1LSMyWg3xd7hXYdsPGWZaf9Eu6 +wvstXSvkeVf8I5Um4+33bzWO/wWdPhh6pnyG++jVVv9pGBOmYOP48yd9iyLP8bqQ +IyPwmNxKgD3f0nlB0brTxVLYE0llmNCelFJMY18C5SvtPpl31COrBm2s8wKBgG8D +zN28pe+SBIkQOxKWhChZfiKbG5LLHFBy6rAq5GguqwaWuNH+lT3N+dlp8t22ZsIl +3Gn2AjWM7X/Yvbu8LnxyE2Vwcg4NKHBe4PsE/HEAwHW44zBoxTvWO/WIbJEOgTG+ +faEDEnN57wDVDozf/gOWlj8JL6uzdCBSBJps9VPhAoGAWQUkdpxRxEARhL7wYR7g +EidDWKnULiULjlrP7VPMJ5hrZf5PmWyZLWW3SkEUhcf25CnJoUoq1OOB1GjL9lBL +0+tXalLnA/mRxO5ILzwJivHyJKnljuPKyXmpKt8H4KRUXV3Uk2may58Jwn8InuRE +aW956i0O1tgrDOj3tSAT8KI= +-----END PRIVATE KEY----- diff --git a/fakenet/listeners/DNSListener.py b/fakenet/listeners/DNSListener.py index da410b81..2acbe160 100644 --- a/fakenet/listeners/DNSListener.py +++ b/fakenet/listeners/DNSListener.py @@ -19,7 +19,7 @@ class DNSListener(object): def taste(self, data, dport): - confidence = 1 if dport is 53 else 0 + confidence = 1 if dport == 53 else 0 try: d = DNSRecord.parse(data) diff --git a/fakenet/listeners/HTTPListener.py b/fakenet/listeners/HTTPListener.py index ce7f2f61..421630fd 100644 --- a/fakenet/listeners/HTTPListener.py +++ b/fakenet/listeners/HTTPListener.py @@ -18,11 +18,8 @@ import mimetypes import time -import shutil -import traceback -import subprocess -from OpenSSL import crypto -from ssl_utils import SSLWrapper + +from .ssl_utils import SSLWrapper from . import * MIME_FILE_RESPONSE = { @@ -208,14 +205,14 @@ def start(self): if self.config.get('usessl') == 'Yes': self.logger.debug("HTTP Listener starting with SSL") config = { - 'cert_dir': self.config.get('cert_dir', 'temp_certs'), + 'cert_dir': self.config.get('cert_dir', 'configs/temp_certs'), 'networkmode': self.config.get('networkmode', None), - 'static_ca': self.config.get('static_ca', False), + 'static_ca': self.config.get('static_ca', "No"), 'ca_cert': self.config.get('ca_cert'), 'ca_key': self.config.get('ca_key') } self.sslwrapper = SSLWrapper(config) - self.server.sslwrapper = sslwrapper + self.server.sslwrapper = self.sslwrapper self.server.socket = self.server.sslwrapper.wrap_socket( self.server.socket) diff --git a/fakenet/listeners/ProxyListener.py b/fakenet/listeners/ProxyListener.py index 100f1ef4..55747859 100644 --- a/fakenet/listeners/ProxyListener.py +++ b/fakenet/listeners/ProxyListener.py @@ -12,7 +12,7 @@ import logging import ssl from OpenSSL import SSL -from .ssl_utils import ssl_detector +from .ssl_utils import ssl_detector, SSLWrapper from . import * import os import traceback @@ -54,7 +54,7 @@ def start(self): self.logger.debug('Starting TCP ...') config = { - 'cert_dir': self.config.get('cert_dir', 'temp_certs'), + 'cert_dir': self.config.get('cert_dir', 'configs/temp_certs'), 'networkmode': self.config.get('networkmode', None), 'static_ca': self.config.get('static_ca', False), 'ca_cert': self.config.get('ca_cert'), diff --git a/fakenet/listeners/ssl_utils/__init__.py b/fakenet/listeners/ssl_utils/__init__.py index f99d7ab8..337ec60b 100644 --- a/fakenet/listeners/ssl_utils/__init__.py +++ b/fakenet/listeners/ssl_utils/__init__.py @@ -7,11 +7,11 @@ import sys import ssl import random -from listeners import ListenerBase +from pathlib import Path from OpenSSL import crypto -g_ssl_fellback = False # Notify only once of SSL static certificate fallback - +from fakenet import listeners +from fakenet.listeners import ListenerBase class SSLWrapper(object): NOT_AFTER_DELTA_SECONDS = 300 * 24 * 60 * 60 @@ -22,18 +22,20 @@ def __init__(self, config): self.config = config self.ca_cert = None self.ca_key = None + self.ca_cn = self.CN - cert_dir = self.config.get('cert_dir', None) + cert_dir = self.abs_config_path(self.config.get('cert_dir', None)) if cert_dir is None: - raise RuntimeError("certdir key is not specified in config") + raise RuntimeError("cert_dir key is not specified in config") if not os.path.isdir(cert_dir): os.makedirs(cert_dir) # generate and add root CA, which is used to sign for other certs: if self.config.get('static_ca') == 'Yes': - self.ca_cert = self.config.get('ca_cert', None) - self.ca_key = self.config.get('ca_key', None) + self.ca_cert = self.abs_config_path(self.config.get('ca_cert', None)) + self.ca_key = self.abs_config_path(self.config.get('ca_key', None)) + self.ca_cn = self._load_cert(self.ca_cert).get_subject().CN else: self.ca_cert, self.ca_key = self.create_cert(self.CN) if ( not self.config.get('networkmode', None) == 'multihost' and @@ -42,36 +44,16 @@ def __init__(self, config): self._add_root_ca(self.ca_cert) def wrap_socket(self, s): - global g_ssl_fellback try: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS) except AttributeError as e: - if not g_ssl_fellback: - g_ssl_fellback = True - self.logger.error('Exception calling ssl.SSLContext: %s' % - (e.message)) - self.logger.error('Falling back on static certificate') - return self.wrap_socket_fallback(s) + self.logger.error('Exception calling ssl.SSLContext: %s' % + (e.message)) else: - ctx.set_servername_callback(self.sni_callback) + ctx.sni_callback = self.sni_callback ctx.load_cert_chain(certfile=self.ca_cert, keyfile=self.ca_key) return ctx.wrap_socket(s, server_side=True) - def wrap_socket_fallback(self, s): - keyfile_path = 'listeners/ssl_utils/privkey.pem' - keyfile_path = ListenerBase.abs_config_path(keyfile_path) - if keyfile_path is None: - raise RuntimeError('Could not locate %s', (key_file,)) - - certfile_path = 'listeners/ssl_utils/server.pem' - certfile_path = ListenerBase.abs_config_path(certfile_path) - if certfile_path is None: - raise RuntimeError('Cound not locate %s' % (certfile_path,)) - - return ssl.wrap_socket(s, keyfile=keyfile_path, certfile=certfile_path, - server_side=True, ciphers='RSA') - - def create_cert(self, cn, ca_cert=None, ca_key=None, cert_dir=None): """ Create a cert given the common name, a signing CA, CA private key and @@ -83,10 +65,10 @@ def create_cert(self, cn, ca_cert=None, ca_key=None, cert_dir=None): f_selfsign = ca_cert is None or ca_key is None if not cert_dir: - cert_dir = os.path.abspath(self.config.get('cert_dir')) + cert_dir = self.abs_config_path(self.config.get('cert_dir')) else: cert_dir = os.path.abspath(cert_dir) - + cert_file = os.path.join(cert_dir, "%s.crt" % (cn)) key_file = os.path.join(cert_dir, "%s.key" % (cn)) if os.path.exists(cert_file) and os.path.exists(key_file): @@ -108,6 +90,10 @@ def create_cert(self, cn, ca_cert=None, ca_key=None, cert_dir=None): # Create a cert cert = crypto.X509() + + # Setting certificate version to 3. This is required to use certificate + # extensions which have proven necessary when working with browsers + cert.set_version(2) cert.get_subject().C = "US" cert.get_subject().CN = cn cert.set_serial_number(random.randint(1, 0x31337)) @@ -117,11 +103,21 @@ def create_cert(self, cn, ca_cert=None, ca_key=None, cert_dir=None): cert.gmtime_adj_notAfter(na) cert.set_pubkey(key) if f_selfsign: + extensions = [ + crypto.X509Extension(b'basicConstraints', True, b'CA:TRUE'), + ] cert.set_issuer(cert.get_subject()) - cert.sign(key, "sha1") + cert.add_extensions(extensions) + cert.sign(key, "sha256") else: + alt_name = b'DNS:' + cn.encode() + extensions = [ + crypto.X509Extension(b'basicConstraints', False, b'CA:FALSE'), + crypto.X509Extension(b'subjectAltName', False, alt_name) + ] cert.set_issuer(ca_cert_data.get_subject()) - cert.sign(ca_key_data, "sha1") + cert.add_extensions(extensions) + cert.sign(ca_key_data, "sha256") try: with open(cert_file, "wb") as cert_file_input: @@ -156,8 +152,8 @@ def _load_cert(self, certpath): with open(certpath, 'rb') as cert_file_input: data = cert_file_input.read() ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, data) - except: - self.logger.error("Failed to load certficate") + except crypto.Error as e: + self.logger.error("Failed to load certficate: %s" % e.message) return ca_cert def _load_private_key(self, keypath): @@ -189,17 +185,29 @@ def _remove_root_ca(self, cn): argv = ['certutil', '-delstore', 'Root', cn] return self._run_win_certutil(argv) - def __del__(self): - cert = None - if self.ca_cert: - cert = self._load_cert(self.ca_cert) - - if (cert is not None and - not self.config.get('networkmode', None) == 'multihost' and - not self.config.get('static_ca') == 'Yes'): - self._remove_root_ca(cert.get_subject().CN) - shutil.rmtree(self.config.get('cert_dir'), ignore_errors=True) + if (not self.config.get('networkmode', None) == 'multihost' and + not self.config.get('static_ca') == 'Yes'): + self._remove_root_ca(self.ca_cn) + shutil.rmtree(self.abs_config_path(self.config.get('cert_dir', None)), ignore_errors=True) return + + def abs_config_path(self, path): + """ + Attempts to return the absolute path of a path from a configuration + setting. + """ + + # Try absolute path first + abspath = os.path.abspath(path) + if os.path.exists(abspath): + return abspath + + if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): + abspath = os.path.join(os.getcwd(), path) + else: + abspath = os.path.join(os.fspath(Path(__file__).parents[2]), path) + + return abspath diff --git a/fakenet/listeners/ssl_utils/privkey.pem b/fakenet/listeners/ssl_utils/privkey.pem deleted file mode 100644 index 16369418..00000000 --- a/fakenet/listeners/ssl_utils/privkey.pem +++ /dev/null @@ -1,47 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDzcX3h/MmqeYoj -t3qVhgcV8NwApbBYr6iLmmx384ayykau/0lfcN2NGovofwMSGyqFom42d6GgFLEJ -uaPiQ//RZK6NQl2w1X2wP/Un4SET5Id4sMBShR2zS/5H7GgSlgqwUqKegnel348b -/A2YcdAd4iQ97yBxINxbkciN/d8L2omI4etreD/yghqX5WN2UH+NoSg7FdpvgBIh -aMKsqC9vEqbgXaIFr3jDJVjyEy4wBfvFKTgEAIvHhE2B6B1/ij3eJk0ZKpub3Du2 -gekZTpqajQL4viE1wjvydjJq/ixL2dr3q3+Ko/4xl0bLU8r6+/0CkkFnMxMP2+mp -4xVT9uM3AgMBAAECggEBAJpM7hAGDMCbxp361pzdVbJndtqGKm8b74WEvImO9mpu -YTzcHGJ9BEBCejlD/+tDAsGvAZJOY0g2tTvHyYNJvGS5HxXz4bSKrN7Aux+qxy93 -oxIxXcUwEHIrkaF+yzw0k9PMnLxBT5r4Rxniua9NPC8kvfnvji2GOYVksqylI/G7 -NwBLrYjFOJLXqikVZveyG0uQPXxwtRwaVgDSU7QX8kxXfpwsaZGdnm7yk80hrNte -U4U/A7kv63tE0+qFu9jQVatYwcGAR7kNr9qsFOHvxPbEQkvS6yOUHMdNF8ZpwFbA -vv6O1pt4jV7kAo5TLFoGXgnONBifzZCTD127laH1j1kCgYEA/Te6CsWbZm/6pPFJ -QpVyzDlSam7hCb5rmOneoOeIjo2CLn22DEeu9ZqETj2xLMfA0diV1r23Eia+kHwO -3XdNKLg3/Ue22FpQuV4iCqxDXkwdM/hMeyeLssQmNlGE0+iBaXL8UZCu5zWnw2TD -pkjeS6KGPCMN1MvYdhGIzYxt/zUCgYEA9h5FVLxBA0s40jgILpPvLbIF84cVDF46 -xnFs7c7jQSH+DYK9C7GFYKyDw3UTI5+UtwalxsBYWSLZuTSzdpUocKzT31TmbY2M -nOYb3s7VHrJUXWnCl5m6EW1oqRUVMZuseECg7IABWUaI8qUQAbU2JAD1VeoslpVo -s7C9TzuxCjsCgYEA395l3+Im6uDzkuIz5+cjEEVZhPm0gZ3VmOKjTlSFGasoPhws -WB0EJOXQNTA7tSCBa8V1a95cvXJ9plXX+prgH7EG5ymBETSAC/KaXB9CjFr0sp7C -V1t2Gb1rHzjhG0yDJYxgYWhuCcIZKRmsFBZ2Wh49WWuQbeMu4+vKrBeMpEUCgYEA -4+Wh01XCaZdk1Ru3T9ICHnEDG75QmjRC/nAXKplxS2V7hu0xujs2Qw9br0igYVMq -dNP9+20uIdOogduv9jUlzjfqtJk8CeORedOFqSoipBsDDcCZxKE/b1W9HRv9lQMO -kAdgO75IAW/T7cM0cDBpIquUgWqmwQU2f3U/xreCNvMCfxQQiFTTSyhnZu195hjJ -fgNZ3SODPMkQoh8Pah1E5Hb83umGBPvt4MN2bfm8wWmLEjpU93xiJa2uN8Jzx5Mb -K3hs4JcPtjJ2m2x0aDt1RGFhllEl0l5tOQg+cKy9jnqA/O1wmMOXsFgYS5Fhg1Me -o70kQ6YRH+pBgnP7Pgcu4X0= ------END PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIC+zCCAeOgAwIBAgIJAPHrk7xP1wNXMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV -BAMMCWxvY2FsaG9zdDAeFw0xNjAxMjEyMjM3MDdaFw0xNjAyMjAyMjM3MDdaMBQx -EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAPNxfeH8yap5iiO3epWGBxXw3AClsFivqIuabHfzhrLKRq7/SV9w3Y0ai+h/ -AxIbKoWibjZ3oaAUsQm5o+JD/9Fkro1CXbDVfbA/9SfhIRPkh3iwwFKFHbNL/kfs -aBKWCrBSop6Cd6Xfjxv8DZhx0B3iJD3vIHEg3FuRyI393wvaiYjh62t4P/KCGpfl -Y3ZQf42hKDsV2m+AEiFowqyoL28SpuBdogWveMMlWPITLjAF+8UpOAQAi8eETYHo -HX+KPd4mTRkqm5vcO7aB6RlOmpqNAvi+ITXCO/J2Mmr+LEvZ2verf4qj/jGXRstT -yvr7/QKSQWczEw/b6anjFVP24zcCAwEAAaNQME4wHQYDVR0OBBYEFHB5zTo/9cg+ -8kTzB3WDwATror8YMB8GA1UdIwQYMBaAFHB5zTo/9cg+8kTzB3WDwATror8YMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACH3jlYfLBR0kGALkJSq9tRI -fB6sjjVOFrZtCt5sOqxh9We3wvMsr2TPAuEVEhiPQ1w1U4x4DTQU6A65N1KOj9Le -anjjlyjNSTqXvNUbxp0wh8x4LJ8DtmsfdXXYp3LsColBmh7JQn/2TL687B9tBXiq -p3P6wkuIOHX45UOQ0kvD4wRf3t+8mL4TmrCH4YHqzbcjI1KXGDfmVQ7i64nYrl3z -v7UCA6Xh0MShv3c0AtX1ccsxObEwjJzWA30zYUQtIWxXpRtLlkiflFU8ak1HzmBU -AHsg1T1KI1012+a1j/LWkKqb91EpUjC/DlaHz8zDOH/S1IEWM6UFjwKYnHLPxoM= ------END CERTIFICATE----- - diff --git a/fakenet/listeners/ssl_utils/server.pem b/fakenet/listeners/ssl_utils/server.pem deleted file mode 100644 index aff78bd3..00000000 --- a/fakenet/listeners/ssl_utils/server.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC+zCCAeOgAwIBAgIJAPHrk7xP1wNXMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV -BAMMCWxvY2FsaG9zdDAeFw0xNjAxMjEyMjM3MDdaFw0xNjAyMjAyMjM3MDdaMBQx -EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAPNxfeH8yap5iiO3epWGBxXw3AClsFivqIuabHfzhrLKRq7/SV9w3Y0ai+h/ -AxIbKoWibjZ3oaAUsQm5o+JD/9Fkro1CXbDVfbA/9SfhIRPkh3iwwFKFHbNL/kfs -aBKWCrBSop6Cd6Xfjxv8DZhx0B3iJD3vIHEg3FuRyI393wvaiYjh62t4P/KCGpfl -Y3ZQf42hKDsV2m+AEiFowqyoL28SpuBdogWveMMlWPITLjAF+8UpOAQAi8eETYHo -HX+KPd4mTRkqm5vcO7aB6RlOmpqNAvi+ITXCO/J2Mmr+LEvZ2verf4qj/jGXRstT -yvr7/QKSQWczEw/b6anjFVP24zcCAwEAAaNQME4wHQYDVR0OBBYEFHB5zTo/9cg+ -8kTzB3WDwATror8YMB8GA1UdIwQYMBaAFHB5zTo/9cg+8kTzB3WDwATror8YMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACH3jlYfLBR0kGALkJSq9tRI -fB6sjjVOFrZtCt5sOqxh9We3wvMsr2TPAuEVEhiPQ1w1U4x4DTQU6A65N1KOj9Le -anjjlyjNSTqXvNUbxp0wh8x4LJ8DtmsfdXXYp3LsColBmh7JQn/2TL687B9tBXiq -p3P6wkuIOHX45UOQ0kvD4wRf3t+8mL4TmrCH4YHqzbcjI1KXGDfmVQ7i64nYrl3z -v7UCA6Xh0MShv3c0AtX1ccsxObEwjJzWA30zYUQtIWxXpRtLlkiflFU8ak1HzmBU -AHsg1T1KI1012+a1j/LWkKqb91EpUjC/DlaHz8zDOH/S1IEWM6UFjwKYnHLPxoM= ------END CERTIFICATE----- diff --git a/setup.py b/setup.py index 7edfb394..f9394cb0 100644 --- a/setup.py +++ b/setup.py @@ -36,8 +36,8 @@ ], package_dir={'fakenet': 'fakenet'}, package_data={'fakenet': ['*.pem','diverters/*.py', 'listeners/*.py', - 'listeners/ssl_utils/*.py', 'listeners/ssl_utils/*.pem', 'configs/*.ini', 'defaultFiles/*', - 'lib/64/*', 'lib/32/*']}, + 'listeners/ssl_utils/*.py', 'configs/*.crt', 'configs/*.key', + 'configs/*.ini', 'defaultFiles/*', 'lib/64/*', 'lib/32/*']}, entry_points={ "console_scripts": [ "fakenet=fakenet.fakenet:main", diff --git a/test/test.py b/test/test.py index 0f0e4f68..5391102d 100644 --- a/test/test.py +++ b/test/test.py @@ -20,10 +20,12 @@ import requests import netifaces import subprocess +import ssl import irc.client import configparser from collections import OrderedDict from icmplib import ping +from requests.adapters import HTTPAdapter logger = logging.getLogger('FakeNetTests') logging.basicConfig(format='%(message)s', level=logging.INFO) @@ -593,9 +595,20 @@ def _test_irc(self, hostname, port=6667): return irc_tester.test_irc() def _test_http(self, hostname, port=None, scheme=None, uri=None, - teststring=None): + teststring=None, verify=False): """Test HTTP Listener""" + + # https://stackoverflow.com/a/50215614 + # HACK to force requests to use Windows Certificate store + class SSLContextAdapter(HTTPAdapter): + def init_poolmanager(self, *args, **kwargs): + ctx = ssl.create_default_context() + ctx.load_default_certs() + kwargs['ssl_context'] = ctx + return super(SSLContextAdapter, self).init_poolmanager(*args, **kwargs) + retval = False + sess = requests.Session() scheme = scheme if scheme else 'http' uri = uri.lstrip('/') if uri else 'asdf.html' @@ -606,8 +619,12 @@ def _test_http(self, hostname, port=None, scheme=None, uri=None, else: url = '%s://%s/%s' % (scheme, hostname, uri) + if (scheme == "https") and verify and sys.platform.startswith('win32'): + adapter = SSLContextAdapter() + sess.mount(url, adapter) + try: - r = requests.get(url, timeout=3) + r = sess.get(url, timeout=3, verify=verify) if r.status_code != 200: raise FakeNetTestException('Status code %d' % (r.status_code)) @@ -620,6 +637,12 @@ def _test_http(self, hostname, port=None, scheme=None, uri=None, except requests.exceptions.Timeout as e: pass + except requests.exceptions.SSLError as e: + pass + + except ValueError as e: + pass + except FakeNetTestException as e: pass @@ -769,14 +792,9 @@ def testGeneral(self, matchspec=[]): t['DNS listener test'] = (self._test_ns, (domain_dne, dns_expected), True) t['HTTP listener test'] = (self._test_http, (arbitrary,), True) - # Enable HTTPS when we have either added Server Name Indication and Dynamic CA or have modified `_test_http` to - # Ignore certificate issues. Here is the error that arises otherwise. - # Starting new HTTPS connection (1): 8.8.8.8 - # Test HTTP listener test with SSL: Uncaught exception of type : [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed - # Starting new HTTPS connection (1): 8.8.8.8 - # Test HTTP listener test with SSL: Uncaught exception of type : [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed - # [!!!] FAILED: HTTP listener test with SSL - # t['HTTP listener test with SSL'] = (self._test_http, (arbitrary, None, 'https'), True) + t['HTTPS listener IP test, no verify'] = (self._test_http, (arbitrary, None, 'https', None, None, False), True) + t['HTTPS listener IP test'] = (self._test_http, (arbitrary, None, 'https', None, None, True), False) + t['HTTPS listener hostname test'] = (self._test_http, ('evil.com', None, 'https', 'test.html', None, True), True) t['HTTP custom test by URI'] = (self._test_http, (arbitrary, None, None, '/test.txt', 'Wraps this'), True) t['HTTP custom test by hostname'] = (self._test_http, ('some.random.c2.com', None, None, None, 'success'), True) t['HTTP custom test by both URI and hostname'] = (self._test_http, ('both_host.com', None, None, '/and_uri.txt', 'Ahoy'), True)