fortra / impacket

Impacket is a collection of Python classes for working with network protocols.
https://www.coresecurity.com
Other
13.49k stars 3.57k forks source link

GetADUsers.py and GetUserSPNs.py LDAP "AcceptSecurityContext error" #474

Closed maaaaz closed 4 years ago

maaaaz commented 6 years ago

Hello there,

I'm facing an issue while using GetADUsers.py and GetUserSPNs.py, the LDAP bind fails and I don't know why. Entered credentials are good.

$ python GetUserSPNs.py adyolo.swag/test:<cred>@192.168.11.144 -dc-ip 192.168.11.144 -debug
Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies

[+] Connecting to 192.168.11.144, port 389, SSL False
Traceback (most recent call last):
  File "GetUserSPNs.py", line 436, in <module>
    executer.run()
  File "GetUserSPNs.py", line 247, in run
    ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
  File "build\bdist.win32\egg\impacket\ldap\ldap.py", line 341, in login
    response['bindResponse']['diagnosticMessage'])
LDAPSessionError: Error in bindRequest -> invalidCredentials: 8009030C: LdapErr: DSID-0C090605, comment: AcceptSecurityC
ontext error, data 52e, v2580
[-] Error in bindRequest -> invalidCredentials: 8009030C: LdapErr: DSID-0C090605, comment: AcceptSecurityContext error,
data 52e, v2580
$ python GetADUsers.py adyolo.swag/test:<cred>@192.168.11.144 -dc-ip 192.168.11.144 -debug
Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies

[+] Connecting to 192.168.11.144, port 389, SSL False
Traceback (most recent call last):
  File "GetADUsers.py", line 240, in <module>
    executer.run()
  File "GetADUsers.py", line 133, in run
    ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
  File "build\bdist.win32\egg\impacket\ldap\ldap.py", line 341, in login
    response['bindResponse']['diagnosticMessage'])
LDAPSessionError: Error in bindRequest -> invalidCredentials: 8009030C: LdapErr: DSID-0C090605, comment: AcceptSecurityC
ontext error, data 52e, v2580
Error in bindRequest -> invalidCredentials: 8009030C: LdapErr: DSID-0C090605, comment: AcceptSecurityContext error, data
 52e, v2580

Client config

Server config

maaaaz commented 6 years ago

And by the way, there's also the error with GetNPUsers.py:

$ python GetNPUsers.py adyolo.swag/test:<cred>@192.168.11.144 -dc-ip 192.168.11.144 -outputfile lol -debug
Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies

[+] Connecting to 192.168.11.144, port 389, SSL False
[*] Cannot authenticate test, getting its TGT
[+] Trying to connect to KDC at 192.168.11.144
$krb5asrep$23$test@ADYOLO.SWAG:5ca3e0c1cf31bc743cc4027b0774552b$<hash>
asolino commented 6 years ago

Hmm.. I have no clue what that could be @maaaaz .

I'm assuming those creds are working well when using smbclient.py or other tools that do not touch LDAP. Did you try other LDAP tools, is ldapsearch working with those creds? Try using -k on those scripts, maybe NTLM is disabled?

maaaaz commented 6 years ago

Yes, these credz work with common LDAP browser such as Sysinternal ADExplorer, my DC or domain does not have anything specific (2 or 3 users, nothing else configured).

