GithubHelp home page GithubHelp logo

ldapdomaindump's Introduction

LDAPDomainDump

Active Directory information dumper via LDAP

Introduction

In an Active Directory domain, a lot of interesting information can be retrieved via LDAP by any authenticated user (or machine). This makes LDAP an interesting protocol for gathering information in the recon phase of a pentest of an internal network. A problem is that data from LDAP often is not available in an easy to read format.

ldapdomaindump is a tool which aims to solve this problem, by collecting and parsing information available via LDAP and outputting it in a human readable HTML format, as well as machine readable json and csv/tsv/greppable files.

The tool was designed with the following goals in mind:

  • Easy overview of all users/groups/computers/policies in the domain
  • Authentication both via username and password, as with NTLM hashes (requires ldap3 >=1.3.1)
  • Possibility to run the tool with an existing authenticated connection to an LDAP service, allowing for integration with relaying tools such as impackets ntlmrelayx

The tool outputs several files containing an overview of objects in the domain:

  • domain_groups: List of groups in the domain
  • domain_users: List of users in the domain
  • domain_computers: List of computer accounts in the domain
  • domain_policy: Domain policy such as password requirements and lockout policy
  • domain_trusts: Incoming and outgoing domain trusts, and their properties

As well as two grouped files:

  • domain_users_by_group: Domain users per group they are member of
  • domain_computers_by_os: Domain computers sorted by Operating System

Dependencies and installation

Requires ldap3 > 2.0 and dnspython. ldapdomaindump requires Python 3.6 or greater.

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.

The ldapdomaindump package can be installed with python setup.py install from the git source, or for the latest release with pip install ldapdomaindump.

Usage

There are 3 ways to use the tool:

  • With just the source, run python ldapdomaindump.py
  • After installing, by running python -m ldapdomaindump
  • After installing, by running ldapdomaindump

Help can be obtained with the -h switch:

usage: ldapdomaindump.py [-h] [-u USERNAME] [-p PASSWORD] [-at {NTLM,SIMPLE}]
                         [-o DIRECTORY] [--no-html] [--no-json] [--no-grep]
                         [--grouped-json] [-d DELIMITER] [-r] [-n DNS_SERVER]
                         [-m]
                         HOSTNAME

Domain information dumper via LDAP. Dumps users/computers/groups and
OS/membership information to HTML/JSON/greppable output.

