#!/usr/bin/env python3 # -*- coding: utf-8 -*- from binascii import unhexlify from ldap3.protocol.formatters.formatters import format_sid import argparse import datetime import json import ldap3 import logging import os import ssl import sys import hashlib import binascii ### Data utils def cast_to_dict(cid): out = {} for key, value in cid.items(): if type(value) == bytes: out[key] = str(value) elif type(value) == list: if len(value) == 1: value = value[0] if type(value) == bytes: out[key] = str(value) elif type(value) == datetime.datetime: out[key] = value.strftime('%Y-%m-%d %T') elif type(value) == datetime.timedelta: # Output format to change out[key] = value.seconds else: out[key] = value else: newlist = [] for element in value: if type(element) == bytes: newlist.append(str(element)) elif type(element) == datetime.datetime: newlist.append(element.strftime('%Y-%m-%d %T')) elif type(element) == datetime.timedelta: # Output format to change newlist.append(element.seconds) out[key] = newlist elif type(value) == datetime.datetime: out[key] = value.strftime('%Y-%m-%d %T') elif type(value) == datetime.timedelta: # Output format to change out[key] = value.seconds else: out[key] = value return out def dict_get_paths(d): paths = [] for key in d.keys(): if type(d[key]) == dict: paths = [[key]+p for p in dict_get_paths(d[key])] else: paths.append([key]) return paths def dict_path_access(d, path): for key in path: if key in d.keys(): d = d[key] else: return None return d class LDAPConsole(object): """docstring for LDAPConsole.""" def __init__(self, ldap_server, ldap_session, target_dn, debug=True): super(LDAPConsole, self).__init__() self.ldap_server = ldap_server self.ldap_session = ldap_session self.delegate_from = None self.target_dn = target_dn self.debug = debug def queryallusers(self, subtree, attributes=['*']): results = {} try: # https://ldap3.readthedocs.io/en/latest/searches.html#the-search-operation print (subtree+','+self.target_dn) self.ldap_session.search(subtree+','+self.target_dn, '(&(uid=*))' , attributes=attributes ) for entry in self.ldap_session.response: print(entry) for entry in self.ldap_session.response: if entry['type'] != 'searchResEntry': continue results[entry['dn']] = entry["attributes"] except ldap3.core.exceptions.LDAPInvalidFilterError as e: print("Invalid Filter. (ldap3.core.exceptions.LDAPInvalidFilterError)") except Exception as e: raise e return results def doLdapLogin(self,subtree, username, password): results = {} try: self.ldap_session.search(subtree+','+self.target_dn, '(&(uid='+username+'))' , attributes=['ntPassword'] ) for entry in self.ldap_session.response: if entry['type'] != 'searchResEntry': continue hash = hashlib.new('md4', password.encode('utf-16le')).digest() ntPassword = binascii.hexlify(hash).upper().decode("utf-8") #print (str(ntPassword)) print (entry['attributes']['ntPassword']) results['data'] = entry["attributes"] if entry['attributes']['ntPassword'] == ntPassword: results['authen'] = "yeah" else: results['authen'] = "none" except ldap3.core.exceptions.LDAPInvalidFilterError as e: print("Invalid Filter. (ldap3.core.exceptions.LDAPInvalidFilterError)") except Exception as e: raise e return results def init_ldap_connection(target, tls_version, args, username, password): if tls_version is not None: use_ssl = True port = 636 tls = ldap3.Tls(validate=ssl.CERT_NONE, version=tls_version) else: use_ssl = False port = 389 tls = None ldap_server = ldap3.Server(target, get_info=ldap3.ALL, port=port, use_ssl=use_ssl, tls=tls) ldap_session = ldap3.Connection(ldap_server, user=username, password=password, authentication='SIMPLE',auto_bind=True) return ldap_server, ldap_session def init_ldap_session(args, username, password): if args.dc_ip is not None: target = args.dc_ip if args.use_ldaps is True: try: return init_ldap_connection(target, ssl.PROTOCOL_TLSv1_2, args, username, password ) except ldap3.core.exceptions.LDAPSocketOpenError: return init_ldap_connection(target, ssl.PROTOCOL_TLSv1, args, username, password ) else: return init_ldap_connection(target, None, args, username, password ) def bytessize(data): l = len(data) units = ['B','kB','MB','GB','TB','PB'] for k in range(len(units)): if l < (1024**(k+1)): break return "%4.2f %s" % (round(l/(1024**(k)),2), units[k]) def parse_args(): parser = argparse.ArgumentParser(add_help=True, description='') parser.add_argument('--use-ldaps', action='store_true', help='Use LDAPS instead of LDAP') parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", default=False, help="show no information at all") parser.add_argument("-debug", dest="debug", action="store_true", default=False, help="Debug mode") parser.add_argument("-o", "--outfile", dest="jsonfile", default="ldap.json", help="Output JSON file. (default: ldap.json)") parser.add_argument("-b", "--base", dest="searchbase", default=None, help="Search base for LDAP query.") authconn = parser.add_argument_group('authentication & connection') authconn.add_argument('--dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller') authconn.add_argument("-u", "--user", dest="auth_username", metavar="USER", action="store", help="user to authenticate with") secret = parser.add_argument_group() cred = secret.add_mutually_exclusive_group() cred.add_argument("-p", "--password", dest="auth_password", metavar="PASSWORD", action="store", help="password to authenticate with") if len(sys.argv) == 1: parser.print_help() sys.exit(1) args = parser.parse_args() return args if __name__ == '__main__': args = parse_args() print("[+]================LDAP2JSON======================================[+]") print() ldap_server, ldap_session = init_ldap_session( args=args, username=args.auth_username, password=args.auth_password ) if args.debug: print("[>] Authentication successful!") print("[>] Extracting all objects from LDAP ...") data = {} baseDN = "dc=majornet,dc=local" if args.debug: print("[>] Querying to LDAP on %s ..." % baseDN) lc = LDAPConsole(ldap_server, ldap_session, baseDN, debug=args.debug) response = lc.queryallusers("ou=users", attributes=["displayName"]) if args.debug: print("[>] LDAP query (objectClass=*) returned %d objects" % len(response)) if args.debug: print("[>] Parsing data ...") for cn in response: path = cn.split(',')[::-1] tmp = data for key in path[:-1]: if key in tmp.keys(): tmp = tmp[key] else: tmp[key] = {} tmp = tmp[key] tmp[path[-1]] = cast_to_dict(response[cn]) json_data = json.dumps(data, indent=4) if args.debug: print("[>] JSON data generated.") print("[>] Writing json data to %s" % args.jsonfile) f = open(args.jsonfile, 'w') f.write(json_data) f.close() print("[>] Written %s bytes to %s" % (bytessize(json_data), args.jsonfile)) response = lc.doLdapLogin("ou=users", username='emorrone',password='morrone') print (response['authen']) response = lc.doLdapLogin("ou=users", username='emorrone',password='emorrone') print (response['authen']) ### ./bin/python ldap2json.py -u "cn=ldapadmin,dc=majornet,dc=local" -p "secret" --dc-ip "127.0.0.1" -b "dc=majornet,dc=local" -debug