dotnet / Kerberos.NET

A Kerberos implementation built entirely in managed code.
MIT License
514 stars 89 forks source link

Kerberos.NET Usage with untrusted domains #3

Closed kavyashivakumar closed 6 years ago

kavyashivakumar commented 6 years ago

hi

i tried to use basic kerberos sample at my end. but i get invalid checksum error. can you help?

`

var keytab = new KeyTable(File.ReadAllBytes(@"C:\Kafka\SRV.keytab")); string raw = ""; string something = "aes"; switch (something) { case "aes": case "aes256": raw = "YIIJOAYGKwYBBQUCoIIJLDCC..."; break; case "aes128": raw = "YIIHCAYGKwYBBQUCoIIG/DCCB..."; break; case "rc4": raw = "YIIKbQYGKwYBBQUCoIIKYTCCCl..."; break; case "spnego": raw = "YIGeBgYrBgEFBQKggZMwgZCgGjAYBgorBgEEAYI3AgIeBgorBgEEAYI3AgIKonIEcE5FR09FWFRTAAAAAAAAAABgAAAAcAAAAAPx046cicVOngxMfxUsCsEIMeUM39SSXP1N9DuDVIU3IFosQ3eWTsKOPdfTNWD4SAAAAAAAAAAAYAAAAAEAAAAAAAAAAAAAAMNbiTClcBVAolAmCpGQrZA="; break; default: raw = args[1]; break; }

        var validator = new KerberosValidator(keytab)
        {
            //Logger = W
        };

        var authenticator = new KerberosAuthenticator(validator);
        var identity = authenticator.Authenticate(raw);

        var t = identity.Result;

        Console.WriteLine(identity);

        var name = identity.Id;`
SteveSyfuhs commented 6 years ago

What's the stacktrace of the exception? What algorithm was used for the ticket encryption? Can you verify the SRV.keytab file has an entry for that algorithm type, that also maps to the SPN of the ticket?

kavyashivakumar commented 6 years ago

thanks Steve sorry that was ignorant of me. here is the stack trace with rc4 algo. they've changed it.

at Kerberos.NET.Entities.RC4DecryptedData.Decrypt(Byte[] k1, Byte[] ciphertext, KeyUsage keyType) at Kerberos.NET.Entities.RC4DecryptedData.Decrypt(KeyTable keytab) at Kerberos.NET.KerberosRequest.Decrypt(KrbApReq token, KeyTable keytab) at Kerberos.NET.KerberosRequest.DecryptNegotiate(NegTokenInit negotiationToken, KeyTable keytab) at Kerberos.NET.KerberosRequest.Decrypt(KeyTable keytab) at Kerberos.NET.KerberosValidator.<Validate>d__17.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Kerberos.NET.KerberosAuthenticator.<Authenticate>d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Kerberos.NET.KerberosAuthenticator.<Authenticate>d__3.MoveNext()

keytab was generated like this

KTPASS -PRINC  srv@Isomething.COM -MAPUSER IA\SRV +RNDPASS -PTYPE KRB5_NT_PRINCIPAL -CRYPTO RC4-HMAC-NT

SteveSyfuhs commented 6 years ago

The SPN isn't required here since it's going down the RC4/MD4 route, so it may be that the key in the file doesn't match what's in AD. The way this looks up keys in the keytab is fairly simplistic in that it'll find the first key where the eType and SPN match what's in the ticket, and if it can't find it by that, it'll attempt to just use the first key of the given eType. You might try looking for duplicate eTypes with SPNs that don't match the ticket.

kavyashivakumar commented 6 years ago

pardon my naivety. but how do i look that?

"You might try looking for duplicate eTypes with SPNs that don't match the ticket."?

my keytab is all encrypted. when i look at simple.keytab in your eg, i can make some sense of it. but not with the one I've

SteveSyfuhs commented 6 years ago

You can look into it through the debugger -- the keytab instance will contain a list of entries. Those entries will have a display value of

{Version} {EncryptionType} {Principal?.Realm}

So any duplicates would have the same EncryptionType and PrincipalName, but you'd have to peer into a given entry to get the exact principal name by expanding it's properties.

Alternatively there are a couple tools out there like

Or you could write a simple command line tool that dumps the contents:

var json = JsonConvert.SerializeObject(keytab); // requires JSON.NET

Console.WriteLine(json);

Just be careful with that, since it'll dump the secrets too.

kavyashivakumar commented 6 years ago