Required options:
  HOSTNAME              Hostname/ip or ldap://host:port connection string to
                        connect to (use ldaps:// to use SSL)

Main options:
  -h, --help            show this help message and exit
  -u USERNAME, --user USERNAME
                        DOMAIN\username for authentication, leave empty for
                        anonymous authentication
  -p PASSWORD, --password PASSWORD
                        Password or LM:NTLM hash, will prompt if not specified
  -at {NTLM,SIMPLE}, --authtype {NTLM,SIMPLE}
                        Authentication type (NTLM or SIMPLE, default: NTLM)

Output options:
  -o DIRECTORY, --outdir DIRECTORY
                        Directory in which the dump will be saved (default:
                        current)
  --no-html             Disable HTML output
  --no-json             Disable JSON output
  --no-grep             Disable Greppable output
  --grouped-json        Also write json files for grouped files (default:
                        disabled)
  -d DELIMITER, --delimiter DELIMITER
                        Field delimiter for greppable output (default: tab)

Misc options:
  -r, --resolve         Resolve computer hostnames (might take a while and
                        cause high traffic on large networks)
  -n DNS_SERVER, --dns-server DNS_SERVER
                        Use custom DNS resolver instead of system DNS (try a
                        domain controller IP)
  -m, --minimal         Only query minimal set of attributes to limit memmory
                        usage

Options

Authentication

Most AD servers support NTLM authentication. In the rare case that it does not, use --authtype SIMPLE.

Output formats

By default the tool outputs all files in HTML, JSON and tab delimited output (greppable). There are also two grouped files (users_by_group and computers_by_os) for convenience. These do not have a greppable output. JSON output for grouped files is disabled by default since it creates very large files without any data that isn't present in the other files already.

DNS resolving

An important option is the -r option, which decides if a computers DNSHostName attribute should be resolved to an IPv4 address. While this can be very useful, the DNSHostName attribute is not automatically updated. When the AD Domain uses subdomains for computer hostnames, the DNSHostName will often be incorrect and will not resolve. Also keep in mind that resolving every hostname in the domain might cause a high load on the domain controller.

Minimizing network and memory usage

By default ldapdomaindump will try to dump every single attribute it can read to disk in the .json files. In large networks, this uses a lot of memory (since group relationships are currently calculated in memory before being written to disk). To dump only the minimal required attributes (the ones shown by default in the .html and .grep files), use the --minimal switch.

Visualizing groups with BloodHound

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. 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 was made for this. With the DCOnly collection method this tool will also only talk to LDAP and collect more information than ldapdomaindump would.

Visualizing dump with a pretty output like enum4linux

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.

License

MIT

ldapdomaindump's People

Contributors

deni avatar dieresys avatar dirkjanm avatar fabaff avatar hackndo avatar iotmani avatar mpgn avatar neffisback avatar s-t-e-v-e-n-k avatar taufderl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ldapdomaindump's Issues

gid = int(group.objectSid.value.split('-')[-1]) TypeError: a bytes-like object is required, not 'str'

python3 ldapdomaindump.py ldap://10.10.73.150

[] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with
[
] Connecting to host...
[] Binding to host
[+] Bind OK
[
] Starting domain dump
Traceback (most recent call last):
File "/home/kali/tools/ldap/ldapdomaindump/ldapdomaindump.py", line 3, in
ldapdomaindump.main()
File "/home/kali/tools/ldap/ldapdomaindump/ldapdomaindump/init.py", line 956, in main
dd.domainDump()
File "/home/kali/tools/ldap/ldapdomaindump/ldapdomaindump/init.py", line 418, in domainDump
rw.generateUsersReport(self)
File "/home/kali/tools/ldap/ldapdomaindump/ldapdomaindump/init.py", line 801, in generateUsersReport
dd.mapGroupsIdsToDns()
File "/home/kali/tools/ldap/ldapdomaindump/ldapdomaindump/init.py", line 346, in mapGroupsIdsToDns
gid = int(group.objectSid.value.split('-')[-1])
TypeError: a bytes-like object is required, not 'str'

Deprecation warning due to invalid escape sequences

Deprecation warnings are raised due to invalid escape sequences. This can be fixed by using raw strings or escaping the literals. pyupgrade also helps in automatic conversion : https://github.com/asottile/pyupgrade/

find . -iname '*.py' | grep -v example | xargs -P4 -I{} python3.8 -Wall -m py_compile {}
./ldapdomaindump/pretty.py:126: DeprecationWarning: invalid escape sequence \I
  user = "NT AUTHORITY\IUSR"
./ldapdomaindump/pretty.py:124: DeprecationWarning: invalid escape sequence \I
  user = "NT AUTHORITY\INTERACTIVE"
./ldapdomaindump/pretty.py:122: DeprecationWarning: invalid escape sequence \A
  user = "NT AUTHORITY\Authenticated Users"

Dependencies in readme.md incorrect.

Readme.md specifies that the only dependencies required are ldap3 and dnspython.
You need to include the future package as well. It is correct in setup.py but not the readme.md file. If you could please update that'd be great.

Requests domain even when domain is specified

I get this error when trying to run command.

ldapdomaindump -u companydomain\admin -p thepassword -m ldaps://192.168.25.151:636

[!] Username must include a domain, use: DOMAIN\username

Attribute value can be NoneType

Hello,

Thanks for the great tool.
I've come across the following stack-trace, one or more attributes' value(s) appears to be NoneType:

Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Program Files\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__main__.py", line 3, in <module>
    ldapdomaindump.main()
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__init__.py", line 922, in main
    dd.domainDump()
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__init__.py", line 414, in domainDump
    rw.generateUsersReport(self)
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__init__.py", line 769, in generateUsersReport
    html = self.generateHtmlTable(dd.users, self.userattributes, 'Domain users')
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__init__.py", line 503, in generateHtmlTable
    of.append('<td>%s</td>' % self.formatAttribute(li[att], liIsGroup))
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__init__.py", line 591, in formatAttribute
    return ', '.join(self.parseFlags(att, uac_flags))
  File "C:\Program Files\Python37\lib\site-packages\ldapdomaindump\__init__.py", line 460, in parseFlags
    if attr.value & val:
TypeError: unsupported operand type(s) for &: 'NoneType' and 'int'

FWIW, I was running in -m minimal mode due to a large set of data.

Do not support special characters in passwords

Hello,
When using a password containing a special character like 'à é ù' throw the following error :

[*] Connecting to host...
[*] Binding to host
Traceback (most recent call last):
  File "/usr/local/bin/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump/__init__.py", line 885, in main
    if not c.bind():
  File "/usr/local/lib/python2.7/dist-packages/ldap3/core/connection.py", line 569, in bind
    response = self.do_ntlm_bind(controls)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/core/connection.py", line 1313, in do_ntlm_bind
    result['server_creds'])
  File "/usr/local/lib/python2.7/dist-packages/ldap3/operation/bind.py", line 81, in bind_operation
    server_creds = name.create_authenticate_message()
  File "/usr/local/lib/python2.7/dist-packages/ldap3/utils/ntlm.py", line 379, in create_authenticate_message
    nt_challenge_response = self.compute_nt_response()
  File "/usr/local/lib/python2.7/dist-packages/ldap3/utils/ntlm.py", line 485, in compute_nt_response
    response_key_nt = self.ntowf_v2()
  File "/usr/local/lib/python2.7/dist-packages/ldap3/utils/ntlm.py", line 496, in ntowf_v2
    password_digest = hashlib.new('MD4', self._password.encode('utf-16-le')).digest()
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

Command used : ldapdomaindump -u "frenchcompany\administrateur" -p "çékàcèheù" -o ldap-dump 192.168.1.1

I was able to get around this by generating and using the hash in the mean time.

Error with Domain Dump

Hi when I run the latest version I get this....

