jborean93 / pypsrp

PowerShell Remoting Protocol for Python
MIT License
324 stars 49 forks source link

[Question] Kerberos stuff #180

Closed XiaoliChan closed 10 months ago

XiaoliChan commented 10 months ago

Hello jborean, I'm playing winrm with kerberos authentication, and I have a question: It's a way to set domain via variable when doing kerberos authentication?

image

code

Client(
                self.host,
                auth="kerberos",
                username=f"{domain}\\{self.username}",
                password=self.password,
                ssl=self.ssl,
                cert_validation=False,
            )
XiaoliChan commented 10 months ago

I have read the article for pypsrp, looks like it relate klist stuff

jborean93 commented 10 months ago

On Linux you need to specify the domain using the UPN format username@DOMAIN.COM. In your example it would be Client(username=f"{self.username}@{domain}").

XiaoliChan commented 10 months ago

On Linux you need to specify the domain using the UPN format username@DOMAIN.COM. In your example it would be Client(username=f"{self.username}@{domain}").

Thanks! now it recognize the domain from UPN, but any variable to set KDC host? (like kdcHost variable in impacket) image

(Want to make this work without any access /etc/krb5.conf)

jborean93 commented 10 months ago

There is none, it's up to your krb5.conf and how it is configured to lookup the KDC. This can either be hardcoded or it can be configured to lookup through DNS.

XiaoliChan commented 10 months ago

Got a new error

'SpnegoError (4294967295): Major (851968): Unspecified GSS failure.  Minor code may provide more information, Minor (2529638919): Server not found in Kerberos database, Context: Processing security token'

Compare with evil-winrm, both have HTTP service ticket and user's TGT image

Code

self.conn = Client(
                "dc-2019.xiaoli.local", # target host current is hard-coding here for testing
                auth="kerberos",
                username=f"{self.username}@{self.domain.upper()}",
                password=self.password,
                ssl=self.ssl,
                cert_validation=False,
            )
XiaoliChan commented 10 months ago

Oh, same issue like https://github.com/jborean93/pyspnego/issues/55

I have try your code to request ST

import gssapi

username = "xiaoli@XIAOLI.LOCAL"
password = b"111qqq..."
spn = "host@dc-2019.xiaoli.local"

kerberos = gssapi.OID.from_int_seq("1.2.840.113554.1.2.2")

user_princ = gssapi.Name(username, name_type=gssapi.NameType.user)
cred = gssapi.raw.acquire_cred_with_password(user_princ, password, usage='initiate', mechs=[kerberos]).creds

c = gssapi.SecurityContext(
    name=gssapi.Name(spn, name_type=gssapi.NameType.hostbased_service),
    creds=cred,
    mech=kerberos,
    usage="initiate",
)

c.step()

print(c.complete)
print(c.actual_flags)

image

But it still failed in winrm

from pypsrp.shell import Process, WinRS
from pypsrp.wsman import WSMan
wsman = WSMan('dc-2019.xiaoli.local', ssl=False, auth='kerberos', username='xiaoli@XIAOLI.LOCAL', cert_validation=False)
with wsman, WinRS(wsman) as shell:
    process = Process(shell, 'whoami')
    process.invoke()

print(process.stdout.decode())

image

MIT krb5 software version Kerberos 5 release 1.20.1

pypsrp version 0.8.1

jborean93 commented 10 months ago

One mistake is that pypsrp requests a service ticket for the SPN WSMAN rather than http or host. Due to backwards compatibility issues I cannot really change this right now but you can override what is used with the negotiate_service kwarg.

client = Client(
    ...,
    negotiate_service='http',
)

You can also set negotiate_hostname_override as the value for the hostname portion of the SPN rather than the WinRM URL hostname if you need to connect with an IP but you know the SPN targetname.

Just an FYI the MIT krb5 C library this is using can emit more logs with the KRB5_TRACE env var. Run with the env var KRB5_TRACE=/dev/stdout and it'll output a lot more info around what it is doing with the krb5 steps.

XiaoliChan commented 10 months ago

@jborean93 Thank you for your help, now it works!

Code

self.conn = Client(
                self.host,
                auth="kerberos",
                #username=f"{self.domain}\\{self.username}",
                username=f"{self.username}@{self.domain.upper()}",
                password=self.password,
                ssl=self.ssl,
                cert_validation=False,
                negotiate_service='http',
                negotiate_hostname_override="dc-2019"
            )

image

XiaoliChan commented 10 months ago

@jborean93 Sorry for ask this question again

I just noticed if I pass the password with nthash when doing Kerberos auth, it will be said my credential is invalid.

self.conn = Client(
                    self.host,
                    auth="kerberos",
                    username=f"{self.username}@{self.domain.upper()}",
                    password=f"00000000000000000000000000000000:{self.nthash}",
                    ssl=self.ssl,
                    cert_validation=False,
                    negotiate_service=spn,
                    negotiate_hostname_override=self.hostname,
                )

Exception

CredentialsExpiredError: SpnegoError (11): Major (720896): The referenced credential has expired, Minor (100001): Success, Context: Getting GSSAPI credential

So pypsrp only accepts cleartext password when doing Kerberos auth?

jborean93 commented 10 months ago

Yes there is no support for providing a hash for Kerberos, you can provide the hash for NTLM though. You can use attempt to create a CCache for it to use but it cannot be provided through pypsrp. IIRC you cannot create Kerberos tokens directly from the hash unless you are using older enc types like RC4, AES is derived from a different key.

XiaoliChan commented 10 months ago

Yes there is no support for providing a hash for Kerberos, you can provide the hash for NTLM though. You can use attempt to create a CCache for it to use but it cannot be provided through pypsrp. IIRC you cannot create Kerberos tokens directly from the hash unless you are using older enc types like RC4, AES is derived from a different key.

Fine, thanks for your reply!

Yes, just request a TGT instead of provide nthash is a solution!