← All talks

(|(MaLDAPtive:¯\_(LDAP)_/¯=ObFUsc8t10n) (De-Obfuscation &:=De*te)(!c=tion)) - Daniel Bohannon

BSides Prishtina45:3335 viewsPublished 2026-05Watch on YouTube ↗
Mentioned in this talk
About this talk
LDAP is no stranger to the security spotlight. While LDAP is a protocol (Lightweight Directory Access Protocol) and Active Directory is the most popular directory services system that supports a subset of LDAP, the terms "LDAP" and "AD" are tightly coupled when discussing the execution, detection and prevention of attacks targeting directory services data. In the last decade, the widespread offensive value of querying AD data via LDAP was crystalized in the security community with the release of open-source tools such as BloodHound (SpecterOps, 2016) and PingCastle (Vincent Le Toux, 2017). The defensive community slowly caught up by identifying ways to log and monitor issued LDAP queries on the client device, server and even over the network (with many caveats applied to all three scenarios). However, even today these islands of LDAP visibility are not as ubiquitous as common event logs. Therefore, proper LDAP visibility mostly remains a privileged asset for those organizations that can afford a slow-growing pool of security agents and services capable of efficiently consolidating issued LDAP queries into a searchable format. Additionally, the security industry's maturity in hunting and detecting malicious LDAP usage is woefully limited to signatures targeting precise search filter substrings found in common open-source attack tools. MaLDAPtive is the 2,000-hour (and counting) quest of offensive and defensive LDAP exploration and tool-building. This research includes mind-bending depths of obfuscation across all elements of LDAP queries (many undocumented and most never seen in the wild), all baked into an obfuscation/de-obfuscation framework built upon our ground-up custom LDAP search filter tokenizer and syntax tree parser. This foundation also serves as the base for our detection framework, complete detection rule set and feature generator prepped for the data science community. Come witness the MaLDAPtive research and open-source framework: transforming LDAP from "lightweight" to "heavyweight."
Show transcript [en]

process. Uh so this talk is all about LDAP offiscation. Um and uh as I said my name is Daniel Bohannan. Uh people call me DVO for short. I'm from Atlanta, Georgia in the US. Um and I work for a company called Permiso. We are a cloud and identity uh startup security company. Um and uh it's really awesome to be here with uh so much of my team uh in the room here today. Uh I was actually uh uh colleagues with Evan Pena at Mandant for 5 years. So it's really nice to see him and uh glad you're able to make it out to to Kosovo and Albania and it's nice to hang out together. Uh

I'm a really big fan of open- source tools. I kind of have an obsession uh with obfiscating things. Uh and if you've never heard of that word, it's a hard word to say, even harder to do. But just think about it. There's many different ways now you could say hello, how are you? Ceniolon, you understand? I'm asking the same thing. And so offiscation is similar. You can take a language and offiscate the words or the order, but the meaning is still conveyed in the same way. So we're going to do that today with uh LDAP in more of a code uh code kind of perspective. Uh I'm a huge coffee fan. Uh I love reading uh and I just love

meeting people. Uh even though I'm an introvert, it's always nice uh to meet and learn from other people. Um I did not do this research alone. my research partner is not able to be here today, but her name is uh Sabaya Elazai or Sabi for short. Um and uh it was really fun to do this research together with her. She is from Kuks Albania uh currently living and working in Germany uh for a company called Solaris and uh Savi if you're watching this uh Mirita and I wish you were here. So I'm going to start with an introduction of kind of the landscape of LDAP uh and active directory. uh we're going to do a a

pretty maybe a mid-level overview of LDAP so you understand the protocol itself and then we're going to spend the bulk of the time actually looking at offiscation related to LDAP and why it matters as a defender. Um and then I'm going to talk about the actual open- source tool that Sabi and I developed for this research um and and do a demo at the end of this. So um active directory or directory access protocol um DAP started in the 80s. LDAP is the lightweight version. That's what the L stands for. Um, that was developed a couple different versions between 93 and 97. There is the open LDAP open- source uh version. What this research is focusing on is uh

Microsoft's implementation of LDAP uh as defined in their active directory which they released in 2000. We chose this because it's the most prominent one in the marketplace. So in terms of numbers and volume, this is the one that has a lot of interest. The it's a client server model. So the server side is the actual directory itself that contains all the information about your users, your computers, your conference rooms, all these uh all these things and then the client applications can use LDAP which is the actual protocol to request information from that server uh or to update uh information in that server. There have been a lot of really cool open- source tools uh that really uh

focus on using LDAP as a mechanism for uh for transmission of information. Uh so Evan mentioned in the keynote blood hound that uses LDAP to retrieve the information from the active directory and then to put it into a nice graph database to find attack paths. Um power view is another one. Ping castle. There's some really good ones out there from a defensive perspective. Um what what do we what do we do? Like what options do defenders have when it comes to logging? Um and here's where it's actually not a there's it's not a great place right now. um because of the volume of data um LDAP logs are actually not they don't just sit inside a Windows