root@kali:/opt/ldapdomaindump# python ldapdomaindump.py -u "davy\rich" 192.168.226.10
Password:
[] Connecting to host...
[
] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
File "ldapdomaindump.py", line 3, in
ldapdomaindump.main()
File "/opt/ldapdomaindump/ldapdomaindump/init.py", line 922, in main
dd.domainDump()
File "/opt/ldapdomaindump/ldapdomaindump/init.py", line 411, in domainDump
self.policy = self.getDomainPolicy()
File "/opt/ldapdomaindump/ldapdomaindump/init.py", line 254, in getDomainPolicy
self.connection.search(self.root, '(objectClass=domain)', attributes=ldap3.ALL_ATTRIBUTES)
File "/usr/local/lib/python2.7/dist-packages/ldap3/core/connection.py", line 788, in search
response = self.post_send_search(self.send('searchRequest', request, controls))
File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/sync.py", line 139, in post_send_search
responses, result = self.get_response(message_id)
File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/base.py", line 325, in get_response
responses = self._get_response(message_id)
File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/sync.py", line 165, in _get_response
dict_response = self.decode_response_fast(ldap_resp)
File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/base.py", line 509, in decode_response_fast
result = search_result_entry_response_to_dict_fast(ldap_message['payload'], self.connection.server.schema, self.connection.server.custom_formatter, self.connection.check_names)
File "/usr/local/lib/python2.7/dist-packages/ldap3/operation/search.py", line 568, in search_result_entry_response_to_dict_fast
entry_dict['attributes'] = checked_attributes_to_dict_fast(response[1][3], schema, custom_formatter) # attributes
File "/usr/local/lib/python2.7/dist-packages/ldap3/operation/search.py", line 453, in checked_attributes_to_dict_fast
checked_attributes[name] = format_attribute_values(schema, name, decode_raw_vals_fast(attribute[3][1][3]) or [], custom_formatter)
File "/usr/local/lib/python2.7/dist-packages/ldap3/protocol/formatters/standard.py", line 213, in format_attribute_values
formatted_values = [formatter(raw_value) for raw_value in values] # executes formatter
File "/usr/local/lib/python2.7/dist-packages/ldap3/protocol/formatters/formatters.py", line 337, in format_ad_timedelta
return format_ad_timestamp(raw_value * -1) - format_ad_timestamp(0)
TypeError: unsupported operand type(s) for -: 'str' and 'datetime.datetime'

TypeError: bad operand type for abs(): 'str'

I am experiencing a problem that seems similar to #27. Please see below:

(venv) deni@NCCBook-Pro ldapdomaindump-0.9.4 % python ldapdomaindump.py 10.129.139.146 
[*] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with
[*] Connecting to host...
[*] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump.py", line 3, in <module>
    ldapdomaindump.main()
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump/__init__.py", line 950, in main
    dd.domainDump()
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump/__init__.py", line 427, in domainDump
    rw.generatePolicyReport(self)
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump/__init__.py", line 833, in generatePolicyReport
    html = self.generateHtmlTable(dd.policy, self.policyattributes, 'Domain policy')
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump/__init__.py", line 521, in generateHtmlTable
    of.append('<td>%s</td>' % self.formatAttribute(li[att], liIsGroup))
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump/__init__.py", line 639, in formatAttribute
    return '%.1f minutes' % self.nsToMinutes(att.value)
  File "/Users/deni/Documents/OSCP/Hack-the-Box/sauna/ldapdomaindump-0.9.4/ldapdomaindump/__init__.py", line 470, in nsToMinutes
    return abs(length) * .0000001 / 60
TypeError: bad operand type for abs(): 'str'

The Virtual Environment used is running Python 3.10.6. Wrapping length in float (return abs( float(length) ) * .0000001 / 60) seems to potentially fix the problem.

--Denis

MemoryError During handling of the above exception, another exception occurred:

MemoryError

During handling of the above exception, another exception occurred:

PS C:\Users\user\Downloads\ldapdomaindump-0.9.3> python .\ldapdomaindump.py DC3 -u domain\user -p pass
[] Connecting to host...
[
] Binding to host
[+] Bind OK
[*] Starting domain dump
MemoryError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "D:\Program Files\python3\lib\site-packages\ldap3\utils\asn1.py", line 121, in decode_sequence
ber_len, ber_value_offset = compute_ber_size(get_bytes(message[start: start + 10]))
MemoryError

During handling of the above exception, another exception occurred:

Active output for large domains

Dump fails against large domains due to memory issues. It would be nice to be able to dump data as it is parsed so that in the event that the user's machine runs out of memory that they still get some of the data.

Python error while trying to dump

Hello,
I tried to use the tool on an assesment and I got the following error :

