Open jakuta-tech opened 4 weeks ago
I made a lot of changes, and added a debug option to display the method names, can you update and try again ?
Sorry I tried running it on the same environment but it still didn't work with the current updates. There were permission errors in some GPO's which broke it. I put in try / except error handling and it allowed it to continue. When I have access I'll see if I can do more testing.
Can you test with administrator account ? Is your domain name entered in lower case ?
yep. Using the -s flag with ADcheck throws an error.
ADcheck -d 'domain' -u 'user' -p 'pass' --dc-ip '192.168.5.1' -s
ldap3.core.exceptions.LDAPSocketOpenError: invalid server address
I wrote a small python script below to test ldap connection and dump users from the DC.
python ldapcheck.py --username 'local.lab\admin' --password 'pass' --server ldaps://192.168.5.1 --base-dn 'DC=local,DC=lab'
Connection successful.
Connects and dumps all users and groups from the same DC.
import argparse
from ldap3 import Server, Connection, ALL, SUBTREE
import socket
def test_ldap_connection(ldap_server_address, ldap_username, ldap_password):
try:
# Define the LDAP server
server = Server(ldap_server_address, get_info=ALL)
# Create the connection object
conn = Connection(server, user=ldap_username, password=ldap_password)
# Try to bind to the server
if conn.bind():
print('Connection successful.')
return conn
else:
print('Connection failed:', conn.result)
if conn.result['result'] == 49:
print("Invalid credentials. Please check your username and password.")
return None
except socket.error as e:
print('Socket error:', str(e))
return None
except Exception as e:
print('An error occurred:', str(e))
return None
def get_users_and_groups(conn, base_dn):
try:
# Search for all user objects
conn.search(search_base=base_dn, search_filter='(objectClass=user)', search_scope=SUBTREE, attributes=['cn', 'sAMAccountName'])
users = conn.entries
print(f"Found {len(users)} users:")
for user in users:
print(f"User: {user.cn}, sAMAccountName: {user.sAMAccountName}")
# Search for all group objects
conn.search(search_base=base_dn, search_filter='(objectClass=group)', search_scope=SUBTREE, attributes=['cn', 'sAMAccountName'])
groups = conn.entries
print(f"\nFound {len(groups)} groups:")
for group in groups:
print(f"Group: {group.cn}, sAMAccountName: {group.sAMAccountName}")
except Exception as e:
print('An error occurred while searching for users and groups:', str(e))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Test LDAP connection to a Domain Controller and gather intelligence.')
parser.add_argument('--server', required=True, help='LDAP server address (e.g., ldap://your-ldap-server.com:389)')
parser.add_argument('--username', required=True, help='Username for LDAP authentication (e.g., user@domain or DOMAIN\\user)')
parser.add_argument('--password', required=True, help='Password for LDAP authentication')
parser.add_argument('--base-dn', required=True, help='Base DN for the LDAP search (e.g., DC=domain,DC=com)')
args = parser.parse_args()
# Test the LDAP connection with the provided arguments
conn = test_ldap_connection(args.server, args.username, args.password)
if conn:
# Gather intelligence about the domain
get_users_and_groups(conn, args.base_dn)
# Unbind the connection
conn.unbind()
I use this tool to collect LDAP entries in my project : https://github.com/CobblePot59/ADmanage/tree/main
Thanks. I tried ADmanage and it works fine.
python ADmanage.py -d 'local.lab' --username 'user' --password 'pass' --dc-ip 192.168.5.1 -s --base-dn 'DC=local,DC=lab' -M get_ADobjects
[
{
"attributes": {
"accountExpires": "2026-01-01 23:59:59.999999+00:00",
"badPasswordTime": "1601-01-01 00:00:00+00:00",
"badPwdCount": "0",
etc
I tried ADcheck with exactly the same details and it throws the same error as before.
ldap3.core.exceptions.LDAPSocketOpenError: invalid server address
from ADmanage import ADclient
from argparse import ArgumentParser
class ADcheck:
def __init__(self, domain, username, password, hashes, dc_ip, options=None):
self.domain = domain
self.base_dn = f"DC={domain.split('.')[0]},DC={domain.split('.')[1]}"
self.username = username
self.password = password
self.hashes = hashes
self.nthash = hashes.split(':')[1] if hashes else ''
self.dc_ip = dc_ip
self.secure = options.secure
self.output = options.output
# self.is_admin = options.is_admin
self.connect()
def connect(self):
self.ad_client = ADclient(self.domain, self.username, self.password, self.hashes, self.dc_ip, self.base_dn, self.secure)
print(self.ad_client.conn.server)
print(self.ad_client.get_ADobjects())
class Options:
def __init__(self):
self.secure = False
self.output = False
# self.is_admin = False
def parse_arguments():
parser = ArgumentParser(description='Process some arguments')
parser.add_argument('-d', '--domain', required=True, help='Domain name of the target system.')
parser.add_argument('-u', '--username', required=True, help='Username for authentication.')
parser.add_argument('-p', '--password', help='Password for authentication.')
parser.add_argument('-H', '--hashes', help='Hashes for authentication.')
parser.add_argument('--dc-ip', required=True, help='IP address of the Domain Controller.')
parser.add_argument('-s', '--secure', action='store_true', help='Use SSL for secure communication.')
parser.add_argument('-L', '--list-modules', action='store_true', help='List available modules.')
parser.add_argument('-M', '--module', help='Module to use.')
parser.add_argument('-o', '--output', action='store_true', help='Generate HTML report file.')
parser.add_argument('--debug', action='store_true', help='Print method name.')
args = parser.parse_args()
return args
def main():
from getpass import getpass
args = parse_arguments()
domain = args.domain
username = args.username
hashes = args.hashes
password = args.password or hashes or getpass('Password :')
dc_ip = args.dc_ip
options = Options()
options.secure = args.secure
options.output = args.output
adcheck = ADcheck(domain, username, password, hashes, dc_ip, options)
if __name__ == '__main__':
main()
What do you get with this code? You should have something like this :
I added some debug into ADcheck to see where it was breaking. The prod domain I tested on uses a UPN format username@UPN_suffix. Allowing for that makes the connection.
ADcheck -d 'xxxxxxxxxxx.com' --username 'username@UPN_suffix' --password 'pass' --dc-ip xxx.xxx.xxx.xxx -s
Connecting to AD with IP: xxx.xxx.xxx.xxx, Username: username@UPN_suffix, Secure: True
Connection successful.
Attempting SMB login with Domain: xxxxxxxxxxx.com, Username: username
SMB connection successful.
There are other errors after that but at least it makes the connection.
With the test.py script
raise LDAPSocketOpenError('invalid server address')
ldap3.core.exceptions.LDAPSocketOpenError: invalid server address
Ok so your problem is not Domain Controller url but in username actually.
In ADmanage username = f"{domain.split('.')[0]}\\{username}"
, I will see about replacing it.
If you specify the username in UPN format in test.py does it work?
Nah the test.py script doesn't seem to parse the username properly. If I'm using username@UPN_suffix then it throws this error. The creds are correct though. It's the same error that ADcheck throws.
raise LDAPBindError(error)
ldap3.core.exceptions.LDAPBindError: automatic bind not successful - invalidCredentials
If I just use --username 'username' without UPN then I get the following error.
raise LDAPSocketOpenError('invalid server address')
ldap3.core.exceptions.LDAPSocketOpenError: invalid server address
I just added the parse function to get it to connect
def parse_username(self, username, default_domain):
if '@' in username:
username_part, domain_part = username.split('@')
return default_domain, username_part # Keep the default domain
elif '\\' in username:
domain_part, username_part = username.split('\\')
return domain_part, username_part
else:
return default_domain, username
similar to #5
Running in Kali
Linux kali 6.6.9-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.6.9-1kali1 (2024-01-08) x86_64 GNU/Linux
Based on the install instructions, and after the first run with
python ADcheck.py -d 'internal.local' -u 'user' -p 'pass' --dc-ip 192.168.1.1
the GPO folder isn't created. Fix is to create the GPO folder and then run again.
Now running the same command
python ADcheck.py -d 'internal.local' -u 'user' -p 'pass' --dc-ip 192.168.1.1
causes the following errorimpacket.smb3.SessionError: SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)
Using netexec for spooler vuln works without issue.
nxc smb 192.168.1.1 -u 'user' -p 'pass' -M spooler
SPOOLER 192.168.1.1 445 DC1 Spooler service enabled
Running with the same user for ADcheck.
python ADcheck.py -d 'internal.local' -u 'user' -p 'pass' --dc-ip 192.168.1.1 -M spooler
Failed to list path: SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.) Access denied. Please check the permissions for the user. impacket.smb3.SessionError: SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)
I put some error checking in GPObrowser.py to skip permission errors and added some debug statements. Running the same command again and it downloads the GPOs. It still hits the permission errors but it skips anything that it doesn't have permission for and it ends with the spooler message.
Spooler service is enabled on remote target : True
I ran it without spooler and then hit this error but I haven't had a chance to see what's going on here.
`1 - Traceback (most recent call last): File "/home/kali/adcheck/ADcheck2/ADcheck.py", line 1023, in
launch_all_methods(adcheck)
File "/home/kali/adcheck/ADcheck2/ADcheck.py", line 33, in launch_all_methods
getattr(obj, method_name)()
File "/home/kali/adcheck/ADcheck2/ADcheck.py", line 120, in accounts_never_expire
if 'DONT_EXPIRE_PASSWORD' in uac_details(user['userAccountControl']):