Thanks Steve. i'll try that. but just posting some additional details here when keytab was created.

`Targeting domain controller: 006.i.SOME.COM

Failed to set property "servicePrincipalName" to "SRV" on Dn "CN=SRV,OU=Service Admins Service Accounts, OU=Service Admins,DC=IA,DC=SOME,DC=COM": 0x13.

WARNING: Unable to set SPN mapping data.

If SRV already has an SPN mapping installed for SRV, this is no cause for concern. Password succesfully set! Key created. Output keytab to C:\TEMP\SRV_APPIA.KEYTAB: Keytab version: 0x502 keysize 70 SRV@IA.SOME.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x17 (RC4-HMAC) keylength 16 (0xxxxxxxxxxxxxxxxxxxxxx) `

I have quite a heterogenous set up. i am on a domain A on windows. and i'm trying to connect to a completely different realm on linux box over kerberos. (no trust between these domains whatsoever) i can do this with MIT tools and java. but i do want to achieve it using .net. its just getting way too difficult. if Kerberos.NET works, can't tell you how much that would help us.

will get back on result soon

kavyashivakumar commented 6 years ago

@SteveSyfuhs this is what i have

    Version 5   int
    [0] "SRV"   string                  _this was entries[0].Principal.Names[0]_
    Realm   "IA.SOME.COM"   string
    EncryptionType  RC4_HMAC_NT Kerberos.NET.Entities.EncryptionType?

i have tried to maintain some anonymity here. entries collection btw, has just 1 entry.

does this make sense?

SteveSyfuhs commented 6 years ago

Okay, here's something to look at. The ptype is set to 1 (NT_PRINCIPAL in K.NET). This might not match what's in the ticket. I would expect the ticket to have the ptype as 2 (NT_SRV_INST). That is used in the PrincipalName equality check when looking up keys. Based on the output though, if this is the only key in the file, it would fall back to using this key anyway, so it might be a red herring.

The SRV SPN warning is kind of interesting. That might cause AD to target this incorrectly. You might try dumping the ticket pre-decryption to see what it's saying, and making sure the SPN actually matches too.

var requestBytes = Convert.FromBase64String(ticketEncoded);

var kerberosRequest = KerberosRequest.Parse(requestBytes);

var json = JsonConvert.SerializeObject(kerberosRequest); // requires JSON.NET
SteveSyfuhs commented 6 years ago

Okay, the keytab looks correct. Check out my comment above about looking into the ticket itself.

kavyashivakumar commented 6 years ago

@SteveSyfuhs

sorry but could you help me with whats ticketEncoded?

kavyashivakumar commented 6 years ago

ah! is it the raw content of rc4?

SteveSyfuhs commented 6 years ago

Ah sorry. That's just the encoded bytes of the ticket. The sample you posted at the top shows:

var identity = authenticator.Authenticate(raw);

raw is the base64 encoded ticket. So you'd use that.

kavyashivakumar commented 6 years ago

ok thank you. just so I know, rc4 = RC4_HMAC_NT? my knowledge in security is Meh!!

i also used exact string of rc4 from your example. btw

SteveSyfuhs commented 6 years ago

Yep, that's correct.

kavyashivakumar commented 6 years ago

So, @SteveSyfuhs