[*] Connecting to host...
[*] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
  File "/usr/local/bin/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump/__init__.py", line 895, in main
    dd.domainDump()
  File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump/__init__.py", line 407, in domainDump
    self.policy = self.getDomainPolicy()
  File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump/__init__.py", line 250, in getDomainPolicy
    self.connection.search(self.root, '(objectClass=domain)', attributes=ldap3.ALL_ATTRIBUTES)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/core/connection.py", line 788, in search
    response = self.post_send_search(self.send('searchRequest', request, controls))
  File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/sync.py", line 139, in post_send_search
    responses, result = self.get_response(message_id)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/base.py", line 325, in get_response
    responses = self._get_response(message_id)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/sync.py", line 165, in _get_response
    dict_response = self.decode_response_fast(ldap_resp)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/strategy/base.py", line 509, in decode_response_fast
    result = search_result_entry_response_to_dict_fast(ldap_message['payload'], self.connection.server.schema, self.connection.server.custom_formatter, self.connection.check_names)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/operation/search.py", line 568, in search_result_entry_response_to_dict_fast
    entry_dict['attributes'] = checked_attributes_to_dict_fast(response[1][3], schema, custom_formatter)  # attributes
  File "/usr/local/lib/python2.7/dist-packages/ldap3/operation/search.py", line 453, in checked_attributes_to_dict_fast
    checked_attributes[name] = format_attribute_values(schema, name, decode_raw_vals_fast(attribute[3][1][3]) or [], custom_formatter)
  File "/usr/local/lib/python2.7/dist-packages/ldap3/protocol/formatters/standard.py", line 213, in format_attribute_values
    formatted_values = [formatter(raw_value) for raw_value in values]  # executes formatter
  File "/usr/local/lib/python2.7/dist-packages/ldap3/protocol/formatters/formatters.py", line 337, in format_ad_timedelta
    return format_ad_timestamp(raw_value * -1) - format_ad_timestamp(0)
TypeError: unsupported operand type(s) for -: 'str' and 'datetime.datetime'

The command I used : ldapdomaindump -u "frenchcompany\administrateur" -p aad3b435b51404eeaad3b435b51404ee:0000000000theNThash0000000000000 -o ldap-dump 192.168.1.1 -m

I have no clue where it could be from :(. I get the same behavior with or without the -m option.
Did someone saw this error already ?

How to use with Davmail?

Davmail provides a gateway between Microsoft's Exchange services to formats like Imap, Caldav, Carddav and LDAP. However, there's no username or password to enter. Leaving them blank gives you
KeyError: 'defaultnamingcontext'

Requirements too strict for ldap3 dependency

Hello @dirkjanm,

While installing the latest current impacket module, which relies on ldapdomaindump, I'm getting that error message:

$ pip install impacket
...
ldapdomaindump 0.9.1 has requirement ldap3==2.5.1, but you'll have ldap3 2.5.2 which is incompatible.
...

I think that you can safely change your requirements from

install_requires=['dnspython', 'ldap3==2.5.1', 'future'],

to

      install_requires=['dnspython', 'ldap3>=2.5.1', 'future'],

Best regards.

KeyError: 'defaultnamingcontext'

Hello,
I just install the tool with pip but i keep getting this error :

ldapdomaindump ldap://1.1.1.1:389                                      20:24:08
[*] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with
[*] Connecting to host...
[*] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
  File "/home/bonclay/.local/bin/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/home/bonclay/.local/lib/python2.7/site-packages/ldapdomaindump/__init__.py", line 892, in main
    dd = domainDumper(s, c, cnf)
  File "/home/bonclay/.local/lib/python2.7/site-packages/ldapdomaindump/__init__.py", line 158, in __init__
    self.root = self.getRoot()
  File "/home/bonclay/.local/lib/python2.7/site-packages/ldapdomaindump/__init__.py", line 171, in getRoot
    return self.server.info.other['defaultNamingContext'][0]
  File "/home/bonclay/.local/lib/python2.7/site-packages/ldap3/utils/ciDict.py", line 68, in __getitem__
    return self._store[self._case_insensitive_keymap[self._ci_key(key)]]
KeyError: 'defaultnamingcontext'

I read your post https://dirkjanm.io/active-directory-forest-trusts-part-one-how-does-sid-filtering-work/, and here is some questions I want to ask

here is my environment, I got a windows 7 x64 workstation in forest-a.local, and log on as [email protected], who is a member of "Enterprise Admins" and "Domain Admins" group in forest-a.local, below is the output of klist:
image

I have no idea how can I connect to server in forest-b.local, because I don't have a valid credential to connect with, I tried [email protected] and [email protected], but neither of them are right, am I supposed to use [email protected]? If so, I think my workstation would just send request to DC of forest-b.local, not DC of forest-a.local

[EXCEPTION] Tool throw exception while dumping

Hi,

I have run tool with expected arguments but after some time it throws an exception.

I guess it is due to unexpected output comes from DC as a result of used LDAP queries.

This is what I get:

[*] Connecting to host...
[*] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
  File "/home/abdulla/dassaq/bin/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldapdomaindump/__init__.py", line 944, in main
    dd.domainDump()
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldapdomaindump/__init__.py", line 416, in domainDump
    self.groups = self.getAllGroups()
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldapdomaindump/__init__.py", line 253, in getAllGroups
    self.connection.extend.standard.paged_search(self.root, '(objectClass=group)', attributes=ldap3.ALL_ATTRIBUTES, paged_size=500, generator=False)
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/extend/__init__.py", line 125, in paged_search
    paged_criticality)
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/extend/standard/PagedSearch.py", line 142, in paged_search_accumulator
    paged_criticality):
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/extend/standard/PagedSearch.py", line 68, in paged_search_generator
    None if cookie is True else cookie)
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/core/connection.py", line 839, in search
    response = self.post_send_search(self.send('searchRequest', request, controls))
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/strategy/sync.py", line 139, in post_send_search
    responses, result = self.get_response(message_id)
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/strategy/base.py", line 411, in get_response
    if self.do_search_on_auto_range(self._outstanding[message_id], response):
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/strategy/base.py", line 735, in do_search_on_auto_range
    self.do_next_range_search(request, resp, attr_name)
  File "/home/abdulla/dassaq/lib/python3.6/site-packages/ldap3/strategy/base.py", line 688, in do_next_range_search
    response['raw_attributes'][attr_type] += current_response['raw_attributes'][attr_name]
