|
| 1 | +#!/usr/bin/env python3 |
| 2 | +import json |
| 3 | +from os import makedirs |
| 4 | +from certipy.commands.find import Find |
| 5 | +from certipy.lib.target import Target, DnsResolver |
| 6 | +from certipy.lib.formatting import pretty_print |
| 7 | +from datetime import datetime |
| 8 | + |
| 9 | +from nxc.helpers.misc import CATEGORY |
| 10 | +from nxc.paths import NXC_PATH |
| 11 | + |
| 12 | + |
| 13 | +class NXCModule: |
| 14 | + """Module made by: @NeffIsBack, @gatariee""" |
| 15 | + name = "certipy-find" |
| 16 | + description = "certipy find command with options to export the result to text/csv/json. Default: Show only vulnerable templates" |
| 17 | + supported_protocols = ["ldap"] |
| 18 | + category = CATEGORY.ENUMERATION |
| 19 | + |
| 20 | + def __init__(self, context=None, module_options=None): |
| 21 | + self.context = context |
| 22 | + self.module_options = module_options |
| 23 | + |
| 24 | + def options(self, context, module_options): |
| 25 | + """ |
| 26 | + VULN Show only vulnerable configurations (Default: True) |
| 27 | + ENABLED Show only enabled templates |
| 28 | +
|
| 29 | + Export options: |
| 30 | + TEXT Export results to a plain text file |
| 31 | + CSV Export results to a CSV file |
| 32 | + JSON Export results to a JSON file |
| 33 | + """ |
| 34 | + self.vuln = True |
| 35 | + self.enabled = False |
| 36 | + self.output_path = f"{NXC_PATH}/modules/certipy-find" |
| 37 | + self.json = False |
| 38 | + self.csv = False |
| 39 | + self.text = False |
| 40 | + |
| 41 | + if "VULN" in module_options: |
| 42 | + self.vuln = module_options["VULN"].lower() in ["true", "1", "yes"] |
| 43 | + if "ENABLED" in module_options: |
| 44 | + self.enabled = module_options["ENABLED"].lower() in ["true", "1", "yes"] |
| 45 | + |
| 46 | + # Export options |
| 47 | + if "JSON" in module_options: |
| 48 | + self.json = module_options["JSON"].lower() in ["true", "1", "yes"] |
| 49 | + if "CSV" in module_options: |
| 50 | + self.csv = module_options["CSV"].lower() in ["true", "1", "yes"] |
| 51 | + if "TEXT" in module_options: |
| 52 | + self.text = module_options["TEXT"].lower() in ["true", "1", "yes"] |
| 53 | + |
| 54 | + def on_login(self, context, connection): |
| 55 | + resolv = DnsResolver.create(connection.args.dns_server if connection.args.dns_server else connection.host) |
| 56 | + target = Target( |
| 57 | + resolver=resolv, |
| 58 | + domain=connection.domain, |
| 59 | + username=connection.username, |
| 60 | + password=connection.password, |
| 61 | + lmhash=connection.lmhash, |
| 62 | + nthash=connection.nthash, |
| 63 | + target_ip=connection.host, |
| 64 | + ldap_port=connection.port, |
| 65 | + ldap_scheme="ldaps" if connection.port == 636 else "ldap", |
| 66 | + ldap_signing=connection.signing_required, |
| 67 | + ldap_channel_binding=connection.cbt_status in ["Always", "When Supported"], |
| 68 | + ) |
| 69 | + |
| 70 | + finder = Find( |
| 71 | + target=target, |
| 72 | + json=self.json, |
| 73 | + csv=self.csv, |
| 74 | + text=self.text, |
| 75 | + output_path=self.output_path, |
| 76 | + stdout=True, |
| 77 | + vulnerable=self.vuln, |
| 78 | + enabled=self.enabled, |
| 79 | + ) |
| 80 | + |
| 81 | + # Get templates and CAs |
| 82 | + templates = finder.get_certificate_templates() |
| 83 | + cas = finder.get_certificate_authorities() |
| 84 | + finder._link_cas_and_templates(cas, templates) |
| 85 | + |
| 86 | + # Get OIDs |
| 87 | + oids = finder.get_issuance_policies() |
| 88 | + |
| 89 | + # Process information |
| 90 | + finder._link_templates_and_policies(templates, oids) |
| 91 | + finder._process_ca_properties(cas) |
| 92 | + finder._process_template_properties(templates) |
| 93 | + |
| 94 | + output = finder.get_output_for_text_and_json(templates, cas, oids) |
| 95 | + pretty_print(output, print_func=context.log.highlight) |
| 96 | + |
| 97 | + # Save to disk if any export option specified |
| 98 | + if self.json or self.csv or self.text: |
| 99 | + makedirs(self.output_path, exist_ok=True) |
| 100 | + |
| 101 | + filename = f"certipy_{connection.hostname}_{connection.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-") |
| 102 | + if self.json: |
| 103 | + with open(f"{self.output_path}/{filename}.json", "w") as f: |
| 104 | + json.dump( |
| 105 | + output, |
| 106 | + f, |
| 107 | + indent=2, |
| 108 | + default=str, |
| 109 | + ) |
| 110 | + if self.csv: |
| 111 | + template_output = finder.get_template_output_for_csv(output) |
| 112 | + ca_output = finder.get_ca_output_for_csv(output) |
| 113 | + with open(f"{self.output_path}/{filename}-templates.csv", "w") as f: |
| 114 | + f.write(template_output) |
| 115 | + with open(f"{self.output_path}/{filename}-cas.csv", "w") as f: |
| 116 | + f.write(ca_output) |
| 117 | + if self.text: |
| 118 | + with open(f"{self.output_path}/{filename}.txt", "w") as f: |
| 119 | + pretty_print(output, print_func=lambda x: f.write(x + "\n")) |
0 commit comments