def analyze(self, topic, data, cert): if topic != "scan": return False, None issuer_common_name = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.issuer.common_name') if issuer_common_name: issuer_common_name = issuer_common_name[0] else: return False, None subject_common_name = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.common_name') if subject_common_name: subject_common_name = subject_common_name[0] else: return False, None upper = False lower = False prefix_length = 0 dot = False banned = False incorrect = False for i in range(len(subject_common_name)): if subject_common_name[i] == '.': if dot: incorrect = True dot = True prefix_length = i elif subject_common_name[i].isupper() and not dot: upper = True elif subject_common_name[i].islower() and not dot: lower = True elif subject_common_name[i] == '-': banned = True correct_pattern = upper and lower and dot and prefix_length == 10 and not banned and not incorrect validity = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.validity.length') key_length = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject_key_info.rsa_public_key.length') if correct_pattern \ and issuer_common_name == subject_common_name \ and int(validity) == 31536000 \ and int(key_length) == 2048: allowed_hashes = { "108d4ee4b9f3cd5c0efba8af2dab5009", } if CSHash(cert) in allowed_hashes: return True, "cluster-3" return False, None
async def scan(producer): while True: # Continuously run new full IPv4 range scans logging.info('New scan started') proc = subprocess.Popen( "zmap -q --log-file=zmap.log -r 10000 --blacklist-file=config/blacklist.conf --sender-threads=1 --cores=0 -p 443 -n 100% -o - | ztee hosts.txt | ./bin/zgrab2 tls -o - --gomaxprocs=1 --senders=1000", shell=True, stdout=subprocess.PIPE) while True: # Continuously poll new certificates from scan raw = proc.stdout.readline() if raw == b'' and proc.poll() is not None: break if raw: # Handle new output line from zgrab date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") try: data = json.loads(raw) sha1 = deep_get( data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.fingerprint_sha1', None) if sha1: # Send certificate and metadata to Kafka body = {"date": date, "data": data, "sha1": sha1} producer.produce("scan", body) except Exception as e: logging.error(e) continue logging.info('Scan ended') time.sleep(600)
def analyze(self, topic, data, cert): if topic != "scan": return False, None validity = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.validity.length') issuer_dn = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.issuer_dn') if issuer_dn == "C=XX, L=Default City, O=Default Company Ltd" and int(validity) == 172800000: allowed_hashes = { "d29c030a2687b4e3364811e73700c523", } if CSHash(cert) in allowed_hashes: return True, "cluster-1" return False, None
async def __analyze(self): try: while True: msg = self.consumer.poll(timeout=1.0) if msg is None: continue if msg.error(): raise confluent_kafka.KafkaException(msg.error()) else: topic = msg.topic() message = json.loads(msg.value()) data = message['data'] date = message['date'] sha1 = message['sha1'] cert = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.raw', "") for module in self.modules: match, comment = module.analyze(topic, data, cert) if match: body = { "date": date, "sha1": sha1, "tag": module.tag, "comment": comment, } logging.info("Found match :") logging.info(str(body)) self.producer.produce("tags", body) except Exception as e: logging.error(e) finally: self.consumer.close()
def analyze(self, topic, data, cert): if topic != "scan": return False, None CN = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.common_name') C = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.country') if CN is None and C and C[0] == "US": allowed_hashes = { "23468ff8bd0e196cdc4fcff56cf8eb7e", } if CSHash(cert) in allowed_hashes: return True, "Powershell Empire C2" # or APfell actually return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None CN = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.common_name') OU = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.organizational_unit') EMAIL = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.email_address') if (CN and OU and EMAIL and EMAIL[0] == OU[0] + "@" + CN[0]): allowed_hashes = { "b432fd10cb96cd7c0d6d07d8ad2afd73", } if CSHash(cert) in allowed_hashes: return True, "Metasploit C2" return False, None
def analyze(self, topic, data, cert): if topic == "scan": subject_common_name = deep_get(data,'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.common_name', "") if subject_common_name: subject_common_name = subject_common_name[0] else: return False, None elif topic == "ct": subject_common_name = deep_get(data, 'leaf_cert.subject.CN', "") else: subject_common_name = "" suffixes = ['.ddns.net', '.spdns.org', '.duckdns.org', '.myddns.com'] for suffix in suffixes: length = len(subject_common_name) if len(suffix) < length and subject_common_name[length - len(suffix):] == suffix: return True, "cluster-1" return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None C = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.country') L = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.locality') ST = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.province') O = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.organization') OU = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.subject.organizational_unit') if (C and C[0]=='') or (L and L[0] == '') or (ST and ST[0] == '') or (O and O[0] == '') or (OU and OU[0] == ''): allowed_hashes = { "4f8c042aa2987ce4d06797a84b2f832d", } if CSHash(cert) in allowed_hashes: serial = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.serial_number') if int(serial) == 146473198: return True, "CobaltStrike Default Certificate" else: return True, "CobaltStrike C2" return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None issuer_dn = deep_get(data,'data.tls.result.handshake_log.server_certificates.certificate.parsed.issuer_dn') if issuer_dn == "C=XX, ST=1, L=1, O=1, OU=1, CN=*": allowed_hashes = { "b00e2855520f59644754e8bfa6dc1821", } if CSHash(cert) in allowed_hashes: return True, "cluster-1" return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None issuer_dn = deep_get(data,'data.tls.result.handshake_log.server_certificates.certificate.parsed.issuer_dn') if issuer_dn == "CN=localhost, C=AU, ST=Some-State, O=Internet Widgits Pty Ltd": allowed_hashes = { "3fbc3c90292240b7a5e5ff9a7130d59c", } if CSHash(cert) in allowed_hashes: return True, "cluster-4" return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None issuer_dn = deep_get(data,'data.tls.result.handshake_log.server_certificates.certificate.parsed.issuer_dn') if issuer_dn == "C=GB, ST=London, L=London, O=Global Security, OU=IT Department, CN=example.com": allowed_hashes = { "b00e2855520f59644754e8bfa6dc1821", "612c9021db95bd4323cbcd3d00fedca7", } if CSHash(cert) in allowed_hashes: return True, "cluster-1" return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None validity = deep_get(data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.validity.length', "0") if int(validity) == 315446400: allowed_hashes = { "1ce2b13bea04aaccc85e2725f8d1e7f4", } if CSHash(cert) in allowed_hashes: return True, "Covenant c2" return False, None
def analyze(self, topic, data, cert): if topic != "scan": return False, None issuer_dn = deep_get(data,'data.tls.result.handshake_log.server_certificates.certificate.parsed.issuer_dn') if issuer_dn == "O=FASTVPS, CN=parking": allowed_hashes = { "0a8940ab07f7dbfabc238c80edb05426", } if CSHash(cert) in allowed_hashes: return True, "cluster-1" return False, None
def main(bootstrap_servers, host, port, user, password): consumerConfiguration = { 'bootstrap.servers': bootstrap_servers, 'group.id': "elasticsearch", 'session.timeout.ms': 30000, 'auto.offset.reset': 'earliest' } consumer = confluent_kafka.Consumer(consumerConfiguration) consumer.subscribe(["scan", "ct", "tags"]) # Elasticsearch configuration es = Elasticsearch([{ 'host': host, 'port': port }], http_auth=(user, password), timeout=60) actions = [] try: while True: msg = consumer.poll(timeout=1.0) if msg is None: # no message received yet continue if msg.error(): raise confluent_kafka.KafkaException(msg.error()) else: topic = msg.topic() if topic == "scan": message = json.loads(msg.value()) data = message['data'] date = message['date'] sha1 = message['sha1'] raw = deep_get( data, 'data.tls.result.handshake_log.server_certificates.certificate.raw', "") tls_version = deep_get( data, 'data.tls.result.handshake_log.server_hello.version.value', "") tls_cipher_suite = deep_get( data, 'data.tls.result.handshake_log.server_hello.cipher_suite.hex', "") actions.append({ "_index": "certificates", "_id": sha1, "date": date, "sha1": sha1, "raw": raw, "scan": True, }) ip = deep_get(data, 'ip', "") md5 = deep_get( data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.fingerprint_md5', "") sha256 = deep_get( data, 'data.tls.result.handshake_log.server_certificates.certificate.parsed.fingerprint_sha256', "") actions.append({ "_index": "hosts_{date}".format(date=date), "ip": ip, "date": date, "md5": md5, "sha1": sha1, "sha256": sha256, "tls_version": tls_version, "tls_cipher_suite": tls_cipher_suite, }) elif topic == "ct": message = json.loads(msg.value()) data = message['data'] date = message['date'] sha1 = message['sha1'] try: issuer_common_name = data["chain"][0]["subject"]["CN"] except KeyError or IndexError: issuer_common_name = "" subject_common_name = deep_get(data, 'leaf_cert.subject.CN', "") raw = deep_get(data, 'leaf_cert.as_der', "") actions.append({ "_index": "certificates", "_id": sha1, "date": date, "sha1": sha1, "raw": raw, "ct": True, }) elif msg.topic() == "tags": message = json.loads(msg.value()) date = message['date'] sha1 = message['sha1'] tag = message['tag'] comment = message['comment'] actions.append({ "_index": "tags", "date": date, "sha1": sha1, "tag": tag, "comment": comment, }) if len(actions) > 1000: bulk(es, iter(actions)) actions = [] except KeyboardInterrupt: sys.stderr.write('Aborted by user\n') finally: consumer.close()