TypeError: list indices must be integers or slices, not str

image

AttributeError: 'list' object has no attribute 'replace'

Hi, I am getting the following error.
I am running ldapdomaindump in a Python 3.10.8 virtual environment. Installation was done with python setup.py install inside the virtual environment, and I used the following command (sensitive info replaced):

ldapdomaindump -u 'redacted.com\username' -p 'password' -d redacted.com -o loot ldaps://192.168.1.123
[*] Connecting to host...
[*] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
  File "/opt/ldapdomaindump/bin/ldapdomaindump", line 4, in <module>
    __import__('pkg_resources').run_script('ldapdomaindump==0.9.3', 'ldapdomaindump')
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/pkg_resources/__init__.py", line 672, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/pkg_resources/__init__.py", line 1472, in run_script
    exec(code, namespace, namespace)
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/EGG-INFO/scripts/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/ldapdomaindump/__init__.py", line 946, in main
    dd.domainDump()
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/ldapdomaindump/__init__.py", line 424, in domainDump
    rw.generateUsersReport(self)
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/ldapdomaindump/__init__.py", line 793, in generateUsersReport
    html = self.generateHtmlTable(dd.users, self.userattributes, 'Domain users')
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/ldapdomaindump/__init__.py", line 521, in generateHtmlTable
    of.append('<td>%s</td>' % self.formatAttribute(li[att], liIsGroup))
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/ldapdomaindump/__init__.py", line 644, in formatAttribute
    return self.htmlescape(self.formatString(att.value))
  File "/opt/ldapdomaindump/lib/python3.10/site-packages/ldapdomaindump-0.9.3-py3.10.egg/ldapdomaindump/__init__.py", line 449, in htmlescape
    return (html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("'", "&#39;").replace('"', "&quot;"))
AttributeError: 'list' object has no attribute 'replace'

Also tried with Python 3.9 and get the same error.

However, running the command on the latest Kali system installed ldapdomaindump shows the following, different error:
image

Thanks

out of memory

LDAP is too big,so when i dump it。 it's out of memory

[] Connecting to host...
[
] Binding to host
[+] Bind OK
[*] Starting domain dump
MemoryError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\Users\xxxxxx\AppData\Local\Programs\Python\Python37-32\lib\site-packages\ldap3\utils\asn1.py", line 121, in decode_sequence
ber_len, ber_value_offset = compute_ber_size(get_bytes(message[start: start + 10]))
MemoryError

During handling of the above exception, another exception occurred:

MemoryError

During handling of the above exception, another exception occurred:

MemoryError

During handling of the above exception, another exception occurred:

MemoryError

During handling of the above exception, another exception occurred:

MemoryError

During handling of the above exception, another exception occurred:

MemoryError

Retrieve Unix attributes

Hi,

It would be useful if the tool could retrieve Unix UID, GID, and default shell info per user, as well.

In Active Directory, the UID attribute is 'uidNumber', the GID attribute is 'gidNumber', and the shell attribute is 'loginShell'

Thanks

TypeError: a bytes-like object is required, not 'str'

Hi, I get the following exception when using the tool:
[*] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with [*] Connecting to host... [*] Binding to host [+] Bind OK [*] Starting domain dump Traceback (most recent call last): File "ldapdomaindump.py", line 3, in <module> ldapdomaindump.main() File "/root/Downloads/ldapdomaindump/ldapdomaindump/__init__.py", line 944, in main dd.domainDump() File "/root/Downloads/ldapdomaindump/ldapdomaindump/__init__.py", line 422, in domainDump rw.generateUsersReport(self) File "/root/Downloads/ldapdomaindump/ldapdomaindump/__init__.py", line 789, in generateUsersReport dd.mapGroupsIdsToDns() File "/root/Downloads/ldapdomaindump/ldapdomaindump/__init__.py", line 350, in mapGroupsIdsToDns gid = int(group.objectSid.value.split('-')[-1]) TypeError: a bytes-like object is required, not 'str'

The value where the exception is raised is the following:
[*] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with [*] Connecting to host... [*] Binding to host [+] Bind OK [*] Starting domain dump group: DN: CN=test,OU=Security Groups,DC=htb,DC=local - STATUS: Read - READ TIME: 2020-02-06T06:24:31.554167 cn: test dSCorePropagationData: 20200206113115.0Z 20200206113115.0Z 20200206113115.0Z 20200206113115.0Z 16010101000000.0Z distinguishedName: CN=test,OU=Security Groups,DC=htb,DC=local groupType: -2147483646 instanceType: 4 name: test objectCategory: CN=Group,CN=Schema,CN=Configuration,DC=htb,DC=local objectClass: top group objectGUID: b'c\x8fp\x02\xfc\x84\x8bN\xa3\xf5\xc9Ec\x15\xdat' objectSid: b'\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00,\x1e%\xb7\x15u\xb2\x15\xc5\xb0\xf3O\xed\x13\x00\x00' sAMAccountName: test sAMAccountType: 268435456
Tried with the following versions:
LDAP Version: 2.5.1-0kali1
ldapdomaindump: 0.9.1-0kali2 (preinstalled) and 0.9.2 (from GIT)