event viewer uh like other EVTX log files um the really the only options in terms of like production ready as um Microsoft Windows Defender for endpoint has this visibility I know some other EDRs like CrowdStrike have this visibility but they don't expose it in a raw format for you to search for but they do have detections built around it uh when Sabi and I did this research uh we used Silk ETW um which is a great way to use event tracing for Windows to get that data. That's what the ETW stands for. But this is not a productionready means of getting this information, but it's what we chose to use in a lab environment because the data uh uh is

the same data source that these production systems will use. Now, let's say that you have some of these production data sources to even get this information in the first place. It's really important to look at the difference between the client side LDAP logs and the server side LDAP logs because there are some significant differences between them. And especially if you're a red teamer, if you're doing anything with LDAP, uh what you really want to realize is all this visibility I just talked about on the client side depends on the fact that you're calling that API through uh WLDAP uh 32.dll. If you use another means of uh issuing the LDAP request, there is zero visibility on the endpoint unless you

have some endpoint network visibility. Um so the team over at Falcon Force released the tool a year or two ago uh which was basically realizing you can issue uh you can get LDAP or active directory information through SOAP requests and so they released uh this kind of SOAP version uh of Blood Hound that has complete like like radio silence on the endpoint logs when it comes to LDAP. So serverside logs are important but they're they actually kind of normalize a lot of data. So some of the offiscation techniques I'll be talking about are normalized in the server side logs but it is still important uh to know that those exist. So this presentation is going to be a a

speedrun through all these elements of the LDAP protocol how it works and then some really fun and hopefully colorful and interesting ways that it can be offiscated. Um this is this research Sabi and I spent over 2,000 hours on which is too much time to spend on one topic I assure you. Um and the name maladaptive comes from the fact that the word mal adaptive is typically something that can't be it's kind of rigid. It can't be changed or twisted. And originally we thought LDAP is a pretty simple query language. How could it really be manipulated a lot? And hopefully I'll convince all of you through this talk. There's actually quite a bit of manipulation and it

actually is quite uh adaptive. So with that, let's look at the protocol itself. Um this is the RFC 4511. Um we won't go into too many dry details of the RFC, but suffice it to say, there are many different elements of the LDAP protocol. These are the four that we're going to spend our time on. Um the base object says where in the directory do we want to actually uh search for this information. The scope says how far throughout the directory do I want to search for this information. The filter is the main part. This is typically what people think of when they say like LDAP query. It's it's the actual filter logic itself. And then the attribute selection

is kind of like a select statement in SQL. It says for whatever objects you get, what properties from those objects do I actually want back? And so this is a great way if you're a developer legitimately using LDAP uh you're probably familiar with all these so that you're only retrieving the information that you need to reduce the the bandwidth over the wire. From an offiscation perspective our primary focus is offiscation capabilities against the filter because that's where most of the specific logic is. And to be fair as a defender this is also the primary detection focus. Most companies are looking only at the search filter, but there's a lot of value, as we'll see throughout this presentation.

There's a lot of value in some of the default um ways that the attribute selection is defined as well as the base object. And so, a lot of the offiscation techniques we'll talk about that apply to the filter, you can actually really selectively apply to the attribute selection in the base object as well. So, we'll we'll cover that towards the end of the presentation. So, let's look at the filter. The filter is comprised of many different tokens. Um, part of this research involved us writing a complete groundup tokenizer for the LDAP query itself. And all that means is you have a huge string, like one big string that is this LDAP query. Tokenization is saying, "All right, the

first part of the string is a parenthesy that is the start of a group. Then we have an attribute. Then we have a comparison operator, then a value." And it it's it's how you structure it's how any language parser works. um when uh when the language is being compiled or when you're typing and you have autocomplete and stuff like that. So these are all the required tokens for any LDAP search filter. And then there are two more optional tokens. The boolean operator as well as the extensible match filter. Um the boolean operator uh can be and or and a not. The extensible match filter is quite an interesting um animal if I can almost say that. uh and it's it's a little uh

unintuitive at first, but basically it's a way this is the example of um uh an and um oper extensible operator. So this value number two actually represents a flag for this particular attribute. Um so this ultimately says the object is not disabled. I I'm sure some red teamers in here have this stuff memorized. I know I don't. But just know that whenever you see these bitwise operators, this is this number is representing different flags. And so you have to go to the documentation to figure out what this LDAP uh query is actually asking for. So now usually you don't just have really really small queries like this. You'll actually combine them with one of those boolean

