New upstream snapshot.
Kali Janitor
2 years ago
0 | # http://editorconfig.org | |
1 | root = true | |
2 | ||
3 | [*] | |
4 | indent_style = space | |
5 | indent_size = 4 | |
6 | end_of_line = crlf | |
7 | charset = utf-8 | |
8 | trim_trailing_whitespace = true | |
9 | insert_final_newline = true | |
10 | ||
11 | [*.md] | |
12 | trim_trailing_whitespace = false |
0 | 0 | The MIT License (MIT) |
1 | 1 | |
2 | Copyright (c) 2016 Dirk-jan | |
2 | Copyright (c) 2020 Dirk-jan Mollema | |
3 | 3 | |
4 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | 5 | of this software and associated documentation files (the "Software"), to deal |
0 | Metadata-Version: 1.0 | |
1 | Name: ldapdomaindump | |
2 | Version: 0.9.3 | |
3 | Summary: Active Directory information dumper via LDAP | |
4 | Home-page: https://github.com/dirkjanm/ldapdomaindump/ | |
5 | Author: Dirk-jan Mollema | |
6 | Author-email: [email protected] | |
7 | License: UNKNOWN | |
8 | Description: UNKNOWN | |
9 | Platform: UNKNOWN |
24 | 24 | - *domain_computers_by_os*: Domain computers sorted by Operating System |
25 | 25 | |
26 | 26 | ## Dependencies and installation |
27 | Requires [ldap3](https://github.com/cannatag/ldap3) > 2.0 and [dnspython](https://github.com/rthalley/dnspython) | |
27 | Requires [ldap3](https://github.com/cannatag/ldap3) > 2.0, [dnspython](https://github.com/rthalley/dnspython) and [future](https://python-future.org/). ldapdomaindump runs on both python 2 and 3. | |
28 | 28 | |
29 | Both can be installed with `pip install ldap3 dnspython` | |
29 | Dependencies can be installed manually with `pip install ldap3 dnspython future`, but should in most cases be handled by pip when you install the main package either from git or pypi. | |
30 | 30 | |
31 | 31 | The ldapdomaindump package can be installed with `python setup.py install` from the git source, or for the latest release with `pip install ldapdomaindump`. |
32 | 32 | |
99 | 99 | |
100 | 100 | ## Visualizing groups with BloodHound |
101 | 101 | LDAPDomainDump includes a utility that can be used to convert ldapdomaindumps `.json` files to CSV files suitable for BloodHound. The utility is called `ldd2bloodhound` and is added to your path upon installation. Alternatively you can run it with `python -m ldapdomaindump.convert` or with `python ldapdomaindump/convert.py` if you are running it from the source. |
102 | The conversion tool will take the users/groups/computers/trusts `.json` file and convert those to `group_membership.csv` and `trust.csv` which you can add to BloodHound. | |
102 | The conversion tool will take the users/groups/computers/trusts `.json` file and convert those to `group_membership.csv` and `trust.csv` which you can add to BloodHound. *Note that these files are only compatible with **BloodHound 1.x** which is quite old. There are no plans to support the latest version as the [BloodHound.py project](https://github.com/fox-it/BloodHound.py) was made for this. With the DCOnly collection method this tool will also only talk to LDAP and collect more information than ldapdomaindump would*. | |
103 | ||
104 | ## Visualizing dump with a pretty output like enum4linux | |
105 | LDAPDomainDump includes a utility that can be used to output ldapdomaindumps `.json` files to an enum4linux like output. The utility is called `ldd2pretty` and is added to your path upon installation. Alternatively you can run it with `python -m ldapdomaindump.pretty` or with `python ldapdomaindump/pretty.py` if you are running it from the source. | |
103 | 106 | |
104 | 107 | ## License |
105 | 108 | MIT |
0 | python-ldapdomaindump (0.9.3+git20210706.1.451ce37-0kali1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Kali Janitor <[email protected]> Fri, 16 Jul 2021 20:17:53 -0000 | |
5 | ||
0 | 6 | python-ldapdomaindump (0.9.1-0kali2) kali-dev; urgency=medium |
1 | 7 | |
2 | 8 | * Remove Python 2 package |
23 | 23 | from __future__ import unicode_literals |
24 | 24 | import sys, os, re, codecs, json, argparse, getpass, base64 |
25 | 25 | # import class and constants |
26 | from datetime import datetime | |
26 | from datetime import datetime, timedelta | |
27 | 27 | try: |
28 | 28 | from urllib.parse import quote_plus |
29 | 29 | except ImportError: |
47 | 47 | 'ACCOUNT_LOCKED':0x00000010, |
48 | 48 | 'PASSWD_NOTREQD':0x00000020, |
49 | 49 | 'PASSWD_CANT_CHANGE': 0x00000040, |
50 | 'PASSWORD_STORE_CLEARTEXT': 0x00000080, | |
50 | 51 | 'NORMAL_ACCOUNT': 0x00000200, |
51 | 52 | 'WORKSTATION_ACCOUNT':0x00001000, |
52 | 53 | 'SERVER_TRUST_ACCOUNT': 0x00002000, |
80 | 81 | 'TREAT_AS_EXTERNAL':0x00000040, |
81 | 82 | 'USES_RC4_ENCRYPTION':0x00000080, |
82 | 83 | 'CROSS_ORGANIZATION_NO_TGT_DELEGATION':0x00000200, |
84 | 'CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION':0x00000800, | |
83 | 85 | 'PIM_TRUST':0x00000400} |
84 | 86 | |
85 | 87 | # Domain trust direction |
111 | 113 | 'lockoutThreshold':'Lockout Threshold', |
112 | 114 | 'maxPwdAge':'Max password age', |
113 | 115 | 'minPwdAge':'Min password age', |
114 | 'minPwdLength':'Min password length'} | |
116 | 'minPwdLength':'Min password length', | |
117 | 'pwdHistoryLength':'Password history length', | |
118 | 'pwdProperties':'Password properties', | |
119 | 'ms-DS-MachineAccountQuota':'Machine Account Quota', | |
120 | 'flatName':'NETBIOS Domain name'} | |
115 | 121 | |
116 | 122 | MINIMAL_COMPUTERATTRIBUTES = ['cn', 'sAMAccountName', 'dNSHostName', 'operatingSystem', 'operatingSystemServicePack', 'operatingSystemVersion', 'lastLogon', 'userAccountControl', 'whenCreated', 'objectSid', 'description', 'objectClass'] |
117 | 123 | MINIMAL_USERATTRIBUTES = ['cn', 'name', 'sAMAccountName', 'memberOf', 'primaryGroupId', 'whenCreated', 'whenChanged', 'lastLogon', 'userAccountControl', 'pwdLastSet', 'objectSid', 'description', 'objectClass'] |
377 | 383 | except (LDAPAttributeError, LDAPCursorError): |
378 | 384 | ugroups = [] |
379 | 385 | #Add the user default group |
380 | ugroups.append(self.getGroupCnFromDn(self.groups_dnmap[user.primaryGroupId.value])) | |
386 | try: | |
387 | ugroups.append(self.getGroupCnFromDn(self.groups_dnmap[user.primaryGroupId.value])) | |
388 | # Sometimes we can't query this group or it doesn't exist | |
389 | except KeyError: | |
390 | pass | |
381 | 391 | for group in ugroups: |
382 | 392 | try: |
383 | 393 | groupsdict[group].append(user) |
430 | 440 | #In grouped view, don't include the memberOf property to reduce output size |
431 | 441 | self.userattributes_grouped = ['cn', 'name', 'sAMAccountName', 'whenCreated', 'whenChanged', 'lastLogon', 'userAccountControl', 'pwdLastSet', 'objectSid', 'description'] |
432 | 442 | self.groupattributes = ['cn', 'sAMAccountName', 'memberOf', 'description', 'whenCreated', 'whenChanged', 'objectSid'] |
433 | self.policyattributes = ['cn', 'lockOutObservationWindow', 'lockoutDuration', 'lockoutThreshold', 'maxPwdAge', 'minPwdAge', 'minPwdLength', 'pwdHistoryLength', 'pwdProperties'] | |
443 | self.policyattributes = ['distinguishedName', 'lockOutObservationWindow', 'lockoutDuration', 'lockoutThreshold', 'maxPwdAge', 'minPwdAge', 'minPwdLength', 'pwdHistoryLength', 'pwdProperties', 'ms-DS-MachineAccountQuota'] | |
434 | 444 | self.trustattributes = ['cn', 'flatName', 'securityIdentifier', 'trustAttributes', 'trustDirection', 'trustType'] |
435 | 445 | |
436 | 446 | #Escape HTML special chars |
445 | 455 | |
446 | 456 | #Convert password max age (in 100 nanoseconds), to days |
447 | 457 | def nsToDays(self, length): |
448 | return abs(length) * .0000001 / 86400 | |
458 | # ldap3 >= 2.6 returns timedelta | |
459 | if isinstance(length, timedelta): | |
460 | return length.total_seconds() / 86400 | |
461 | else: | |
462 | return abs(length) * .0000001 / 86400 | |
449 | 463 | |
450 | 464 | def nsToMinutes(self, length): |
451 | return abs(length) * .0000001 / 60 | |
465 | # ldap3 >= 2.6 returns timedelta | |
466 | if isinstance(length, timedelta): | |
467 | return length.total_seconds() / 60 | |
468 | else: | |
469 | return abs(length) * .0000001 / 60 | |
452 | 470 | |
453 | 471 | #Parse bitwise flags into a list |
454 | 472 | def parseFlags(self, attr, flags_def): |
473 | outflags = [] | |
474 | if attr is None or attr.value is None: | |
475 | return outflags | |
476 | for flag, val in iteritems(flags_def): | |
477 | if int(attr.value) & val: | |
478 | outflags.append(flag) | |
479 | return outflags | |
480 | ||
481 | #Parse bitwise trust direction - only one flag applies here, 0x03 overlaps | |
482 | def parseSingleFlag(self, attr, flags_def): | |
455 | 483 | outflags = [] |
456 | 484 | if attr is None: |
457 | 485 | return outflags |
458 | 486 | for flag, val in iteritems(flags_def): |
459 | if attr.value & val: | |
460 | outflags.append(flag) | |
461 | return outflags | |
462 | ||
463 | #Parse bitwise trust direction - only one flag applies here, 0x03 overlaps | |
464 | def parseTrustDirection(self, attr, flags_def): | |
465 | outflags = [] | |
466 | if attr is None: | |
467 | return outflags | |
468 | for flag, val in iteritems(flags_def): | |
469 | if attr.value == val: | |
487 | if int(attr.value) == val: | |
470 | 488 | outflags.append(flag) |
471 | 489 | return outflags |
472 | 490 | |
593 | 611 | return self.formatGroupsHtml(att.values) |
594 | 612 | #Primary group |
595 | 613 | if aname == 'primarygroupid': |
596 | return self.formatGroupsHtml([self.dd.groups_dnmap[att.value]]) | |
614 | try: | |
615 | return self.formatGroupsHtml([self.dd.groups_dnmap[att.value]]) | |
616 | except KeyError: | |
617 | return 'NOT FOUND!' | |
597 | 618 | #Pwd flags |
598 | 619 | if aname == 'pwdproperties': |
599 | 620 | return ', '.join(self.parseFlags(att, pwd_flags)) |
604 | 625 | if att.value == 0: |
605 | 626 | return 'DISABLED' |
606 | 627 | else: |
607 | return ', '.join(self.parseTrustDirection(att, trust_directions)) | |
628 | return ', '.join(self.parseSingleFlag(att, trust_directions)) | |
608 | 629 | if aname == 'trusttype': |
609 | return ', '.join(self.parseFlags(att, trust_type)) | |
630 | return ', '.join(self.parseSingleFlag(att, trust_type)) | |
610 | 631 | if aname == 'securityidentifier': |
611 | 632 | return format_sid(att.raw_values[0]) |
612 | 633 | if aname == 'minpwdage' or aname == 'maxpwdage': |
676 | 697 | if aname == 'member' or aname == 'memberof' and type(att.values) is list: |
677 | 698 | return self.formatGroupsGrep(att.values) |
678 | 699 | if aname == 'primarygroupid': |
679 | return self.formatGroupsGrep([self.dd.groups_dnmap[att.value]]) | |
700 | try: | |
701 | return self.formatGroupsGrep([self.dd.groups_dnmap[att.value]]) | |
702 | except KeyError: | |
703 | return 'NOT FOUND!' | |
680 | 704 | #Domain trust flags |
681 | 705 | if aname == 'trustattributes': |
682 | 706 | return ', '.join(self.parseFlags(att, trust_flags)) |
684 | 708 | if att.value == 0: |
685 | 709 | return 'DISABLED' |
686 | 710 | else: |
687 | return ', '.join(self.parseTrustDirection(att, trust_directions)) | |
711 | return ', '.join(self.parseSingleFlag(att, trust_directions)) | |
688 | 712 | if aname == 'trusttype': |
689 | return ', '.join(self.parseFlags(att, trust_type)) | |
713 | return ', '.join(self.parseSingleFlag(att, trust_type)) | |
690 | 714 | if aname == 'securityidentifier': |
691 | 715 | return format_sid(att.raw_values[0]) |
692 | 716 | #Pwd flags |
767 | 791 | if self.config.outputhtml: |
768 | 792 | html = self.generateHtmlTable(dd.users, self.userattributes, 'Domain users') |
769 | 793 | self.writeHtmlFile('%s.html' % self.config.usersfile, html) |
794 | if self.config.outputgrep: | |
795 | grepout = self.generateGrepList(dd.users, self.userattributes) | |
796 | self.writeGrepFile('%s.grep' % self.config.usersfile, grepout) | |
770 | 797 | if self.config.outputjson: |
771 | 798 | jsonout = self.generateJsonList(dd.users) |
772 | 799 | self.writeJsonFile('%s.json' % self.config.usersfile, jsonout) |
773 | if self.config.outputgrep: | |
774 | grepout = self.generateGrepList(dd.users, self.userattributes) | |
775 | self.writeGrepFile('%s.grep' % self.config.usersfile, grepout) | |
776 | 800 | |
777 | 801 | #Generate report with just a table of all computer accounts |
778 | 802 | def generateComputersReport(self, dd): |
779 | 803 | if self.config.outputhtml: |
780 | 804 | html = self.generateHtmlTable(dd.computers, self.computerattributes, 'Domain computer accounts') |
781 | 805 | self.writeHtmlFile('%s.html' % self.config.computersfile, html) |
806 | if self.config.outputgrep: | |
807 | grepout = self.generateGrepList(dd.computers, self.computerattributes) | |
808 | self.writeGrepFile('%s.grep' % self.config.computersfile, grepout) | |
782 | 809 | if self.config.outputjson: |
783 | 810 | jsonout = self.generateJsonList(dd.computers) |
784 | 811 | self.writeJsonFile('%s.json' % self.config.computersfile, jsonout) |
785 | if self.config.outputgrep: | |
786 | grepout = self.generateGrepList(dd.computers, self.computerattributes) | |
787 | self.writeGrepFile('%s.grep' % self.config.computersfile, grepout) | |
788 | 812 | |
789 | 813 | #Generate report with just a table of all computer accounts |
790 | 814 | def generateGroupsReport(self, dd): |
791 | 815 | if self.config.outputhtml: |
792 | 816 | html = self.generateHtmlTable(dd.groups, self.groupattributes, 'Domain groups') |
793 | 817 | self.writeHtmlFile('%s.html' % self.config.groupsfile, html) |
818 | if self.config.outputgrep: | |
819 | grepout = self.generateGrepList(dd.groups, self.groupattributes) | |
820 | self.writeGrepFile('%s.grep' % self.config.groupsfile, grepout) | |
794 | 821 | if self.config.outputjson: |
795 | 822 | jsonout = self.generateJsonList(dd.groups) |
796 | 823 | self.writeJsonFile('%s.json' % self.config.groupsfile, jsonout) |
797 | if self.config.outputgrep: | |
798 | grepout = self.generateGrepList(dd.groups, self.groupattributes) | |
799 | self.writeGrepFile('%s.grep' % self.config.groupsfile, grepout) | |
800 | 824 | |
801 | 825 | #Generate policy report |
802 | 826 | def generatePolicyReport(self, dd): |
803 | 827 | if self.config.outputhtml: |
804 | 828 | html = self.generateHtmlTable(dd.policy, self.policyattributes, 'Domain policy') |
805 | 829 | self.writeHtmlFile('%s.html' % self.config.policyfile, html) |
830 | if self.config.outputgrep: | |
831 | grepout = self.generateGrepList(dd.policy, self.policyattributes) | |
832 | self.writeGrepFile('%s.grep' % self.config.policyfile, grepout) | |
806 | 833 | if self.config.outputjson: |
807 | 834 | jsonout = self.generateJsonList(dd.policy) |
808 | 835 | self.writeJsonFile('%s.json' % self.config.policyfile, jsonout) |
809 | if self.config.outputgrep: | |
810 | grepout = self.generateGrepList(dd.policy, self.policyattributes) | |
811 | self.writeGrepFile('%s.grep' % self.config.policyfile, grepout) | |
812 | 836 | |
813 | 837 | #Generate policy report |
814 | 838 | def generateTrustsReport(self, dd): |
815 | 839 | if self.config.outputhtml: |
816 | 840 | html = self.generateHtmlTable(dd.trusts, self.trustattributes, 'Domain trusts') |
817 | 841 | self.writeHtmlFile('%s.html' % self.config.trustsfile, html) |
842 | if self.config.outputgrep: | |
843 | grepout = self.generateGrepList(dd.trusts, self.trustattributes) | |
844 | self.writeGrepFile('%s.grep' % self.config.trustsfile, grepout) | |
818 | 845 | if self.config.outputjson: |
819 | 846 | jsonout = self.generateJsonList(dd.trusts) |
820 | 847 | self.writeJsonFile('%s.json' % self.config.trustsfile, jsonout) |
821 | if self.config.outputgrep: | |
822 | grepout = self.generateGrepList(dd.trusts, self.trustattributes) | |
823 | self.writeGrepFile('%s.grep' % self.config.trustsfile, grepout) | |
824 | 848 | |
825 | 849 | #Some quick logging helpers |
826 | 850 | def log_warn(text): |
0 | from __future__ import print_function | |
1 | from builtins import str | |
2 | import argparse | |
3 | import json | |
4 | import os.path | |
5 | import re | |
6 | from time import strftime, gmtime | |
7 | ||
8 | class PrettyOuput(object): | |
9 | ||
10 | def d2b(self, a): | |
11 | tbin = [] | |
12 | while a: | |
13 | tbin.append(a % 2) | |
14 | a //= 2 | |
15 | ||
16 | t2bin = tbin[::-1] | |
17 | if len(t2bin) != 8: | |
18 | for x in range(6 - len(t2bin)): | |
19 | t2bin.insert(0, 0) | |
20 | return ''.join([str(g) for g in t2bin]) | |
21 | ||
22 | def convert(self, time): | |
23 | if isinstance(time, str): | |
24 | return time | |
25 | if time == 0: | |
26 | return "None" | |
27 | if time == -9223372036854775808: | |
28 | return "Not Set" | |
29 | sec = abs(time) // 10000000 | |
30 | days = sec // 86400 | |
31 | sec -= 86400*days | |
32 | hrs = sec // 3600 | |
33 | sec -= 3600*hrs | |
34 | mins = sec // 60 | |
35 | sec -= 60*mins | |
36 | result = "" | |
37 | if days > 1: | |
38 | result += "{0} days ".format(days) | |
39 | elif days == 1: | |
40 | result += "{0} day ".format(days) | |
41 | if hrs > 1: | |
42 | result += "{0} hours ".format(hrs) | |
43 | elif hrs == 1: | |
44 | result += "{0} hour ".format(hrs) | |
45 | if mins > 1: | |
46 | result += "{0} minutes ".format(mins) | |
47 | elif mins == 1: | |
48 | result += "{0} minute ".format(mins) | |
49 | return result | |
50 | ||
51 | def password_complexity(self, data): | |
52 | ||
53 | print(''' | |
54 | +-----------------------------------------+ | |
55 | | Password Policy Information | | |
56 | +-----------------------------------------+ | |
57 | ''') | |
58 | ||
59 | print("[+] Password Info for Domain:", data[0]['attributes']['dc'][0].upper()) | |
60 | print("\t[+] Minimum password length: ", data[0]['attributes']['instanceType'][0]) | |
61 | print("\t[+] Password history length:", data[0]['attributes']['pwdHistoryLength'][0]) | |
62 | ||
63 | password_properties = self.d2b(data[0]['attributes']['pwdProperties'][0]) | |
64 | print("\t[+] Password Complexity Flags:", password_properties) | |
65 | print("") | |
66 | print("\t\t[+] Domain Refuse Password Change:", password_properties[0]) | |
67 | print("\t\t[+] Domain Password Store Cleartext:", password_properties[1]) | |
68 | print("\t\t[+] Domain Password Lockout Admins:", password_properties[2]) | |
69 | print("\t\t[+] Domain Password No Clear Change:", password_properties[3]) | |
70 | print("\t\t[+] Domain Password No Anon Change:", password_properties[4]) | |
71 | print("\t\t[+] Domain Password Complex:", password_properties[5]) | |
72 | print("") | |
73 | print("\t[+] Maximum password age:", self.convert(data[0]['attributes']['maxPwdAge'][0])) | |
74 | print("\t[+] Minimum password age:", self.convert(data[0]['attributes']['minPwdAge'][0])) | |
75 | print("\t[+] Reset Account Lockout Counter:", self.convert(data[0]['attributes']['lockoutDuration'][0])) | |
76 | print("\t[+] Account Lockout Threshold:", data[0]['attributes']['lockoutThreshold'][0]) | |
77 | print("\t[+] Forced Log off Time:", self.convert(data[0]['attributes']['forceLogoff'][0])) | |
78 | ||
79 | def domain_info(self, data): | |
80 | print(''' | |
81 | +--------------------------------------+ | |
82 | | Getting Domain Sid For | | |
83 | +--------------------------------------+ | |
84 | ''') | |
85 | print('[+] Domain Name:', data[0]['attributes']['dc'][0]) | |
86 | print('Domain Sid:', data[0]['attributes']['objectSid'][0]) | |
87 | print('') | |
88 | return data[0]['attributes']['dc'][0] | |
89 | ||
90 | def user_info(self, users, dc): | |
91 | print(''' | |
92 | +------------------------+ | |
93 | | Users Infos | | |
94 | +------------------------+ | |
95 | ''') | |
96 | for user in users: | |
97 | desc = user['attributes'].get('description')[0] if user['attributes'].get('description') else "(null)" | |
98 | print("Account: " + dc + "\\" + user['attributes']['sAMAccountName'][0] + "\tName: " + user['attributes']['name'][0] + "\tDesc: " + desc) | |
99 | ||
100 | print("") | |
101 | for user in users: | |
102 | print("user:[" + user['attributes']['sAMAccountName'][0] + "]") | |
103 | print("") | |
104 | ||
105 | def groups_info(self, groups, dc): | |
106 | print(''' | |
107 | +------------------------+import os.path | |
108 | | Groups Infos | | |
109 | +------------------------+ | |
110 | ''') | |
111 | for group in groups: | |
112 | print("group:[" + group['attributes']['name'][0] + "]") | |
113 | ||
114 | for group in groups: | |
115 | if group['attributes'].get('member'): | |
116 | users = re.findall(r"^CN=([\w\s\-\_\{\}\.\$\#]+)", '\n'.join(group['attributes']['member']), re.M) | |
117 | if users: | |
118 | print("\n[+] Getting domain group memberships:") | |
119 | for user in users: | |
120 | if user == "S-1-5-11": | |
121 | user = "NT AUTHORITY\\Authenticated Users" | |
122 | elif user == "S-1-5-4": | |
123 | user = "NT AUTHORITY\\INTERACTIVE" | |
124 | elif user == "S-1-5-17": | |
125 | user = "NT AUTHORITY\\IUSR" | |
126 | print("Group '" + group['attributes']['name'][0] + "' has member: " + dc + "\\" + user) | |
127 | ||
128 | def main(): | |
129 | parser = argparse.ArgumentParser(description='LDAPDomainDump to pretty output like enum4linux.') | |
130 | ||
131 | #Main parameters | |
132 | parser.add_argument("-d", "--directory", help="The ldapdomaindump directory where the json files are saved. Required files: domain_users.json, domain_groups.json and domain_policy.json") | |
133 | args = parser.parse_args() | |
134 | ||
135 | if args.directory: | |
136 | with open(args.directory + '/domain_policy.json') as f: | |
137 | domain = json.load(f) | |
138 | with open(args.directory + '/domain_users.json') as f: | |
139 | users = json.load(f) | |
140 | with open(args.directory + '/domain_groups.json') as f: | |
141 | groups = json.load(f) | |
142 | pretty = PrettyOuput() | |
143 | dc = pretty.domain_info(domain) | |
144 | pretty.password_complexity(domain) | |
145 | pretty.user_info(users, dc.upper()) | |
146 | pretty.groups_info(groups, dc.upper()) | |
147 | else: | |
148 | print("Missing parameter --directory /output/") | |
149 | ||
150 | if __name__ == "__main__": | |
151 | main() |
0 | Metadata-Version: 1.0 | |
1 | Name: ldapdomaindump | |
2 | Version: 0.9.3 | |
3 | Summary: Active Directory information dumper via LDAP | |
4 | Home-page: https://github.com/dirkjanm/ldapdomaindump/ | |
5 | Author: Dirk-jan Mollema | |
6 | Author-email: [email protected] | |
7 | License: UNKNOWN | |
8 | Description: UNKNOWN | |
9 | Platform: UNKNOWN |
0 | LICENSE | |
1 | MANIFEST.in | |
2 | Readme.md | |
3 | setup.py | |
4 | bin/ldapdomaindump | |
5 | bin/ldd2bloodhound | |
6 | bin/ldd2pretty | |
7 | ldapdomaindump/__init__.py | |
8 | ldapdomaindump/__main__.py | |
9 | ldapdomaindump/convert.py | |
10 | ldapdomaindump/pretty.py | |
11 | ldapdomaindump/style.css | |
12 | ldapdomaindump.egg-info/PKG-INFO | |
13 | ldapdomaindump.egg-info/SOURCES.txt | |
14 | ldapdomaindump.egg-info/dependency_links.txt | |
15 | ldapdomaindump.egg-info/requires.txt | |
16 | ldapdomaindump.egg-info/top_level.txt⏎ |
0 | ldapdomaindump |
0 | from setuptools import setup | |
1 | setup(name='ldapdomaindump', | |
2 | version='0.9.1', | |
3 | description='Active Directory information dumper via LDAP', | |
4 | author='Dirk-jan Mollema', | |
5 | author_email='[email protected]', | |
6 | url='https://github.com/dirkjanm/ldapdomaindump/', | |
7 | packages=['ldapdomaindump'], | |
8 | install_requires=['dnspython', 'ldap3==2.5.1', 'future'], | |
9 | package_data={'ldapdomaindump': ['style.css']}, | |
10 | include_package_data=True, | |
11 | scripts=['bin/ldapdomaindump', 'bin/ldd2bloodhound'] | |
12 | ) | |
0 | from setuptools import setup | |
1 | setup(name='ldapdomaindump', | |
2 | version='0.9.3', | |
3 | description='Active Directory information dumper via LDAP', | |
4 | author='Dirk-jan Mollema', | |
5 | author_email='[email protected]', | |
6 | url='https://github.com/dirkjanm/ldapdomaindump/', | |
7 | packages=['ldapdomaindump'], | |
8 | install_requires=['dnspython', 'ldap3>=2.5,!=2.5.2,!=2.5.0,!=2.6', 'future'], | |
9 | package_data={'ldapdomaindump': ['style.css']}, | |
10 | include_package_data=True, | |
11 | scripts=['bin/ldapdomaindump', 'bin/ldd2bloodhound', 'bin/ldd2pretty'] | |
12 | ) |