KeyError: 1040

Just tried ldapdomaindump and getting this error. I have tried it with and without --minimal and other options.
(I replaced real data here with generic)

ldapdomaindump -u domain\user -p "password" --minimal (IP of AD)
[] Connecting to host...
[
] Binding to host
[+] Bind OK
[*] Starting domain dump
Traceback (most recent call last):
File "/usr/local/bin/ldapdomaindump", line 4, in
import('pkg_resources').run_script('ldapdomaindump==0.9.1', 'ldapdomaindump')
File "/usr/local/lib/python2.7/dist-packages/pkg_resources/init.py", line 750, in run_script
self.require(requires)[0].run_script(script_name, ns)
File "/usr/local/lib/python2.7/dist-packages/pkg_resources/init.py", line 1527, in run_script
exec(code, namespace, namespace)
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump-0.9.1-py2.7.egg/EGG-INFO/scripts/ldapdomaindump", line 3, in
ldapdomaindump.main()
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump-0.9.1-py2.7.egg/ldapdomaindump/init.py", line 922, in main
dd.domainDump()
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump-0.9.1-py2.7.egg/ldapdomaindump/init.py", line 414, in domainDump
rw.generateUsersReport(self)
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump-0.9.1-py2.7.egg/ldapdomaindump/init.py", line 769, in generateUsersReport
html = self.generateHtmlTable(dd.users, self.userattributes, 'Domain users')
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump-0.9.1-py2.7.egg/ldapdomaindump/init.py", line 503, in generateHtmlTable
of.append('%s' % self.formatAttribute(li[att], liIsGroup))
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump-0.9.1-py2.7.egg/ldapdomaindump/init.py", line 597, in formatAttribute
return self.formatGroupsHtml([self.dd.groups_dnmap[att.value]])
KeyError: 1040

Error installing from source

Trying to install from the latest commit using:

python setup.py install

Results in:

running install
running bdist_egg
running egg_info
writing ldapdomaindump.egg-info/PKG-INFO
writing dependency_links to ldapdomaindump.egg-info/dependency_links.txt
writing requirements to ldapdomaindump.egg-info/requires.txt
writing top-level names to ldapdomaindump.egg-info/top_level.txt
reading manifest file 'ldapdomaindump.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'ldapdomaindump.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
installing scripts to build/bdist.linux-x86_64/egg/EGG-INFO/scripts
running install_scripts
running build_scripts
error: file '/root/tools/ldapdomaindump/bin/ldd2pretty' does not exist

It looks like ldd2pretty hasn't yet been added to bin

Groups visualization

Look into ways to visualize complex group structures, maybe with a separate utility or with a command line client to just get all memberships of a useraccount

Dependency on future

Do you still need to depend on future library? Do you still need to support Python 2? future will no longer work with Python 3.12 because imp module has been removed from stdlib and the situation will be even worse with 3.13 because the lib2to3 which is the core of the future will be removed from stdlib as well.

It should be simple to remove the dependency on future, it's a good idea to do it, and I can help you with that if you want to.

ldapdomaindump silently fails

I was troubleshooting a problem with ntlmrelayx.py, and I think that ldapdomaindump is the cause.

ntlmrelayx.py was never setting the global variable domainDumped = True because there was some silent error in the dump that was causing the function to return prematurely. Sure enough when I moved the line domainDumped = True to the top of the code (setting it before the domain dump was attempted) it works fine.

I looked quickly at the ldapdomaindump code and it seems like there are lots of try: blocks that return nothing on error. It would be nice to know why this is failing and at least get a little feedback for troubleshooting purposes.

Thanks for the awesome work.

Intermediate flush to disk

First of all thanks for the nice project. Would it be possible to implement some regular "disk flushes"? I tried to dump a slightly large AD today and - even with the -m option enabled - easily exhausted the 12GB RAM in my VM. Writing information to disk and clearing internal data structures from time to time will probably drastically reduce the memory consumption.

Converter for BloodHound 2.0

BloodHound 2.0 and newer uses JSON as format, while the convert utility still outputs CSV's.
I'm not sure how many people use this feature but it would be good to support the new format.

Output formatter error

Hi,

Microsoft establishes that the trustType attribute contains one of the following values:

  • TTD (TRUST_TYPE_DOWNLEVEL, 0x00000001)
  • TTU (TRUST_TYPE_UPLEVEL, 0x00000002)
  • TTM (TRUST_TYPE_MIT, 0x00000003)
  • TTDCE (TRUST_TYPE_DCE, 0x00000004)

But using the latest stable version of ldapdomaindump I'm getting two values at least:

image

Thanks for your support

Imports fail from `builtins`

from builtins import str, itervalues, iteritems

When trying to run convert.py I get ImportError: cannot import name itervalues (and also iteritems).

It seems perhaps these were taken out of builtins. They are available with the six library.

Updating the line linked above to this, seems to fix the problem.

Generating HTML Replace Issue and No HTML