operators. So now you can look for any objects in the directory where the name equals sabi and the object is not disabled. So you can start to combine many different filters together. And ultimately this is all kind of written uh this is what we call a filter list. So these two filters are combined together to make a list of filters or a filter list. So when you see this actually in logs is just one really long string which again in this case is two individual filters which together produce a filter list and the whole string itself in the actual LDAP query is what we'll call the search filter. So um oh no I'm hilarious hilarious

clippy. Okay, sorry about that. Um, so what we're going to do next, um, after Are you sure? It looks like you could use some help with crowd work. Jeez. Okay. Uh, Cliff has got jokes. Um, oh, you do have jokes. I have trivia and jokes. Uh, I don't know. We could use some jokes, right? What do you call the biggest LDAP search request in history? Uh, I don't know what. 10 and a half megs. It's trivia and a joke. Turns out that is the actual size limit for an LDAP search request, which is crazy when you think about a search like a query for information. But it actually makes a lot of sense when LDAP is also used to

exchange information. So you're actually pushing data to Active Directory. If you have a large environment, 10 and a half megs, you could hit that pretty quickly. Now, as a as a red teamer, if you ever see really large data sets like this, you should immediately ask yourself, gee, out of 10 1/2 megs, how much of that is actually going to be logged for defenders? Uh, the answer is a bit scary. Not much of it. Uh, so sometimes when we're talking about offiscation, it's just understanding what are the limitations of logging and can you just push beyond that and have most of your evil payload in the later part of that uh that query so it's not even logged in

the first place. So, I'll leave that as an exercise for the uh for the red teamers in the room. So, now that we have a good quick understanding of the LDAP protocol and all those pieces, let's actually look at how we can start offiscating it. Um, and for the defenders in the room, this is going to get pretty dark, so hang in there. There's a light at the end of the tunnel. Uh, but it's it's going to get it's going to get wild first. So, as I mentioned, the filter is our main focus of offiscation. Most of our time will be looking at this and then we'll apply some of those techniques to the base

object and the attribute selection with a couple different genres of opiscation. Several of the things that we uh uncovered in this research uh were not documented anywhere. Uh and so whenever I'm talking about one of those things uh we'll have this little undocumented symbol up in the corner there. Um so those are typically they're usually the more fun ones. So we have this very simple example of the search filter and these are the seven different token types and we're going to focus on these five tokens in this order starting with the attribute itself. So uh different ways we can offiscate the attribute uh for Microsoft's implementation of uh LDAP in active directory it is case insensitive which

means that the word name we can actually change those characters. Now, this may seem really small, but there are actually defensive products that are case-sensitive in their searches and don't allow case insensitivity. So, know your tools as defenders and make sure that you're not missing an alert looking for lowercase characters when uppercase uh are allowed. Uh so, the casing is different. You can also write the attribute name like this. This is called object identifiers and the oid notation looks like this. It's all published um uh in in their documentation to say who's the organization, where is it based, what class is it, etc. Um this one trick alone would burn most detections I've ever seen with LDAP. So

if you're a red team running blood hound, look at what attributes are being used, Google the uh oid and just substitute that and you're going to you're going to win a lot just with that one trick. So let's say as defenders, you go through and you figure this out and start to look for oid notation. How would you write a reax for this? Right? Numbers and dots, right? Well, not quite. Uh you can actually prefix it with oid dot. Kind of weird. Um some official Microsoft uh tools do this but don't really talk about it much. You that's also case insensitive. And then one of the really cool undocumented things is in this notation, you can

actually prepend zeros to every one of those octets. And you can prepin like hundreds of zeros to those octets. It's actually pretty crazy. So the the really nice part is like it's cool to say our input can look like this but what really matters is for defenders what does this look like in the logs? It looks exactly like you see here. Every offiscation technique we will talk about in the client side logs looks exactly like the input which is really really um a compelling reason for uh offensive people uh to to dig into this stuff. Another thing is uh we have this feature called ambiguous name resolution. This is actually its own attribute. So if I

want to look for name equals domain admins, hopefully there are some detections looking for these kinds of uh search filters. What we can do is actually instead of calling the attribute name, we can call a r ambiguous name resolution or we could even call its object ID um as well as some of the other offiscation tricks we showed. And what this does is based on the version of active directory, it will take your value in this case domain admins and it will basically plug it into these different attributes. And so logically, it's actually going to turn it into a query that looks like this. It's not logged like this, but this is actually what happens. So most of these

are the same input value of domain admins with a wild card at the end um wherever it is able to. And in this case, since we even have a space in the value, it's going to assume we're likely asking for a person's first name, last name, and it will logically add this additional set of information. Um, again, think of all the rules that a defender might write looking for interesting attributes. Do they ever look for the ambiguous name resolution attribute? It is a really, really good indicator of someone who knows there's something somewhere in the environment named domain admins, but they don't know exactly which property it's in or they do know and just want to be evasive on

