CobblePot59 / ADcheck

Assess the security of your Active Directory with few or all privileges.
96 stars 11 forks source link

Error SMB3 #6

Open jakuta-tech opened 4 weeks ago

jakuta-tech commented 4 weeks ago

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 error

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.)

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']):


KeyError: 'userAccountControl'`
CobblePot59 commented 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 ?

jakuta-tech commented 2 weeks ago

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.

CobblePot59 commented 2 weeks ago

Can you test with administrator account ? Is your domain name entered in lower case ?

jakuta-tech commented 2 weeks ago

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()
CobblePot59 commented 2 weeks ago

I use this tool to collect LDAP entries in my project : https://github.com/CobblePot59/ADmanage/tree/main

jakuta-tech commented 2 weeks ago

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

CobblePot59 commented 2 weeks ago
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 : image

jakuta-tech commented 2 weeks ago

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

537

    raise LDAPSocketOpenError('invalid server address')
ldap3.core.exceptions.LDAPSocketOpenError: invalid server address
CobblePot59 commented 2 weeks ago

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?

jakuta-tech commented 2 weeks ago

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