It seems to a be common issue when connecting to an AD (https://stackoverflow.com/questions/23426739/connecting-python-to-ldap-server-using-python-ldap-getting-desc-invalid-cre) but I can't find how to specify OPT_REFERRALS to 0 (or equivalent) in these modules current code...

asolino commented 6 years ago

Hmm.. interesting @maaaaz .

Is there any chance to get a PCAP of ADExplorer or similar working (it should be ldap not ldaps) so I can see the specific options added and where?.. Looks like we don't have support for that at the moment.

maaaaz commented 6 years ago

Sure, I attached 2 pcaps :

GetADUsers.py_LDAP_bug.zip

.144 is the DC server, .154 is the client (not domain-joined).

Do you know if some people did manage to make GetADUsers.py work on a real environment ? From a domain-joined machine ?

Cheers amigo !

asolino commented 6 years ago

Thanks @maaaaz

At first sight I'm not seeing much difference actually.. just the successful one then encrypting the contents.. Have you tried forcing the connection to ldaps instead? Try changing this line. Another thing that could happen is the server enforcing encryption/signing, although I never played with that in the context of LDAP.

maaaaz commented 6 years ago

I tried by changing ldap:// to ldaps:// here, unsuccessfully:

$ python GetADUsers2.py adyolo.swag/test:<cred>@192.168.11.144 -dc-ip 192.168.11.144 -debug
Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies

[+] Connecting to 192.168.11.144, port 636, SSL True
Traceback (most recent call last):
  File "GetADUsers2.py", line 240, in <module>
    executer.run()
  File "GetADUsers2.py", line 131, in run
    ldapConnection = ldap.LDAPConnection('ldaps://%s'%self.__target, self.baseDN, self.__kdcHost)
  File "build\bdist.win32\egg\impacket\ldap\ldap.py", line 118, in __init__
    self._socket.do_handshake()
  File "Z:\Partage\impacket_fork\venvimpacketwinnew\lib\site-packages\OpenSSL\SSL.py", line 1907, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "Z:\Partage\impacket_fork\venvimpacketwinnew\lib\site-packages\OpenSSL\SSL.py", line 1632, in _raise_ssl_error
    raise SysCallError(-1, "Unexpected EOF")
SysCallError: (-1, 'Unexpected EOF')
(-1, 'Unexpected EOF')

On the server-side, I only see a SSL/TLS ClientHello attempt and then nothing:

Secure Sockets Layer
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 97
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 93
            Version: TLS 1.0 (0x0301)
            Random: ad9e2c711c07de673902ea5e34955895cd3544a30f311f85...
                GMT Unix Time: Apr 21, 2062 06:53:53.000000000 Romance Standard Time
                Random Bytes: 1c07de673902ea5e34955895cd3544a30f311f8575e895e7...
            Session ID Length: 0
            Cipher Suites Length: 18
            Cipher Suites (9 suites)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
                Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
                Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
                Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)
            Compression Methods Length: 1
            Compression Methods (1 method)
                Compression Method: null (0)
            Extensions Length: 34
            Extension: ec_point_formats (len=4)
                Type: ec_point_formats (11)
                Length: 4
                EC point formats Length: 3
                Elliptic curves point formats (3)
                    EC point format: uncompressed (0)
                    EC point format: ansiX962_compressed_prime (1)
                    EC point format: ansiX962_compressed_char2 (2)
            Extension: supported_groups (len=10)
                Type: supported_groups (10)
                Length: 10
                Supported Groups List Length: 8
                Supported Groups (4 groups)
                    Supported Group: x25519 (0x001d)
                    Supported Group: secp256r1 (0x0017)
                    Supported Group: secp521r1 (0x0019)
                    Supported Group: secp384r1 (0x0018)
            Extension: SessionTicket TLS (len=0)
                Type: SessionTicket TLS (35)
                Length: 0
                Data (0 bytes)
            Extension: encrypt_then_mac (len=0)
                Type: encrypt_then_mac (22)
                Length: 0
            Extension: extended_master_secret (len=0)
                Type: extended_master_secret (23)
                Length: 0

I also think that it may have to do with encryption/signing but have no clue how to dig deeper. It would be nice to have a pcap of a working connection and then diff the results.

NemosDemos commented 6 years ago

So I've been running into the same issue, on what sounds like a very similar environment (2012R2 DC, in a very simple lab, Kali Linux client). Although I did find a work-around, I haven't been able to pinpoint the exact issue.

I found running GetADUsers.py lab.local/pat@192.168.56.10 or GetADUsers.py lab.local/pat@pdc.lab.local would fail, referencing the same AcceptSecurityContext errors as OP (without even prompting for a password).

However, running GetADUsers.py lab.local/pat -all or GetADUsers.py lab.local/pat -dc-ip 192.168.56.10 -all would return users, after prompting for a password.

The bit that confuses me though, is that I couldn't spot any differences in the Wireshark captures/initial LDAP handshake between failure and success...

rmaksimov commented 4 years ago