how they're asking for it. Um, another undocumented thing here that's really cool that on the last slide, remember I said the input was domain admins and logically it's adding this wild card. So what if we wanted to do something like look for uh the curb tgt or kerose ticket granting ticket. Uh, we could use the uh we could use the ANR. Um, but we could also just substring it and say I just want to look for ANR equals KRB because I know logically it will add a wild card to that and still find curb TGT. What's really interesting though is if you explicitly put a wild card, it will not look at anything else after that search.

So these last two searches are identical in terms of their actual implementation, but they're logged completely differently. Uh, so everything else after there is just garbage text to kind of throw people off. Um, if you don't want any of that wild card stuff that ANR provides, you can add a second equal sign. Um, and it will basically only look for the exact value match without any of that fuzzy matching stuff. Um, so interview for the first token which is the attribute token. We have casing offiscation. We have the whole world of object identifiers with all the crazy zero prefixes. And then we have the ANR or the ambiguous name resolution which is really cool. And we

have more clippy. All right. Do you know where else ambiguous name resolution exists? I don't know where. Microsoft. What is more ambiguous than resolving to rename your products every 6 months? Am I right? I I didn't know Clippy could tell Microsoft jokes. That's a It's a new world we live in. All right. Um, next one is the comparison operator. Most of the time this is a simple equal sign. You also have the approximately equal to which is tilda equals. Um, and this will remove some capabilities like the casing still doesn't matter but wild cards become non-functional. But this is still a nice trick if you know the exact value. But what we really want to focus on are

these range operators greater than equal less than equal. So let's say that we have something like this. We're looking for the attribute service principle name equals star. Uh this is what we call the presence filter and it says give me any object in the directory that has this defined but I don't care what the value is anything at all. Um this is also a really interesting filter in and of itself. Why is someone looking for an SPN in an environment? So if you have an attack tool that is doing this, what is another way you can logically get the same answer without making it look like this presence filter? Well, we could say instead of equals wild card, greater

than or equal to the equal sign or greater than equal to exclamation point or less than zzz. We're ultimately treating the value as a string and saying are you greater than something way over here or less than something way over here. Um, and what's really nice about that uh is you can have a lot of flexibility with what the actual values are. Now, this is a really weird thing that took us many days to figure out. This is what this is the ordering of the printable asky characters from 32 all the way to 127. What's really weird is that Microsoft has some I do not know why a very abnormal range of characters when you're doing this uh these

operators. So because casing doesn't matter, we don't need to bring the uppercase, but this the digits and lowercase characters are at the far right. This specific grouping of these special characters kind of fall into two bookends and then all the other characters fall right in the middle there. Again, I do not know why, but this is the ordering of characters whenever you're doing uh this kind of range operator. Uh we did a lot of brute forcing to figure this out, which is crazy. And this ordering is is also dependent on the kind of object that the attribute is. So when I said that we had to write a parser for this research, we also had to identify what are the types

of attributes. Is it a string? Is it a binary string? Is it an integer? A boolean. And all those things came into play to understand what kind of ordering we even need when we're doing comparisons. So that was that was a big headache. We can also say what about logically equivalent range filters. So in this case we're looking for the exact value. This is another one of those bitwise values. So we're looking for the exact value ending with 368. Well we can use the range and say if I don't want to look for equals 368 I can say greater than equal to 7 less than equal to 9. And this will also return the number 8. And then if we want to get

really prec precise we can exclude the exact value ending with 7 and 9. So now this is identical to looking for the number ending in 8. And when we wrote the tool mouth adaptive, we came up with a few different kind of recipes. Here's like a timeline way of looking at the same uh this same logic. But we have four different recipes for um either doing inward- facing inclusion with precise exclusion. Um you could say greater than less than the number itself. Uh we also like to do some different forward and backward looking range negations like this one. Um so negating the range and then removing the the negation itself. Um so all these options are randomly chosen whenever

you're offiscating an LDAP search filter uh with our tool. You can do a similar thing with strings but it's a little less precise than numbers. So you do have to deal with some uh kind of wild cards for the tail of the string but it is also possible to get domain admins uh query to work like that. So interview for the comparison operator we have approximately equal to uh we have the uh range filters and then also we can use range filters for uh precise values whether they're uh numbers or even strings which is a lot of fun. Um and if these look familiar to you these are actually precursors to the sharp hound um SPN uh uh modules if

you're doing any kind of Kerber roasting like these these aren't just random things I chose. These are parts of real search filters that defenders are looking for and those detections would really get smoked if they were if an attacker was mainly applying the kind of stuff that we're showing here. Boolean operators is the third token type and and an or is the most common, but most of the fun we're going to talk about comes with the not boolean operator. Um, so uh one of the cool things about this is boolean operators are very additive. You can actually add quite a bit. Remember a search filter can be 10 and a half megs. we have a lot

