def pyopenssl_check_hostname(cert, hostname): ''' Return True if valid. False is invalid ''' general_names = SubjectAltName() if cert.get_subject().commonName: for idx in range(0, cert.get_extension_count()): ext = cert.get_extension(idx) if ext.get_short_name() == "subjectAltName": san = ext.get_data() decoded_san = der_decoder.decode(san, asn1Spec=general_names) for name in decoded_san: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) val = str(component.getComponent()) if component.getName( ) == 'dNSName' and val.startswith('*'): # Wilcard if val[2:] == hostname.split('.', 1)[1]: # print 'OK - wilcard' return True # Normal hostnames elif component.getName( ) == 'dNSName' and val == hostname: # print 'OK - normal' return True else: return False
def get_subj_alt_name(peer_cert): # Search through extensions dns_name = [] if not SUBJ_ALT_NAME_SUPPORT: return dns_name general_names = SubjectAltName() for i in range(peer_cert.get_extension_count()): ext = peer_cert.get_extension(i) ext_name = ext.get_short_name() if ext_name != 'subjectAltName': continue # PyOpenSSL returns extension data in ASN.1 encoded form ext_dat = ext.get_data() decoded_dat = der_decoder.decode(ext_dat, asn1Spec=general_names) for name in decoded_dat: if not isinstance(name, SubjectAltName): continue for entry in range(len(name)): component = name.getComponentByPosition(entry) if component.getName() != 'dNSName': continue dns_name.append(str(component.getComponent())) return dns_name
def _get_subj_alt_name(cls, peer_cert): '''Extract subjectAltName DNS name settings from certificate extensions @param peer_cert: peer certificate in SSL connection. subjectAltName settings if any will be extracted from this @type peer_cert: OpenSSL.crypto.X509 ''' # Search through extensions dns_name = [] general_names = SubjectAltName() for i in range(peer_cert.get_extension_count()): ext = peer_cert.get_extension(i) ext_name = ext.get_short_name() if ext_name == cls.SUBJ_ALT_NAME_EXT_NAME: # PyOpenSSL returns extension data in ASN.1 encoded form ext_dat = ext.get_data() decoded_dat = der_decoder.decode(ext_dat, asn1Spec=general_names) for name in decoded_dat: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) dns_name.append(str(component.getComponent())) return dns_name
def get_san(hostname, port, debug=False): """Gets Subject Alternative Names from requested host. Thanks to Cato- for this piece of code: https://gist.github.com/cato-/6551668""" subdomains = [] general_names = SubjectAltName() # Tries to connect to server, exits on error unless Traceback is requested try: cert = ssl.get_server_certificate((args.hostname, args.port)) except Exception as e: err = colored('FATAL: Could not connect to server.', 'white', 'on_red') print(err, end='\n') if debug: raise e exit(1) # requesting certificate x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) # get all extensions from certificate and iterate until we find a SAN entry for extension_id in range(0, x509.get_extension_count()): ext = x509.get_extension(extension_id) # decode the shortname to UTF-8 to be able to compare strings ext_name = ext.get_short_name().decode('utf-8') if ext_name == 'subjectAltName': # get_data() returns a heavily coded byte string ext_data = ext.get_data() decoded_dat = decoder.decode(ext_data, asn1Spec=general_names) # decoding SANS with magic sauce, please explain this to me. for name in decoded_dat: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) subdomains.append(str(component.getComponent())) # merge list results from crt.sh if found. if args.search_crt: crt_subdomains = search_crt(hostname) if crt_subdomains is not False: subdomains = list(subdomains) + list(crt_subdomains) # return a unique set of subdomains without wildcards filtered_domains = clean_san_list(subdomains, args.matching_domain) return set(sorted(filtered_domains))
def get_subj_alt_name(peer_cert): dns_name = [] general_names = SubjectAltName() for i in range(peer_cert.get_extension_count()): ext = peer_cert.get_extension(i) ext_name = ext.get_short_name() if ext_name == "subjectAltName": ext_dat = ext.get_data() decoded_dat = der_decoder.decode(ext_dat, asn1Spec=general_names) for name in decoded_dat: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) dns_name.append(str(component.getComponent())) return dns_name
def subject_alt_name(target, port=443): general_names = SubjectAltName() try: cert = ssl.get_server_certificate((target, port)) except: return x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) for extension_id in range(0, x509.get_extension_count()): ext = x509.get_extension(extension_id) ext_name = ext.get_short_name().decode('utf-8') if ext_name == 'subjectAltName': ext_data = ext.get_data() decoded_dat = decoder.decode(ext_data, asn1Spec=general_names) for name in decoded_dat: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) if str(component.getComponent()) not in subdomains: subdomains.append(str(component.getComponent())) print("Sub-domain found from subject_alt_name: " + str(component.getComponent()))
def getpeercert(self, binary_form=False): """ :return: The remote peer certificate in a tuple """ x509 = self.ssl_conn.get_peer_certificate() if not x509: raise ssl.SSLError('No peer certificate') if binary_form: return OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_ASN1, x509) dns_name = [] general_names = SubjectAltName() for i in xrange(x509.get_extension_count()): ext = x509.get_extension(i) ext_name = ext.get_short_name() if ext_name != 'subjectAltName': continue ext_dat = ext.get_data() decoded_dat = der_decoder(ext_dat, asn1Spec=general_names) for name in decoded_dat: if not isinstance(name, SubjectAltName): continue for entry in xrange(len(name)): component = name.getComponentByPosition(entry) if component.getName() != 'dNSName': continue dns_name.append(('DNS', str(component.getComponent()))) return { 'subject': ((('commonName', x509.get_subject().CN), ), ), 'subjectAltName': dns_name, 'notAfter': x509.get_notAfter() }
def get_subject_alternates(cert): """Get the subject alternates from a x509 object :param cert: x509 certificate object :returns: list of subject alternates residing on x509 object """ general_names = SubjectAltName() subject_alternates = [] for items in range(cert.get_extension_count()): ext = cert.get_extension(items) if ext.get_short_name() == 'subjectAltName': ext_dat = ext.get_data() decoded_dat = der_decoder.decode(ext_dat, asn1Spec=general_names) for name in decoded_dat: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) subject_alternates.append(str( component.getComponent())) return subject_alternates
def getSubAltName(peer_cert): ''' Performs inspection of the SSL cert :param peer_cert: The cert to inspect :return: dns_name - list of names found ''' dns_name = [] general_names = SubjectAltName() for i in range(peer_cert.get_extension_count()): extension = peer_cert.get_extension(i) extension_name = extension.get_short_name() if extension_name == "subjectAltName": extension_data = extension.get_data() decoded_data = der_decoder.decode(extension_data, asn1Spec=general_names) for name in decoded_data: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) dns_name.append(str(component.getComponent())) return dns_name
def print_cert(cert, printraw=False): #Certain there are better toolsets for this, but parse cert #subject and break it into the DN components that are important #if this fails, just dump the raw subject data as returned by #openssl try: certobj = crypto.load_certificate(crypto.FILETYPE_ASN1, cert) if printraw: print crypto.dump_certificate(crypto.FILETYPE_PEM, certobj) subject = certobj.get_subject() print 'CN={},OU={},O={},L={},S={},C={}'.format( subject.commonName, subject.organizationalUnitName, subject.organizationName, subject.localityName, subject.stateOrProvinceName, subject.countryName) #Get contents of subjectAltName extension extct = certobj.get_extension_count() # Code copied blatently from https://gist.github.com/cato-/6551668/raw/9d0c4d5e1ba16b92c4f4a18e74e460c097676785/verify_cert.py # and ndg.httpsclient.ssl_peer_verification.ServerSSLCertVerification general_names = SubjectAltName() for i in range(extct): ext = certobj.get_extension(i) if ext.get_short_name() == 'subjectAltName': data = der_decoder.decode(ext.get_data(), asn1Spec=general_names) for names in data: for entry in range(len(names)): component = names.getComponentByPosition(entry) print str(component.getComponent()) bstr = io.BytesIO(ext.get_data()) blen = len(bstr.read()) bstr.seek(0) val = '' print subject.get_components() except: print "Unable to parse certificate subject"
def scan_site(hostnames, match_domain, output, crtsh, timeout, suppress): """Scan domains from input or a text file, format is HOST[:PORT]. e.g: gsan scan domain1.com domain2.com:port You can also pass a text file instead, just replace the first domain argument for a file. eg: gsan scan filename.txt If no ports are defined, then gsan assumes the port 443 is available.""" subdomains_data = [] subjaltname = SubjectAltName() if isfile(hostnames[0]): with open(hostnames[0], "r") as host_file: hostnames = [host.rstrip("\n") for host in host_file] hostnames = [parse_host_port(host) for host in hostnames] else: hostnames = [parse_host_port(host) for host in hostnames] bad_hosts = [] for hostname in hostnames: click.secho(f"[+] Getting subdomains for {hostname[0]}", bold=True) subdomains = [] port = hostname[1] if hostname[1] else 443 setdefaulttimeout(timeout) try: cert = ssl.get_server_certificate((hostname[0], port)) except Exception: click.secho(f"[!] Unable to connect to host {hostname[0]}", bold=True, fg="red") bad_hosts.append(hostname[0]) continue # Thanks to Cato- for this piece of code: # https://gist.github.com/cato-/6551668 # get all extensions from certificate and iterate until we find a SAN entry x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) for extension_id in range(0, x509.get_extension_count()): ext = x509.get_extension(extension_id) ext_name = ext.get_short_name().decode("utf-8") if ext_name == "subjectAltName": ext_data = ext.get_data() decoded_dat = decoder.decode(ext_data, asn1Spec=subjaltname) for name in decoded_dat: if isinstance(name, SubjectAltName): for entry in range(len(name)): component = name.getComponentByPosition(entry) subdomains.append(str(component.getComponent())) subdomain_df = pd.Series(subdomains) if crtsh: crtsh_results = get_crtsh(hostname[0]) if crtsh_results.empty: pass else: subdomain_df = pd.concat([subdomain_df, crtsh_results]) if subdomains: subdomain_df = strip_chars(subdomain_df) if match_domain: subdomain_df = filter_domain(subdomain_df, hostname[0]) subdomain_df = reindex_df(subdomain_df) subdomains_data.append(subdomain_df) column_names = [name[0] for name in hostnames] for name in bad_hosts: column_names.remove(name) try: concat_df = concat_dfs(subdomains_data, column_names) except ValueError: click.secho(f"[!] No subdomains where found", bold=True, fg="yellow") else: if not suppress: click.secho("[+] Results:", bold=True) print(concat_df.to_string()) if output: dump_filename(output, concat_df) elif suppress and not output: filename = datetime.now().strftime( "subdomains-%Y-%m-%d-%H-%M-%S.csv") click.secho("[!] Suppress was active but no output was defined", bold=True, fg="yellow") click.secho(f"[!] Output was automatically generated", bold=True, fg="yellow") dump_filename(filename, concat_df) elif suppress and output: dump_filename(output, concat_df)