The old thread but is still open. It seems that the problem was due to the extra @ sign that was always appended to the end of the target option string, so it was parsed incorrectly (https://github.com/SecureAuthCorp/impacket/blame/96b522884f31f68cfff6cda24c2c30827b3b35e8/examples/GetADUsers.py#L225-L231)

the following

python GetUserSPNs.py adyolo.swag/test:<cred>@192.168.11.144 -dc-ip 192.168.11.144 -debug

resulted in

options.target = 'adyolo.swag/test:<cred>@192.168.11.144'
targetParam = 'adyolo.swag/test:<cred>@192.168.11.144@'
password = '<cred>@192.168.11.144'
address = ''
maaaaz commented 4 years ago

@rmaksimov, wow so it would really come only from a parsing error ? nice finding ! Obvious things are sometimes not that obvious.

maaaaz commented 4 years ago

@rmaksimov, @asolino, I did investigate and indeed it was only a parsing bug.

The code recently changed for GetUserSPNs.py but according the current stable 0.9.21 version available on pypi, you just need to change, in all example scripts, address by password at this line https://github.com/SecureAuthCorp/impacket/blob/8d4c91481b01dae9f62804893fcc74a40ffc7c45/examples/GetUserSPNs.py#L441

Yes. really. that tiny stuff that led this 2-year old bug, where we thought it was related to the LDAP communication, TLS and so !

After doing this tiny modification, I really ran the same 2 commands I tried in the original post, and it just worked flawlessly.

For the record, the really complex modifications I did to debug this:

225     targetParam = options.target+'@'
226     print('targetParam "%s"' % targetParam)
227     domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(targetParam).groups('')
228 
229     print("parsing before stuff ; username: %s | password: %s | domain: %s" % (username,password,domain))
230     #In case the password contains '@'
231     if '@' in password:
232         password = password + '@' + address.rpartition('@')[0]
233         address = address.rpartition('@')[2]
234 
235     if domain == '':
236         logging.critical('Domain should be specified!')
237         sys.exit(1)
238 
239     if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
240         from getpass import getpass
241         password = getpass("Password:")
242 
243     if options.aesKey is not None:
244         options.k = True
245 
246     try:
247         print("parsing after ; username: %s | password: %s | domain: %s" % (username,password,domain))
248         executer = GetADUsers(username, password, domain, options)

That gave me this at execution showing that the password field is altered, hence leading the original error:

targetParam "adyolo.swag/<user>:<pass>@192.168.200.130@"
parsing before stuff ; username: <user> | password: <pass> | domain: adyolo.swag
parsing after ; username: <user> | password: <pass>@192.168.200.130 | domain: adyolo.swag
asolino commented 4 years ago

Nice finding @maaaaz!

We will test it locally and update accordingly. Thanks!

0xdeaddood commented 4 years ago

Hey guys!

The proper use to run these scripts (GetADUsers, GetUserSPNs, GetNPUsers...) is by specifying the target as follows: domain/username[:password]. The IP address must not be specified in the target. In case you need to define the IP address of the domain controller, you must use the parameter -dc-ip.

In a recent commit, the regex that processes the target (domain/username[:password]) was improved and the variable address was removed.

rmaksimov commented 4 years ago

@maaaaz,

The following is not an option

you just need to change, in all example scripts, address by password at this line

I worked on this problem some time ago, but this is not an easy task with the current process of passing/parsing user input in those scripts where an address can be provided, but is not required. Also it is due to the fact that the symbols used in an address are a subset of password alphabet symbols. So, there is no that symbol that allow to separate a password value from an address value as it could be done with the values of domain (before the first "/") and username (between the first "/" and the first ":")

For example, with the following string "domain/username:pass@1.1.1.1" specified by a user you will never know whether it is the password, "pass@1.1.1.1", or the password, "pass", and the address, "1.1.1.1". In the case where it is just a password, user must to specify the trailing @ sign by himself. It's not cool and the syntax will also look ugly ("domain/username:pass@1.1.1.1@")

This is probably a rare case, but it could happen, so it should be considered to avoid such problems in the future examples

There seems to be a misunderstanding. I mentioned that it was a bug, but now everything works fine. I should have noted that this problem is no longer relevant and can be closed Details are in @0xdeaddood's post https://github.com/SecureAuthCorp/impacket/issues/474#issuecomment-625949398

sebastienPoussard commented 4 years ago

It seems the problem is still present in Impacket v.0.9.22

kali@kali:~/Downloads$ python3 GetUserSPNs.py  spookysec.local/svc-admin -no-pass -debug
Impacket v0.9.22.dev1+20200921.175010.84c8d6a7 - Copyright 2020 SecureAuth Corporation

[+] Impacket Library Installation Path: /usr/local/lib/python3.8/dist-packages/impacket-0.9.22.dev1+20200921.175010.84c8d6a7-py3.8.egg/impacket
[+] Connecting to spookysec.local, port 389, SSL False
Traceback (most recent call last):
  File "GetUserSPNs.py", line 506, in <module>
    executer.run()
  File "GetUserSPNs.py", line 266, in run
    ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
  File "/usr/local/lib/python3.8/dist-packages/impacket-0.9.22.dev1+20200921.175010.84c8d6a7-py3.8.egg/impacket/ldap/ldap.py", line 341, in login
    raise LDAPSessionError(
impacket.ldap.ldap.LDAPSessionError: Error in bindRequest -> invalidCredentials: 8009030C: LdapErr: DSID-0C090690, comment: AcceptSecurityContext error, data 52e, v4563
[-] Error in bindRequest -> invalidCredentials: 8009030C: LdapErr: DSID-0C090690, comment: AcceptSecurityContext error, data 52e, v4563

Or am I missing something ?

asolino commented 4 years ago

This is not related to the original problem @sebastienPoussard .. You're not specifying target IP. Looks like you haven't specified the svc-admin credentials. Closing this issue since the original problem got clarified by @0xdeaddood.