of room to play with. Um, so this is a search filter looking for country equals Albania. We can add tons of ors and ands. And in this case, it doesn't matter if I and or one thing, it's still that one thing. So this was still the same search filter. When it comes to the negation, if you not not, it's the same as it was not there in the first place. And so double negation is also a really cool thing that we can do. Um now uh the not negation actually uh is actually any of the boolean operators can actually exist inside of the parentheses of the filter itself. This is technically documented by Microsoft. They don't

recommend it. And actually if you were to run this last query here and look at the server side logs. they would add an extra layer of parenthesis to show they're technically like logically expanding that uh which is uh which is uh when we discovered this this was a really hard to find bug in our offiscation module because we didn't realize it was actually adding another parenthesy uh in the server itself and uh yeah that that was that was a fun one to find. So let's make this query a little more real life. We want to look for an object where the country equals Albania and the location or the city is either Kukus or Tyrana. Right

now we have to start thinking about what is the scope of these boolean operators because they're actually additive as you go left to right. So everything inside of this one box is affected by the and all the filters and filter lists and then the second box is an or. Well, what if we add a third boolean operator? Now we have three boxes. Um, Sabi and I refer to this as the scope of the boolean operator. And it's really important when we're calculating for every single filter. What is the combination of boolean operators that apply to that filter? And this is what we have. Albania only has the one. Uh, way down in here, we have 1 plus 2 plus

3. So, it's and or and andor or and. So, now you have to bring in a lot of boolean uh logic to understand ultimately is this thing anded or is it ored? And you can have dozens and dozens of these if you make your query even crazier. Now, here's a really cool thing. What if we added a not in there? Not only applies to the first filter it reaches, left to right. So, in this case, the knot applies to cucus but not name. Uh so even the way like we had to rewrite the parser so many times to be able to capture this accurately but it's a really cool distinction um that the not operator can be extremely confusing

to look at and in the parser that we wrote every single filter has the full chain of all the raw boolean operators as well as the simple logical result which is a simple is this an and or an or is it negated or is it not negated. So interview the boolean operator um has additive qualities double negation uh logical inversion. Uh the logical inversion for any math nerds this is called de Morgan's law. If you knot A or B it's the same as not A and not B. Uh and so what we can do here is if we start with the same exact query. What if we added a knot at the very beginning? If we add a knot there that's step

number one. Everything that's in the scope of that new knot we can say locate all of its boolean operators and invert them. So this and will become an or. That ore becomes an and. Step three says now locate every filter inside of the scope and change the negation. So these all have no negation. So we're going to add negation. If they did have negation, we'd have to remove negation. Logically, this is an example of Deorgan's law, which is the same exact filter that we started with. Making your head hurt a little bit, right? Imagine defenders trying to figure this out. Uh and so you can really add a lot of complexity very quickly with boolean operators. And so

that's the last technique that we can use uh against boolean operators. Um so oh goodness Cliffy. All right. What is my least favorite LDAP token type? I'm going to guess boolean operator since that's where we're at. Let's see. The boolean operator. Okay. Because I'm afraid of getting tied up in knots. That's cringe. All right. That that that's the last clippy joke, I promise. So get out of here. Come on. All right. Next token type is the extensible match filter. Now this is that that weird bitwise thing I was telling you about. It's like an and or an or. Um it's this really long value here and it really depends on the attribute itself as to

what the number is resolved to because that number represents a flag. Um so there are four different uh extensible match filters supported by um Active Directories LDAF implementation. We're really focusing on the first two because those are the original the OGs. And that's the and and the or. The way I like to remember it is 804 or and 803 is and 804 or rhymes. That's that's how my my brain thinks. So let's look at this example. This search filter down here is looking for the uh it's 803. So that's the ex that's the end of the flags that comprise 515. If you're doing your bitwise math, 515 is 1 + 2 + 512. So what that means is we don't have to

say all that in one filter. We could break that 515 out and and end it with another version of itself that's looking for 1 and 514 because that will capture those flags cuz it adds up to 515. 514 we could break out into 512 and two. And logically all three of those queries I just showed you are the same. They're just checking piece by piece for the same flags. If this was originally an ore, we would add an ore boolean and make sure that we have the 804 for the ore. Now, this becomes really complicated when you look at something like this. This is from the Power View open source offensive tool where they're looking for this really big value. And

so, you can imagine there are not infinite but a large number of possibilities that we could break up this logic into many many different filters. Um, and so in our parser, we basically break this down. So, you can see the actual flags as like a like a bit mapping to understand what flags are actually being set by a filter. Um, we also have what we call the bitwise breakout. Let's say that you're not looking for an and or an or. You're looking for an object that contains explicitly this value, explicitly those 10 or 12 flags. You can convert this to an and or logic using this kind of formula. We're going to take all the one

