The method proposed by LaNMaSteR53 relied on connecting to the IPC$ share of a domain controller. While there is a discussion to be had regarding the use of administrative shares, we will table that for now and focus instead on defeating LaNMaSteR53 and his desire to control our network.
Perhaps the most obvious way to detect this would be to review your Windows Security Event Logs on your Active Directory domain controllers. But if LaNMaSteR53 is successful, he will eventually gain domain admin privileges and clean up his tracks. Unless you are pumping your Windows Security Event Logs in real-time into a SIEM system of some sort and correlating based on failed logon attempts, chances are you may not see the activity at all or you may not detect it until it is too late. So, how can you defeat or detect the presence of this activity without a snazzy, expensive SIEM system?
One way would be via an Intrusion Detection System (IDS) such as the open source Snort product. With a pretty simple rule, you can monitor responses sent from your Active Directory domain controllers to clients requesting authentication. If the count of all failed authentication attempts from any given client in your enterprise exceeds a certain threshold in a specified period of time, you may be under attack even if you do not have a large number of disabled user accounts, etc.
If an organization locks user accounts after, say, 5 failed logon attempts, one might choose to configure a rule to look for more than 4 failed logon attempts from a single client in a period of 2 minutes. The following Snort rule accomplishes this purpose:
alert tcp any 88 -> any any (msg:"Possible domain user spraying detected"; \
flow:established, to_client; \
flow:established, to_client; \
content:"|05|"; offset:14; depth:15; \
content:"|1e|"; distance:4; within:1; \
content:"|18|"; distance:30; within:1; \
detection_filter:track by_dst, count 4, seconds 120; \
reference:url,foxtrot7security.blogspot.com/2011/12/defeat-domain-user-spraying-brute_28.html; \
classtype:attempted-user; \
sid:1700000; \
rev:0;)
Like a lot of things in Security, you may end up with some false positives. Some tuning of the “count” and “seconds” thresholds based on your local environment should cut out most of the noise while allowing you to detect truly malicious activity.
Now for the truly curious who are asking the question, “So how does this rule work?”…
Microsoft adopted Kerberos as the preferred authentication protocol for Windows 2000 and subsequent Active Directory domains. While a comprehensive discussion of Kerberos 5 is beyond the scope of this post, there is a good Microsoft TechNet article that explains it pretty well. Kerberos 5 is also defined in RFC 5120.
By default, Microsoft Active Directory has a Kerberos feature called pre-authentication enabled. Pre-authentication makes offline password guessing attacks very difficult and, during the course of the user authentication, a Kerberos error is generated if an invalid user password is presented as part of the pre-authentication process. The Kerberos error generated in this case is KDC_ERR_PREAUTH_FAILED. This error code is set within the context of a KRB-ERROR structure is defined in the RFC as follows:
KRB-ERROR ::= [APPLICATION 30] SEQUENCE {
pvno [0] INTEGER (5),
msg-type [1] INTEGER (30),
ctime [2] KerberosTime OPTIONAL,
cusec [3] Microseconds OPTIONAL,
stime [4] KerberosTime,
susec [5] Microseconds,
error-code [6] Int32,
crealm [7] Realm OPTIONAL,
cname [8] PrincipalName OPTIONAL,
realm [9] Realm -- service realm --,
sname [10] PrincipalName -- service name --,
e-text [11] KerberosString OPTIONAL,
e-data [12] OCTET STRING OPTIONAL }
These responses are delivered from the KDC (aka the targeted domain controller) to the client via TCP port 88 which is the registered port for Kerberos. All we need to do is inspect the packets returned to the client for the proper pvno (protocol version number), msg-type and error-code to be able to detect a failed login. Then we simply count the number of failed logins versus our threshold values for count and seconds and—viola—we know what is going on in our network!
So how does this work via the Snort rule presented? First, we start with a packet capture of a failed login and look for the packet containing the KDC_ERR_PREAUTH_FAILED message. The payload might look something like this:
0000 00 00 00 e5 7e 81 e2 30 81 df a0 03 02 01 ......~. .0......
0010 05 a1 03 02 01 1e a4 11 18 0f 32 30 31 31 31 32 ........ ..201112
0020 32 31 32 31 31 30 35 39 5a a5 05 02 03 0e cb ab 21211059 Z.......
0030 a6 03 02 01 18 a9 06 1b 04 58 58 61 64 aa 19 30 ........ .XXad..0
0040 17 a0 03 02 01 02 a1 10 30 0e 1b 06 6b 72 62 74 ........ 0...krbt
0050 67 74 1b 04 58 58 61 64 ac 81 90 04 81 8d 30 81 gt..XXad ......0.
0060 8a 30 49 a1 03 02 01 0b a2 42 04 40 30 3e 30 09 .0I..... .B.@0>0.
0070 a0 03 02 01 17 a1 02 04 00 30 0a a0 04 02 02 ff ........ .0......
0080 7b a1 02 04 00 30 09 a0 03 02 01 80 a1 02 04 00 {....0.. ........
0090 30 1a a0 03 02 01 03 a1 13 04 11 58 58 41 44 2e 0....... ...XXAD.
00a0 58 58 2e 43 4f 4d 6f 6f 6f 66 75 73 30 3d a1 03 XX.COMdo ofus0=..
00b0 02 01 13 a2 36 04 34 30 32 30 05 a0 03 02 01 17 ....6.40 20......
00c0 30 06 a0 04 02 02 ff 7b 30 05 a0 03 02 01 80 30 0......{ 0......0
00d0 1a a0 03 02 01 03 a1 13 1b 11 58 58 41 44 2e 58 ........ ..XXAD.X
00e0 58 2e 43 4f 4d 64 6f 6f 66 75 73 X.COMdoo fus
One hint: Wireshark understands Kerberos packets (and many other protocols too!) and makes this much easier and far more understandable. Hence, using Wiresshark is highly recommended when doing protocol analysis.
Let’s look at our rule again and break down the important parts in the context of the packet. We start with:
alert tcp any 88 -> any any (msg:"Possible domain user spraying detected"; \
We are looking at “any” IP sending traffic on tcp port 88 since we are inspecting Kerberos traffic. The directional arrow specifies that we are looking for the traffic to originate on tcp port 88. (More advanced topic: you could improve rule performance by creating a variable in Snort equating it only to your domain controllers. Your sensors would have less traffic to inspect that way!)
The next line says that the communication should be part of an established TCP session between the client and the server and that the response traffic should be destined for the client from the server:
flow:established, to_client; \
Now we start our content inspection. Omitting the first 14 bytes of the packet (essentially Kerberos stuff we don’t care about, but which is defined in the RFC) we can find our protocol version number or pvno. We expect this value to be hexadecimal “05” since we are inspecting Kerberos version 5 traffic.
content:"|05|"; offset:14; depth:15; \
Moving deeper into the packet, we find the msg-type field and look for a decimal value of 30 as specified in the RFC snippet shown above. Expressed in hexadecimal, this value is “1e”. If we get this value, we know that we are now dealing with a Kerberos 5 error message.
content:"|1e|"; distance:4; within:1; \
At this point, we need only make sure we have found the pre-authentication failure. Buried deep in the packet 30 bytes beyond the msg-type field, we find the err-code field which should contain a decimal value of 24 if this is a pre-authentication failure. This corresponds to a hexadecimal value of “18”.
content:"|18|"; distance:30; within:1; \
And to track how many responses are going to a client, we use the following line:
detection_filter:track by_dst, count 4, seconds 120; \
The track by_dst says to start a new set of counters for each client IP requesting authentication. Count and seconds are our thresholds for alerting. Remember, we will actually alert on the 5th failed authentication attempt if we specify a count of 4 because this is saying MORE than 4 detected events.
I didn’t spell it out above, but there really is a method to the madness when you are creating these types of rules:
1. Make sure you understand the problem you want to solve.
2. Capture traffic that illustrates the problem.
3. Look at the traffic and understand it thoroughly based on the protocol definition.
Make sure you understand the traffic flow characteristics and directional nature of the traffic.
4. Identify anything in the traffic that would allow you to spot the problem you are trying to solve.
5. Write a rule using what you have learned.
6. Test and implement the rule once you know that it captures the traffic you want.
Above all, don’t get impatient when doing your research. This is hard work and requires a pretty detailed level of understanding. You will often need hours of time to solve a single problem especially if you are not intimately familiar with the protocol or process in question.
With that, I am signing off for 2011. Hope everyone has a safe and happy 2012.