Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added csv export option #152

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The `find` command is useful for enumerating AD CS certificate templates, certif
```
Certipy v4.0.0 - by Oliver Lyak (ly4k)

usage: certipy find [-h] [-debug] [-bloodhound] [-old-bloodhound] [-text] [-stdout] [-json] [-output prefix] [-enabled] [-dc-only] [-vulnerable] [-hide-admins] [-scheme ldap scheme] [-dc-ip ip address] [-target-ip ip address] [-target dns/ip address] [-ns nameserver] [-dns-tcp]
usage: certipy find [-h] [-debug] [-bloodhound] [-old-bloodhound] [-text] [-stdout] [-json] [-csv] [-output prefix] [-enabled] [-dc-only] [-vulnerable] [-hide-admins] [-scheme ldap scheme] [-dc-ip ip address] [-target-ip ip address] [-target dns/ip address] [-ns nameserver] [-dns-tcp]
[-timeout seconds] [-u username@domain] [-p password] [-hashes [LMHASH:]NTHASH] [-k] [-sspi] [-aes hex key] [-no-pass]

optional arguments:
Expand All @@ -86,6 +86,7 @@ output options:
-text Output result as text
-stdout Output result as text to stdout
-json Output result as JSON
-csv Output result as CSV
-output prefix Filename prefix for writing results to

find options:
Expand Down
71 changes: 71 additions & 0 deletions certipy/commands/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import struct
import time
import zipfile
import csv
from collections import OrderedDict
from datetime import datetime
from typing import List
Expand Down Expand Up @@ -84,6 +85,7 @@ def __init__(
self,
target: Target,
json: bool = False,
csv: bool = False,
bloodhound: bool = False,
old_bloodhound: bool = False,
text: bool = False,
Expand All @@ -100,6 +102,7 @@ def __init__(
):
self.target = target
self.json = json
self.csv = csv
self.bloodhound = bloodhound or old_bloodhound
self.old_bloodhound = old_bloodhound
self.text = text or stdout
Expand Down Expand Up @@ -476,6 +479,74 @@ def find(self):
"Saved JSON output to %s" % repr("%s_Certipy.json" % prefix)
)

if self.csv:
self.save_templates_to_csv(prefix, output)
logging.info(
"Saved CSV output to %s" % repr("%s_Certipy.csv" % prefix)
)

def save_templates_to_csv(
self, prefix: str, output: dict[LDAPEntry],
):
def flatten_dict(
template_entries: dict, parent_key:str='', sep:str='.',
):
items = []
for key, value in template_entries.items():
new_key = f"{parent_key}{sep}{key}" if parent_key else key
if isinstance(value, dict):
if "Permissions" in key:
for sub_key, sub_value in value.items():
if 'Enrollment Permissions' in sub_key:
items.append((sub_key, '\n'.join(sub_value['Enrollment Rights'])))
elif 'Object Control Permissions' in sub_key:
for subsub_key, subsub_value in sub_value.items():
if isinstance(subsub_value, list):
items.append((subsub_key, '\n'.join(subsub_value)))
else:
items.append((subsub_key, subsub_value))
else:
items.append((new_key, ", ".join([f"{key}: {value}" for key, value in value.items()])))
elif isinstance(value, list):
items.append((new_key, '\n'.join(value)))
else:
items.append((new_key, value))
return dict(items)

column_order = ['Template Name',
'Display Name',
'Certificate Authorities',
'Enabled',
'Client Authentication',
'Enrollment Agent',
'Any Purpose',
'Enrollee Supplies Subject',
'Certificate Name Flag',
'Enrollment Flag',
'Private Key Flag',
'Extended Key Usage',
'Requires Manager Approval',
'Requires Key Archival',
'Authorized Signatures Required',
'Validity Period',
'Renewal Period',
'Minimum RSA Key Length',
'Enrollment Permissions',
'Owner',
'Write Owner Principals',
'Write Dacl Principals',
'Write Property Principals',
'[!] Vulnerabilities']

with open("%s_Certipy.csv" % prefix, 'w', newline='') as csvfile:
writer = csv.DictWriter(csvfile,
fieldnames=column_order,
extrasaction='ignore',
delimiter=';',
quoting=csv.QUOTE_ALL)
writer.writeheader()
writer.writerows([flatten_dict(output['Certificate Templates'][id_]) for id_ in output['Certificate Templates']])

def get_output_for_text_and_json(
self, templates: List[LDAPEntry], cas: List[LDAPEntry]
):
Expand Down
5 changes: 5 additions & 0 deletions certipy/commands/parsers/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable
action="store_true",
help="Output result as JSON",
)
group.add_argument(
"-csv",
action="store_true",
help="Output result as CSV",
)
group.add_argument(
"-output",
action="store",
Expand Down