bits and we're going to and them like this. We're going to take all the zero bits which equals that much larger number. We're going to ore them and then negate it. And then we're going to and both of those things together. Logically, this produces the same result. Um, and then we're back to the first one. Now, we can take those numbers and start regrouping them into smaller and smaller random uh groupings of the flags as long as the bitwise math still checks out. So, there is a crazy amount of possibilities to get the same objects uh using this. Um, and this is the one technique I'll say that even with our parser, it's really hard to write

detections for because our parser will accur accurately show per filter what the flags are. But now you have to do grouping across many different filters and these these filters are all close together like uh there's an undocumented depth of 99 layers in LDAP queries. So you can have multi,000line LDAP queries and trying to piece that together is actually uh quite difficult. Which is why looking for the presence of obiscation is also a very very valuable detection approach for defenders and one that I'm pretty passionate about as opposed to looking for all the ways a specific known bad value can be written. All right, the fifth and final token is the value itself. Um uh this is uh

should be no surprise casing doesn't matter in most cases. That's a pun I didn't even think about. Um anyway, in most examples casing doesn't matter uh except for some of these specific attribute types. Um so here we're looking for the curb uh ticket granting ticket casing doesn't matter. Another thing that we can do is we can prepin zeros to values that are integers even if they are negative like that. Um the date is a really weird thing as well. Uh we noticed that some dates um had no milliseconds. It was just like Z. Most of them have 0000 Z. We actually found that it's quite a bit more flexible than that. As long as there is a uppercase Z

after that dot you can put literally anything you want. So we can say date time is February 17th 2008 Kazan ple this is a valid date time according to LDAP which is hilarious and you know what this uppercase Z isn't required to look like that you can use hex representation so now 5a also is uppercase Z so you can get really creative with your uh with your date times um next we kind of introduced you to hex encoding there hexen coding is really for handling special characters that can cause issues like uh you know uh breakout uh attacks and injection attacks etc. But you can hex encode regular characters too like the Z character. So for curb TGT we could

replace those T's with /74 which is its hex value and this is exactly how it's logged in the client side logging server side logging it actually resolves this nicely but again most defenders are looking at client side and so this is a great way to bypass a lot of detections that are looking for exact strings. Um, this is a weird little undocumented thing. Microsoft says that uh hex representation is a slash followed by two characters. This was the weirdest bug that we found. If this if the hex values start with zero, which there's only 16 of those, uh, you can actually drop that leading zero and slash 0 is null. You don't have to do 0 0. This

might sound like a really silly thing, but like literally this was a bug that only occurred like one out of 10,000 times in our unit testing. Uh, and so it was a really difficult one to smash, but we got it. All right, wild cards. You can substitute wild cards in strings again for most attribute types and it will match accordingly. This is not new. This is usually one of the first things that we look for in offiscation research. Um, Hope Walker, she's a a researcher at Spectre Ops and she wrote two really good blog posts about getting started with Active Directory queries using manual tools. Uh, and she even mentions this as a technique for looking

for sensitive terms like password or administrator. using substrings of those words in wellplaced wild card characters. So in review for this last token we have casing offiscation preprinted zeros time stamp kon fun hex encoding and wild cards. So the good news is that's pretty much all the offiscation that we uncovered um for for LDAP. No, I'm kidding. There's more. Okay, that was just for the tokens. What do we do now? We have offiscation for these token types. What about the bigger picture of an LDAP search filter? Well, we can add different layers of group start and group ends to make that filter even bigger. We can add whites space, which is more than just whites space.

It's new lines, it's tabs, it's whites space. We can also add completely garbage filters in here. And this is a really really crazy this is one of the hardest things we had to do in the parser. Normally, you have to have the the parentheses balanced. And we thought you had to have the parentheses balanced within the value itself or within the attribute itself or the extensible match filter. But technically, as long as they're balanced throughout the whole filter, you can have uneven parentheses in the attribute that ultimately match up with something in the match filter, in the value, etc. So, getting this to parse correctly was a a miracle to say the least. Um, but yeah, you can really

hide hundreds of of things uh in your search filter to make the the real truth hard to find. So, that's the end of the search filter offiscation. We'll quickly look at which of those techniques apply to the base object and the attribute selection. almost skipped through here. So if we have a simple base object like this, this should these colors should look familiar. This is an attribute. This is a comparison operator and a value. So for the attributes casing doesn't matter. Same for values. We can do hex encoding. Um for the uh we can use the oid notation filled with the oid prefix, the prepended zeros. We can also add whites space. And weirdly again

undocumented if the value doesn't contain any hex, we can encapsulate it with double quotes. not single only double but it's all logged this way in client side logs which is really really interesting um for the attribute selection um this is a real set of attribute uh selection values for uh one of the sharp hound uh commands um you can use random casing uh the oid syntax is also allowed with the oid prefix zeros etc you can also add whites space only this is again undocumented you can add whites space only if you're using oid notation And this is logged this way. So a lot of tools will strip this out of the um the event logs and then give you an array of