This issue seems to have popped up since last week. I am not sure if there is a dependency error happening, but the HTML attribute keeps erroring out. I am not sure what is happening if I am honest. Running on the latest version of Kali.
2024-01-12 ldapdomaindumperror

TypeError: Missing required parameter 'digestmod'

Hi! I keep receiving the following error while running ldapdomaindump, do you have any idea what might be going wrong?

ldapdomaindump $target -u 'BLACKFIELD.local\support' -p '#00^BlackKnight'
[*] Connecting to host...
[*] Binding to host
Traceback (most recent call last):
  File "/usr/bin/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/usr/lib/python3/dist-packages/ldapdomaindump/__init__.py", line 912, in main
    if not c.bind():
  File "/usr/lib/python3/dist-packages/ldap3/core/connection.py", line 563, in bind
    response = self.do_ntlm_bind(controls)
  File "/usr/lib/python3/dist-packages/ldap3/core/connection.py", line 1302, in do_ntlm_bind
    request = bind_operation(self.version, 'SICILY_RESPONSE_NTLM', ntlm_client, result['server_creds'])
  File "/usr/lib/python3/dist-packages/ldap3/operation/bind.py", line 81, in bind_operation
    server_creds = name.create_authenticate_message()
  File "/usr/lib/python3/dist-packages/ldap3/utils/ntlm.py", line 379, in create_authenticate_message
    nt_challenge_response = self.compute_nt_response()
  File "/usr/lib/python3/dist-packages/ldap3/utils/ntlm.py", line 485, in compute_nt_response
    response_key_nt = self.ntowf_v2()
  File "/usr/lib/python3/dist-packages/ldap3/utils/ntlm.py", line 497, in ntowf_v2
    return hmac.new(password_digest, (self.user_name.upper() + self.user_domain).encode('utf-16-le')).digest()
  File "/usr/lib/python3.8/hmac.py", line 153, in new
    return HMAC(key, msg, digestmod)
  File "/usr/lib/python3.8/hmac.py", line 51, in __init__
    raise TypeError("Missing required parameter 'digestmod'.")
TypeError: Missing required parameter 'digestmod'.

UnicodeDecodeError

Hi to all,

I have tried to extract information from a LDAP server, but in one part I receive this error in a group property, I am not sure what it is, has anyone else had a similar problem?

[] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with
[
] Connecting to host...
[] Binding to host
[+] Bind OK
[
] Starting domain dump
Traceback (most recent call last):
File "/root/Tools/ldapdomaindump/ldapdomaindump.py", line 3, in
ldapdomaindump.main()
File "/root/Tools/ldapdomaindump/ldapdomaindump/init.py", line 950, in main
dd.domainDump()
File "/root/Tools/ldapdomaindump/ldapdomaindump/init.py", line 423, in domainDump
rw.generateUsersReport(self)
File "/root/Tools/ldapdomaindump/ldapdomaindump/init.py", line 795, in generateUsersReport
dd.mapGroupsIdsToDns()
File "/root/Tools/ldapdomaindump/ldapdomaindump/init.py", line 351, in mapGroupsIdsToDns
gid = int(group.objectSid.value.split('-')[-1])
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfb in position 13: ordinal not in range(128)

Thx

Loosen the version constraint of ldap3 to avoid dependency conflicts

Hi, ldapdomaindump locked the version constraint of ldap3 as ldap3==2.5.1, which leads to a troubling scenario that its direct downstream project impacket has to lock ldap3==2.5.1.

What makes the situation worse is that the downstream projects ['adidnsdump-1.1.0', 'bloodhound-0.7.0', 'boofuzz-0.1.5'] of impacket are also forced to lock this specific version ldap3==2.5.1 to avoid conflicts.

Could you please loosen the version constraint of ldap3?
Benefit of this is that users using both of impacket and ldap3 can upgrade their third party libraries in a timely manner to reduce technical debts.

Solution

The dependency trees of your project and affected downstream projects are shown as follows.
Taking the version constraints of upstream and downstream projects into comprehensive consideration, you can

  1. Loosen ldap3 to be ldap3>=2.5.1.
  2. Try to add an upper bound for ldap3’ version constraint, according to your compatibility.

@dirkjanm Please let me know your choice. I can submit a PR to fix this issue.

Thanks for your attention.
Best,
Neolith

Improve groups output

At the moment:

  • Groups don't show subgroups or parent groups
  • User memberships are not recursive
  • Groups don't include comments

This should be improved

unsupported operand type for bytes and datetime.datetime error

image