"MechType":{"Mechanism":"SPNEGO","Oid":"1.3.6.1.5.5.2"},"NegotiationToken":{"MechTypes":[{"Mechanism":"Kerberos V5 Legacy","Oid":"1.2.840.48018.1.2.2"},{"Mechanism":"Kerberos V5","Oid":"1.2.840.113554.1.2.2"},{"Mechanism":"NegoEx","Oid":"1.3.6.1.4.1.311.2.2.30"},{"Mechanism":"NTLM","Oid":"1.3.6.1.4.1.311.2.2.10"}],"MechToken":{"NegotiateExtension":null,"ThisMech":{"Mechanism":"Kerberos V5","Oid":"1.2.840.113554.1.2.2"},"InnerContextToken":{"ProtocolVersionNumber":5,"MessageType":14,"APOptions":536870912,"Ticket":{"TicketVersionNumber":5,"Realm":"IDENTITYINTERVENTION.COM","SName":{"Realm":"IDENTITYINTERVENTION.COM","Names":["HTTP/aadg.windows.net.nsatc.net"],"NameType":2},"EncPart":{"EType":23,"KeyVersionNumber":3,"Cipher":"j

followed by some gibberish

going by your comment etype doesn't match. ? have i made some dumb mistake?

SteveSyfuhs commented 6 years ago

Heh... you're trying to decrypt a sample ticket with YOUR key. That definitely won't work. :) You can tell based on the realm: identityintervention.com -- that's my test domain.

Can you confirm you're using the right raw value?

kavyashivakumar commented 6 years ago

lol. i can definitely confirm i'm using the wrong value. after seeing it in quick watch

so i was right. thats a dumb mistake. i'm definitely using my keytab. but here is my question.

how do i get that string? of rc4? the raw string?

security is something that has never been my arena. i'm a basic learner still

kavyashivakumar commented 6 years ago

doesn't look like i'm about to find anything in keytab Object?

SteveSyfuhs commented 6 years ago

The string is issued by AD to a client to authenticate the user to a server. This is usually through a web browser 401/www-authenticate handshake, or through an SSPI/GSS request-for-ticket when its not web-based (e.g. server to server).

Normally, you'd use a tool like Fiddler to grab the value from the Authorization header of the client request to the server. You can see sample interactions in various samples:

https://syfuhs.net/2017/03/20/authenticating-web-requests-with-kerberos-net/ https://syfuhs.net/2017/03/20/configuring-an-spn-in-active-directory-for-kerberos-net/

kavyashivakumar commented 6 years ago

thanks Steve. mine is definitely latter. server to server. sasl mechanism is gssapi. i did see both these links. but it had something to do with http.so wondered if they're for me to start with. but i may be jumping guns here. so let me try. so does this mean i'll have to rely on AD? like i mentioned in one of the answer above there's absolutely no trust whatsoever between server realm and client domain

the comment you see in second link is from me. we have gotten around hte problem using Java wrapper. but i'd like to try this using .net

SteveSyfuhs commented 6 years ago

I saw an email notification, but the actual comment seems to have disappeared here. Wondering if you accidentally or purposefully deleted it?

In any case, regarding your question:

so does this mean i'll have to rely on AD? like i mentioned in one of the answer above there's absolutely no trust whatsoever between server realm and client domain...

The intent of this library is to handle validating tickets from AD. The flow would be something like this:

AD => (user) client => (Kerberos.NET) server

This allows the server to authenticate a user within an AD domain. This is also the case for server to server authentication where the client is just another server. The receiving server definitely does not need a trust to AD, but at some point AD needs to know who to issue the ticket to, which is where the SPN stuff comes into play. In this case its just a user or computer in AD that identifies as your service. AD will happily issue a ticket to it.

So, I guess I might be a little confused as to how you're wanting to use this library then?

SteveSyfuhs commented 6 years ago

Oh, I see your comment now. There absolutely has to be a reference in AD for your service, but it just needs to be a user/computer/service account with a matching SPN. You do not need a trust set up.

kavyashivakumar commented 6 years ago

I might have deleted but definitely would have clubbed into 1 post Definitely you’re not confused. What you’ve described is exactly what I need. I think I haven’t exactly understood about spn. And your article. I’m going to go through t again and try to understand Thank you for being so helpful. Hopefully I’ll come back with bit smarter qns soon

kavyashivakumar commented 6 years ago

I think that’s exactly what I may be missing I didn’t realize that’s what you were saying and I misunderstood that I needed to put trust between domains. So in AD of client need a reference to the service as you have mentioned with matching spn. Is this right understanding ?

If you’ve written any basic articles abot this would love to read up. Will google for now

SteveSyfuhs commented 6 years ago

Take a look at this: https://syfuhs.net/2017/03/19/a-look-at-azure-ad-single-sign-on/. It goes through the practical flow of how this would work. (obviously, they don't use Kerberos.NET, but it's the gist).

The SPN is just an identifier in AD. An SPN is associated with all requests for tickets. Basically, the client says "I need a ticket for user A to service B". Service B has an SPN of "HTTP/foo.com". The "HTTP" is just a prefix that describes the type of service -- it doesn't enforce anything and can be anything you want. That said, there are conventions around it, such that websites use "HTTP", file shares use "CIFS" or "SMV", etc. See this for more information on that: https://msdn.microsoft.com/en-us/library/ms677601(v=vs.85).aspx and https://technet.microsoft.com/en-us/library/cc772815(WS.10).aspx.

AD then looks for an account with an SPN of "HTTP/foo.com" and generates a ticket that is encrypted against the password of the account. The ticket is handed back to the client, which is then sent to the server. The server knows this password, and is able to decrypt the ticket. The ticket then contains all the useful identifying information about the user.

As such, there is no explicit trust set up here. There's an implicit trust because both know a shared secret, but there isn't anything special that needs to be set up beyond the account.

kavyashivakumar commented 6 years ago

Will do. This info definitely is helpful

kavyashivakumar commented 6 years ago

@SteveSyfuhs hi Steve, i'm getting SPN sorted. but meanwhile, was wondering if you can help me wth this. hypothetically, if we have trust between domains, would i still need SPN?

SteveSyfuhs commented 6 years ago

The trust wouldn't do much in this case. The point of the trust is to allow resources in one domain to see resources in another domain. With the trust, you need an SPN in either domain, where the caller can see into that domain to get the SPN. Without the trust, you need the SPN in the calling domain, because otherwise it just doesn't know what server it's talking to.

In this case, the trust is probably overkill if you're just doing it for this one thing. It may significantly complicate what you're doing too.

kavyashivakumar commented 6 years ago

thank you. that's quite helpful. stuck in bit of a cross domain issue here. lets say we're in domain A, the envt i'm trying to connect to is in domain B. but when we all goes live, the service is indeed in domain A. i obviously have to see it working in lower envt. i was told spn cannot be created in domain A to connect to domain B since it'll affect production.(which is in domain A)

kavyashivakumar commented 6 years ago

but that does affect Steve? i suggested if we can have 2 spns since its based on service\host.

SteveSyfuhs commented 6 years ago

You can have multiple SPNs per account in AD, and SPNs must be unique. You cannot have one SPN for two services living in two different domains (or, you can, but the caller would need to know how to differentiate the two, which is complicated).

So you can do 2 SPNs, one for A and one for B -- they'd be service\host.domaina.com and service\host.domainb.com.

Alternatively, you can set up a second account with a separate SPN for the other environment too.

kavyashivakumar commented 6 years ago

exactly. that is what i'd have thought. i do not want to use the same spn for both prod and lower envt anyway. because the host is obviously going to be different for both a and b. like you said 'service\host.domaina.com' and 'service\host.domainb.com.'

i have asked why can't we create a seperate spn for my SA. Thanks Steve. will get back soon

kavyashivakumar commented 6 years ago

You can have more than one if its host qualified. The issue more is that we cant add an SPN for a non-production host/service (in B) into a Production domain or against a Production (A) user.

i think i see sense in what he's saying. this is turning into a battle

it looks like more of a architectural constraint than technical to me. is this correct understanding?

SteveSyfuhs commented 6 years ago

It's definitely not a technical constraint. I imagine it's purely a security policy constraint, which is reasonable.

kavyashivakumar commented 6 years ago

fair enough. i'm not sure how to go about this then. without SPN doesn't look like i can use .NET? although have got it working with Java.

SteveSyfuhs commented 6 years ago

I suspect Java is falling back to NTLM, which doesn't require an SPN. This library explicitly blocks NTLM because its unsafe. You can always create a test domain that mimics domainA to try it out.

kavyashivakumar commented 6 years ago

I thought by nature windows OS fall back to ntlm anyway by nature if kerberos is set to default?

I would love to understand why it works and not .net. from what i read, it says since java doesn't use win32lib it works like a charm. but i'm not convinced i can make any sense of it.

kavyashivakumar commented 6 years ago

Also @steve, how do you mean ntlm is unsafe ?

SteveSyfuhs commented 6 years ago

Often the authentication process uses the "Negotiate" scheme. The client and server negotiate the highest possible mechanism both understand. This is generally NTLM or Kerberos. If you don't have an SPN configured (or for myriad other reasons), the client and server will fall back to NTLM. NTLM just needs a single key that matches the same key used in Kerberos.

.NET relies on the native Windows components to do all the Kerberos stuff. THIS library doesn't rely on any of that. It's entirely isolated from Windows, much the way the Java version is. The difference is that this library explicitly blocks NTLM because it's unsafe.

If you don't have an SPN set up for the Java version it's very likely that it's using NTLM.

Some quick searches on the topic: https://blog.preempt.com/the-security-risks-of-ntlm-proceed-with-caution https://www.slideshare.net/blackwhites/ntlm-unsafe

kavyashivakumar commented 6 years ago

had read about negotiating and falling back. had understood the basics. but thanks for this Steve. quite a helpful interaction. shame i couldn't' use this dll. but i would still continue to use this in our same domain. appreciate your help on this.