attributes. This attribute will actually contain whites space at the end of it. Uh which you want to be really careful if you're parsing these um in in your uh defenses. Um you can add duplicate attribute names and in fact you can add complete garbage attribute names. It will just disregard them. This is all logged in client side logging. In server side logging it will drop duplicates or attribute names that don't make sense. Now, the last one's really cool. You can just add a wild card, which is interesting. And actually, maybe this is kind of a way to figure out how old somebody is in it. What do you even call this character? Like an asterisk,

technically a wild card. Uh my father was an engineer his whole life. He would call this Splat. Like the like there are different names and nicknames people have for this character. Um what Clippy says is there's even more when it comes to LDAP. This is the same as if you don't define any attribute selection at all because it will return all the attributes. Here's what's really crazy. If you add a wild card, if that's the only attribute you defined, then it will in the logs, it will say the word null. That's that's what it comes back as. If you have any other attributes defined and you have a wild card, it will change that wild card to this square brackets

all with list. But here's the kicker. Our wild card is at the very end. In logging, it's going to bring it to the very beginning, but it's going to attach it to the end of the first attribute. I do not know why. So if you are parsing these logs, you need to find the first attribute, look for that string, pop it out, and change it to a wild card and recognize that ultimately it doesn't matter which ones were defined. Every attribute is coming back with this LDAP filter because that wild card was there. So how as defenders do we deal with this? Well, we wrote this custom parser. We did it in C as a state machine

parser. We had never written a parser before. I would actually recommend it. It was really fun. It was a really different uh it was a really different exercise. um certainly a lot of frustration um but a really rewarding experience. So as I mentioned before you start with a tokenizer so you have a whole string as input and you have to tokenize and say what are the different tokens and then we have to structurally say how do these tokens relate to each other and we use what's called a syntax tree or a parse tree. I know those are not technically the exact same but the similarities are are okay for the way that we're explaining it here. Um so we

start with a string. We then tokenize that to all the pieces of the LDAP uh uh protocol itself. And then we have some some different things like automatically decoding hex values or if we're using the oid notation, we do all that encoding in line. And lastly, we put all this detection capability in this one function that we call find evil. And that will basically take it, do all the parsing, uh, and then show you all the rules that we're matching for it. And we spent a lot of time rewriting this parser to make it as fast as possible. So just on just on this Mac right here, uh, parsing this, uh, 100,000 instances of this LDAP search filter was under 4.4

seconds. And that's to also run every one of those through all 65 rules that we developed at the time. Um, so let me do a quick demo of this. Uh, and then we will get to the coffee break. Uh, so we like to have a lot of fun with colors. When you spend this much time working on one project, you have to make it fun. So here's an example of taking uh, search filter. Um, and we can see the tokenization. If we do the uh enrich tokens, we now have things like it's resolving the oid notation in this this uh LDAP context object that we created. If we go ahead and look at that object,

um then we'll see even more information. We can also parse the entire filter in the whole search filter into individual filters and we can see here's all the normalization for the booleans. We have dictionaries and lists for all the tokens involved depending on what's more efficient for you to traverse based on your needs. We can look at the boolean operator context object and see the logical boolean operator for all those weird and or the Morgan's laws stuff. We have all of that uh parsed out here as well as every single value. We give you token by token, character by character the kind of value whether it was hex, wild card, etc. You can pipe it into

find evil and see all the rules that matched and the piece of the filter they matched on. Um and then if we go a little bit further um we have all the bitwise stuff we talked about with all the flags. All that is resolved in the parser and you can access um again a list or a dictionary of a bit map value to understand what are all the flags that are set uh in this individual one even to the point that we can write detections on that. So this detection says if your attribute resolves to user account control whether you said that whether it was an oid and this one flag is set no matter how many uh bitwise

operators you did then we're going to mark this as a detection and here's an example of all those uh all those flags in the format that we have it. Um another cool thing is with all the wild card tricks that we do like I showed you how the hex characters are automatically decoded but what about wild cards like like how would you deal with wild cards because there's nothing to decode. Well, we allow you to input a list of highinterest values and we test wild cards by expanding them and searching. So, uh this we we can write detections that say the wild cards and the way they're in the string technically match one of the high value strings like

domain admins. And so we are able to do detections like that even that weird uh implicit wild card. Uh we have that as its own detection as well. Um which you should really never ever see in the wild. Um the very last part of the demo is uh if you want to use this tool in the interactive mode, this is what it looks like. um where you can uh put in any kind of LDAP uh filters and walk through all these offiscation and deoffiscation techniques layer by layer and so we can do a quick test. So we expect to get three results back. Um we can uh go through all the menus and the menus also