Traceback (most recent call last):
  File "/usr/local/bin/ldapdomaindump", line 3, in <module>
    ldapdomaindump.main()
  File "/usr/local/lib/python3.7/dist-packages/ldapdomaindump/__init__.py", line 944, in main
    dd.domainDump()
  File "/usr/local/lib/python3.7/dist-packages/ldapdomaindump/__init__.py", line 419, in domainDump
    self.policy = self.getDomainPolicy()
  File "/usr/local/lib/python3.7/dist-packages/ldapdomaindump/__init__.py", line 258, in getDomainPolicy
    self.connection.search(self.root, '(objectClass=domain)', attributes=ldap3.ALL_ATTRIBUTES)
  File "/usr/local/lib/python3.7/dist-packages/ldap3/core/connection.py", line 788, in search
    response = self.post_send_search(self.send('searchRequest', request, controls))
  File "/usr/local/lib/python3.7/dist-packages/ldap3/strategy/sync.py", line 139, in post_send_search
    responses, result = self.get_response(message_id)
  File "/usr/local/lib/python3.7/dist-packages/ldap3/strategy/base.py", line 325, in get_response
    responses = self._get_response(message_id)
  File "/usr/local/lib/python3.7/dist-packages/ldap3/strategy/sync.py", line 165, in _get_response
    dict_response = self.decode_response_fast(ldap_resp)
  File "/usr/local/lib/python3.7/dist-packages/ldap3/strategy/base.py", line 509, in decode_response_fast
    result = search_result_entry_response_to_dict_fast(ldap_message['payload'], self.connection.server.schema, self.connection.server.custom_formatter, self.connection.check_names)
  File "/usr/local/lib/python3.7/dist-packages/ldap3/operation/search.py", line 568, in search_result_entry_response_to_dict_fast
    entry_dict['attributes'] = checked_attributes_to_dict_fast(response[1][3], schema, custom_formatter)  # attributes
  File "/usr/local/lib/python3.7/dist-packages/ldap3/operation/search.py", line 453, in checked_attributes_to_dict_fast
    checked_attributes[name] = format_attribute_values(schema, name, decode_raw_vals_fast(attribute[3][1][3]) or [], custom_formatter)
  File "/usr/local/lib/python3.7/dist-packages/ldap3/protocol/formatters/standard.py", line 213, in format_attribute_values
    formatted_values = [formatter(raw_value) for raw_value in values]  # executes formatter
  File "/usr/local/lib/python3.7/dist-packages/ldap3/protocol/formatters/standard.py", line 213, in <listcomp>
    formatted_values = [formatter(raw_value) for raw_value in values]  # executes formatter
  File "/usr/local/lib/python3.7/dist-packages/ldap3/protocol/formatters/formatters.py", line 337, in format_ad_timedelta
    return format_ad_timestamp(raw_value * -1) - format_ad_timestamp(0)
TypeError: unsupported operand type(s) for -: 'bytes' and 'datetime.datetime'

Memory usage very high

Memory usage seems very high for large domains when dumping domain_users_by_group. Removing the redundant user memberships seems to fix it for HTML, but for json the problem persists.

Error when running ldapdomaindump

Can someone shed some light what is causing the error below? Appreciate any help!

[] Connecting to host...
[
] Binding to host
Traceback (most recent call last):
File "/usr/local/bin/ldapdomaindump", line 3, in
ldapdomaindump.main()
File "/usr/local/lib/python2.7/dist-packages/ldapdomaindump/init.py", line 940, in main
if not c.bind():
File "/usr/local/lib/python2.7/dist-packages/ldap3/core/connection.py", line 563, in bind
response = self.do_ntlm_bind(controls)
File "/usr/local/lib/python2.7/dist-packages/ldap3/core/connection.py", line 1302, in do_ntlm_bind
request = bind_operation(self.version, 'SICILY_RESPONSE_NTLM', ntlm_client, result['server_creds'])
File "/usr/local/lib/python2.7/dist-packages/ldap3/operation/bind.py", line 81, in bind_operation
server_creds = name.create_authenticate_message()
File "/usr/local/lib/python2.7/dist-packages/ldap3/utils/ntlm.py", line 379, in create_authenticate_message
nt_challenge_response = self.compute_nt_response()
File "/usr/local/lib/python2.7/dist-packages/ldap3/utils/ntlm.py", line 485, in compute_nt_response
response_key_nt = self.ntowf_v2()
File "/usr/local/lib/python2.7/dist-packages/ldap3/utils/ntlm.py", line 496, in ntowf_v2
password_digest = hashlib.new('MD4', self._password.encode('utf-16-le')).digest()
File "/usr/lib/python2.7/hashlib.py", line 116, in __py_new
return __get_builtin_constructor(name)(string)
File "/usr/lib/python2.7/hashlib.py", line 97, in __get_builtin_constructor
raise ValueError('unsupported hash type ' + name)
ValueError: unsupported hash type MD4

Bad operation type

] Connecting as anonymous user, dumping will probably fail. Consider specifying a username/password to login with
[
] Connecting to host...
[] Binding to host
[+] Bind OK
[
] Starting domain dump
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
"main", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/opt/scripts/ldapdomaindump/ldapdomaindump/main.py", line 3, in
ldapdomaindump.main()
File "ldapdomaindump/init.py", line 944, in main
dd.domainDump()
File "ldapdomaindump/init.py", line 425, in domainDump
rw.generatePolicyReport(self)
File "ldapdomaindump/init.py", line 827, in generatePolicyReport
html = self.generateHtmlTable(dd.policy, self.policyattributes, 'Domain policy')
File "ldapdomaindump/init.py", line 519, in generateHtmlTable
of.append('%s' % self.formatAttribute(li[att], liIsGroup))
File "ldapdomaindump/init.py", line 635, in formatAttribute
return '%.1f minutes' % self.nsToMinutes(att.value)
File "ldapdomaindump/init.py", line 468, in nsToMinutes
return abs(length) * .0000001 / 60
TypeError: bad operand type for abs(): 'unicode'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.