support uh wild card so you can randomize which ones you choose, what level of offiscation you apply. So we just did some simple whites space offiscation. The deoffiscation also goes through piece by piece so that you can remove little bits just the same as you can add little bits. Um, or you can just say, "Get me everything. I want the mess sampler platter." And now this is your search filter. And we can test it and see it still returns the exact same three results. So, as you can see, it can get pretty crazy. We always run the detection score there. And you can run find evil to look at a detailed view of all the detections with color coding to

show which pieces were detected and why. And lastly, uh, if you want to use an interactive tool to find your menu, your your recipe of choice, you can then copy it and in a non-interactive fashion, you have access to all these same underlying functions. Uh, again, PowerShell is the wrapper that allows us to do all the cool pipelining stuff, but all the real guts of the project are written in C purely for performance. So um this is a tool that Sabi and I released uh at Black Hat and Defcon and we actually had a really interesting release process which is we released everything you see here except for the offiscation module. Uh and actually we

have still yet to release that module because we wanted to give defenders a head start because defenders don't even have access to the logs in the first place. If an attacker doesn't even have to try hard at this why give them a machine gun and so our hope is that defenders will take this information will get the logging they need. will use these tools and set up the ability to get that visibility. Um, and then depending on CFP acceptances, we'll release the offiscation module accordingly. But it's been done for over a year. It's pretty crazy. And uh, we also released a huge corpus of offiscated examples so you can see would I detect this if I found this in my

logs. Three main takeaways. Search filters are great for visibility. Attackers love it. Um, it is a huge volume of data. So defenders getting awareness and access to this data is a big problem. Which again is the whole reason why we even risked our CFP acceptance in the first place by saying we're not releasing the thing that you really want, but we are convincing you the research can stand on its own even without releasing the offensive part. And we're really happy that Defcon and Black Hat agreed with that assessment. Um and lastly, the rest of what you saw is released as an open source tool. The full parser, all the uh detections is uh completely open source and available.

Um, and I want to leave you with one last unofficial um, takeaway. So, keep in mind uh, we Sabi and I did this uh, this whole presentation uh, for the the Black Hat Las Vegas US audience. And so, we really had a lot of fun putting some little Albanian treats and Easter eggs throughout the presentation. So, I thought, you know what? We should end the presentation officially by sharing our favorite Albanian proverbs. So, that's what we did. So my favorite Albanian proverbs proverb is ro samallet which means live long like a mountain. It's a a greeting kind of a prosperity and well-being. And I'd like to think that we just climbed a mountain together. Like I know I'm a little out

of breath but we we went through a lot of material that took thousands of hours to produce. And I hope that you got something out of it. But the most important thing is we all started from different places in this room. And so I hope that you help other people climb the mountain that they're on right now and learning the next step of whatever part of security they're interested in. We have to help each other out. Uh we talk about finding adversaries, but we can't be adversarial towards one another if we don't really care for the human that we're talking to. And so I really hope my desire is that you take that away from this talk. Um, and if Savia

were here, she would share her favorite Albanian proverb, which is yapes, which means please no more paper clips. Obviously, that's not a real one, but uh, the American audience thought that was pretty funny, and I didn't know how that would land since everyone would know what's being said. So, um, with that, I just want to say from myself and from Sabi, thank you so much for your time, Dardon, and everyone really had a lot of fun. I'll I'll be around I'll be around all day today and tomorrow. So, please come by, say hello. Would love to talk. And mine and Savi's information is here. Here's the URL to the uh uh maladaptive framework. And again, really appreciate

your time. I know I'm standing in the way of the coffee break. Do we have time for a question or should we go straight for coffee? >> One question and then we go for um break. >> One question. >> One question or with Okay. Right. Make it quick. No pressure. >> Hey Daniel, great talk. Thank you. >> So the question is basically what is LDAP? I haven't seen it like referenced in the wild. Maybe because I'm not like of the industry. Yeah. >> So I just want to see where it is in the wild. You mentioned it's a protocol. >> Yeah. >> So I just want to see what layer does it live, you know? That's

>> Here's where I'd fail an exam on the network protocol stack. Yeah. So, think of any company that has active directory for managing their users, their computers, all that. They're going to have some kind of act some kind of a directory to house that information. So, open LDAP is maybe the second most common option, but active directory is Microsoft's implementation that is the most common in terms of numbers. And so, those organizations would have all those things connected and would transfer information like last password reset, all that stuff. So again, think of it like a specialized hierarchical database that has all the information about all the users and computers in that company. That's active directory. And then LDAP

is the protocol that any application can use to ask questions. So I could say uh give if we were all working at a company together, I could query the active directory and say give me all people who are from Federai, right? And it would look across and find that information and return the data back. This is good for administering systems. It's great for attackers because they can also say, "Show me everyone that has really interesting privileges or who have not reset their password in the last 12 months or things like that." So, it's a way to access that information for both good or evil. So, yeah, great question. Thank you so much and I'll be around for

any questions. So, should follow me there. >> Thank you, Daniel and Sabi.

[ feedback ]