 So, I have a tendency to have way too much content, so this is going to be like 30 minutes for probably three hours of content, so buckle up. We're going to start real fast. Nothing really interesting about me. Manager at KPMG, we have some KPMG folks, also known as Mr. Unicoder, founders of Ring Zero Team and bunch of other stuff. Some GitHub code that I wrote over the years and also have a Patreon for like three years now where I produce a lot of content regarding red teaming and a lot of love to share with everyone. So first of all, I guess we're just going to address this difference between red team and typical intrusion tests. I guess the key point here is that typical pen testing will not assess the blue team effectiveness, right? So we're not trying to see if they're good at detecting you or anything like that. We're usually not going to do any social engineering and stuff like that. So when we go to red team, this is slightly different, right? We want to actually assess the detection capability and responsiveness capability of a company. Because of that, usually we're going to have to deal with some security products such as EDR, antivirus and stuff like that. So that's the major difference from a client perspective and also the tester perspective, right? We have to deal with those products. And today we're going to definitely focus on one of the phase of a red team engagement. There's much more to it, but lack of time. So we're going to focus on gaining access, right? You have to have your code running at some points and we need to find a way to evade those products as red team. So for those of you that are familiar with typical C2 framework, you probably have seen something like that in the past. This is typical shell code. This is something that you're going to see with meterpreter, cobalt strike or any other of the framework that are quite popular. No need to tell you that EDR and AV are really good at detecting those patterns. Those bytes are known forever. I just want to point out the MZARUH. Just keep that in mind for the future. We're going to have to go back to those bytes a bit later on. So this is well-known. This is highly predictable. Of course, from a detection perspective, this can be detected easily. There's a lot of stuff on your net about evasion and bypass, nothing fancy here. Personally, I love to not follow the rules, right? If we look at the typical tricks that's been used over the years, you're going to come across XOR loop, which is super typical. It's like the dummy encryption kind of thing, using RC4 because it's a super simple algorithm to implement, AS2 or GZIP base 64, layers of crap on top of your shell code to actually achieve evasion. There is a big issue with that. Most of these will end up creating what we consider encrypted shell code, which bring me to this next slide. Entropy is a thing. And I know that there's someone that's going to present or have presented a talk about entropy. Long story short, high entropy, which means that the sample is highly random, is bad. They know that there's a lot of encryption. And legitimate software tend to have really low entropy in the sense that there's a lot of pattern that repeat themselves, right? So if you encrypt half of your payload, there's a good chance that your entropy will be terrible. You definitely want to avoid that. So you want to make sure that your code look as repeatable as possible. And one of the key things that I love to do instead of encryption, because as I mentioned, I like to not follow the trend. What I do is fairly simple. I'll create a dictionary of words, whatever you pick 256 word at random. And then what you're going to do is you're going to transfer all your bytes into the offset associated with the word in your list. So what you're going to end up with is something similar to this, right? Let's say your shell code is zero, zero, two, zero, one, zero, zero, one. And you have your table that contains all the offsets. In this case, we only have three because we only have three different bytes. So table first, second and third. And the mapping of the shell code will be converted to first, third, second, first, first, second, which are actually words, right? So you may have realized by now that we converted the shell code into actual words, which is quite useful, right? Especially when it comes to entropy, you're going to have a pretty decent entropy because these patterns are going to repeat over and over and over and over on your cell phone. Especially if you're looking at stage less payload, you're going to end up with like a three megabyte sample with a bunch of word that are harmless on their own. Now, how you actually deal with this? Lately, I've been using a lot of C sharp for my payload. So you can literally just use a built in C sharp capability, which is array index of the point is you're going to provide your needles, which is whatever bytes you're trying to map back to whatever it's in your table and you're going to get the offset, which will eventually be converted into the byte associated with the original shell code payload. So if you look at the final code, if you implement this in C sharp, this is pretty much what it's going to look like, right? Overall, nothing really fancy. You have your table, your mapping and the loop here that actually just convert back the index into this byte area, which contains the final data. And then the typical memory allocation, virtual protect, martial copy and just executing the function pointer to actually point to the shell code. So this is pretty standard stuff. The only major difference here, I'd say, is really the use of word instead of something else, but you can be creative. The whole point here is avoid encryption. Like this pattern is not going to be as detected as any encryption pattern because this is not something as popular as the other type of code that we see. We need to understand something too. It's really important, right? When we talk about EDRs and antivirus solution, when they actually detect that kind of code, when you drop your pedal on this, they're going to check for signature, right? It's really important to understand that the compiler will do a lot of magic for you under the hood. And you need to understand that sometimes the compiler will lie to you. We're going to have a brief look at what the compiler may actually do for you. Keep in mind that the same concept can also apply to C sharp. C sharp have a metal language called MSI, which is the up cold version of whatever you're going to write in C sharp that is interpreted by the dot net environment. So just a quick example. If you look at GCC, for example, for standard C code, right? You can just compile it. If you're trying to use the optimization switch, in this case, the 01, you actually ended up with totally different codes. So if we look at this example here, we have this piece of C code that doesn't really do much, right? It's just an XOR loop, typical stuff when it comes to encrypting shell code, right? This is obviously weak encryption, but the whole point is about just to hide what you want to have executed memory later on, right? This is the assembly code of this. Keep in mind that at the end of the day, what's inside of your binary are those bytes here, right? So they can build signature on that. That's in this case here, you have the four loop with the XOR OF, which is actually living in here. So they could actually build a signature on those bytes here. The point is you want to obviously modify those bytes. That's kind of the whole point. So if we look at the same code with optimization one, like I show earlier, you'll see that we have two different pieces of code, right? Notice here that the XOR is now here. You have this byte here, which is 8030 OF, and the original one was 83F1 OF. Depending on how the compiler did its magic, it totally changed the code and the signature is totally different. Just keep in mind that sometimes using optimization may change your code. When it comes to writing malicious code, it may also affect the way that the code behaves because you may actually try to trick it. But this is really important to understand something. The compiler is definitely smarter than you. Sometimes you're going to write super complex code when you're going to compile it. Well, it's going to end up being the exact same code that you had at the beginning. This is an example of why the compiler is definitely smarter than you. It's going to try to guess for you. So if you declare a variable like I equals one and you're trying to assign XOR I into the variable A, well, it's just going to figure out that A will contain whatever the result of one XOR with I is, right? So they're just going to remove all that code. So it's not going to change the signature. This is something to keep in mind. The trick usually when you want to make sure that the signature will be different, is to make sure that you don't have any static value in there. So the compiler cannot predict what the value is going to look like. So here's an example where we have some code. It's the same thing as what we saw earlier, right? This is the more complex version of this code, which is exactly the same result at the end. It's just that we use much more complex code. So as you can see here, we have the dword key equals dword size XOR with dword size. This is a typical trick. It's actually going to zero out the value. And you may believe that, oh, this is cool. I'm going to change the signature of my code with this XORing technique. At the end of the day, the compiler doesn't know what's inside of dword size, but it knows that this operation will turn out to just create a value with zero. And then you have all the magic. You know, we do plus plus, which is going to increment the value by one bit shifting by four and then reducing it by one. At the end of the day, this is going to end up generating OF. So you may think, oh, that's pretty cool. My code is super smart. Hided the fact that it's XOR with OF. Well, the compiler figured it out for you. So as you can see here, you ended up with the same signature XOR, the byte, which is part of the loop with OF because the compiler was able to understand what's going on in there. If you look at the code on the right side, pretty similar. The only difference is the key in this case here, which is dword size, is actually unknown because we don't negate it with XOR or something like that. So technically, the compiler doesn't know at this point what it's going to look like. However, it's still smarter than us. It figured that this whole operation will end up causing a NOT, which will actually revert all the bad. It's a NOT instruction. So this was replaced with a NOT on this specific register here. At the end of the day, it's still a victory for you because you ended up having a different signature. Right? So it's just a matter in this case of using different piece of code. You change a line or two and the whole signature is going to be totally different. So main takeaway when it comes to obfuscating your C code or C sharp code, look at the actual final result, you may be surprised, right? So if we go back to the original code, right, which is the one that we have on the right and the fully optimized one, well, you're going to realize at this point that the signature is totally different. At the end of the day, those two pieces of code will yell the same result. But signature wise, they're totally different. And it's really important to actually assess the code that you generate through your compiler. So this is in our example here. If we remove the optimization on the last one that we saw, because keep in mind that the one on the right was also optimized when compiled. If we remove it, you'll notice here that it's much bigger. So we had the original 83 F one of F and now this whole operation here has been modified for like a sub EX XOR. I cannot really see anything, to be honest. And here is, but the XOR is 31 C one. So at the end of the day, totally different signature, right? Just by modifying a bit of your code, right? The downside with that, it's pretty annoying, right? Let's be honest. If you have to rewrite all your tools to make sure that you obfuscate all the C codes or whatever language you use, it's going to be painful. But keep in mind that this is doable, especially if you maintain your old tool set. That's why I usually love to write my own tools. So I have full control over the source code and I kind of understand what's going on to a certain extent. There's that. So let's just go back to this original code. For those of you that are not necessarily familiar with most of the modern C2, they have a concept which is called sleep mask, right? Sleep mask is the ability to actually encrypt the data in memory. So when the beacon or the agent is not doing anything, it's going to sleep in memory encrypted. There's no point of actually looking at that piece of code because it's not doing anything. So they hook the sleep method. There's multiple method that you can hook. But the point is, when it's living in there, it's going to be totally encrypted. So if there's memory scanning going on, well, they're not going to see anything. However, there's a catch. Most of the code like that have no effect when it comes to memory scanning, because if we look at all these engine work, you're going to end up actually copying the code in memory at several places. Just a quick hint, final here. We definitely going to have the final shellcode in there. It's going to live in memory. That's going to be the original shellcode, right? The Marshall copy, too, is going to copy this into an unmanaged variable that can be later reused to call. So we at least have two variables that actually contain the clear text shellcode, which is definitely something that you want to avoid, right? And you can always validate that by actually dumping the process. So if you just dump a process and you search for patterns, remember the MZARUH that I mentioned earlier, without surprise, well, we have two instance of it. The reason why we don't have a third one is because the agent itself is actually encrypted in memory at this specific time, right? So that's the expected deliver. That's the whole point of a sleep mask. So million dollar question. Is there a way to have an invisible shellcode within your process? Well, the answer is yes. Here's the trick. It rely on trying to get rid of the final content and the allocated content later on. How can you do that? Well, there's two things. We're going to have a thread that's going to be spawned. So the whole point of having a thread is you have execution in parallel to another flow of code that's running. The whole point is the whole shenanigans of memory copying and execution is going to be inside of that thread, which I call let's have fun for whatever reason. And then inside of your main, you're just going to call that thread. So far, nothing fancy. And then just to be sure, I'm also going to modify those variable to make them global to the class. So I'm sure that there's no copy because there's a bunch of magic going on, especially with C sharp, where you have clone of memory and stuff like that. So if it's inside of a thread and it's not necessarily global, you may end up with an extra copy of code without even knowing. Once again, curiosity. Here's the key. Feel free to just down your process and analyze what you see in memory. You may be extremely surprised of what you'll find in there. So by making them global, it's going to definitely help you. Right. And for those of you that know me, I love C, not the best at writing C, but I love it. So here's the million dollar solution. There's a specific keyword in C sharp called fix where you can actually use raw type like bite pointer. So everybody says there's no pointer in C sharp. Well, technically you can cheat and everything is a pointer life, but that's another story point is you have your main. You call your tread with let's have fun. So you're going to get your beacon running in memory. Then you're going to sleep for whatever I usually put 12 second just to be safe to make sure that it's going to happen. Then you enter this unsafe piece of extremely terrible code. But the point is you're going to generate a string. In this case, it's just going to be the length of whatever the shell code originally was. And I'm going to replace this with a bunch of CC. You can just randomize that be creative. Doesn't matter. And now just for the fun of it, I'm going to retrieve the raw pointer to the actual data that we created earlier. So PTR is now the raw unmanaged pointer to data, which is the managed environment variable. And what we're going to do is fairly simple. Keep in mind that the tread is running in parallel at this point. 12 seconds later, well, we definitely have the shell code and memory was copied by the whole framework associated with whatever payload you use. And we move back to let's clean up that memory. It's super simple. All you do is you copy that string back into those two locations because at this point, the execution was completed. The beacon itself copied itself somewhere else. So you don't need the original payload. You empty them with this and you move on with your life. And if you actually check what's going on, extra point, you can also even change back the memory permission because this is also well known. Read write executable memory will get you detected depending on whatever product they use. So you can even revert back the permission on the code to typical executory, which is what you will expect to see. And you're done. And if you actually done that process memory, well, all you're going to find is a bunch of CC. So the beauty of this is there's no trace of your shell code because at this point it was modified in memory. So keep in mind that whatever tools you use, it's always important to make sure that you do your cleanup. And most people don't actually think about that. And memory scanning is getting more and more popular because we're getting more and more creative. So if you don't clean your original clear shell code after the execution, well, there's no point of using fancy technique. They're just going to find the sample in memory, simple as that. Keep in mind that, as I mentioned, same can be done in C-Shot, right? MSI is like C. C is also like the human version of assembly. Same goes with C-Shot, right? So you have the upcode associated with a bunch of instruction. Which are our IL instruction, which is the middleware kind of thing between C-Sharp and the engine and the upcode is what's understood by the runtime environment associated with C-Sharp. So we can also technically obfuscate these, right? The beauty of this is there is built-in capability to allow you to actually get the IL code of a method. So I'm not going to go to too much details here, but you can use a sample like, let's say you created your malicious payload and you're going to get all the IL bytes. The point here is you can actually obfuscate them, right? By extracting all the IL, you can actually, in this case here, use a simple technique like a plus three. So every byte is going to be added plus three. So it's going to become gibberish. And it's actually not going to point to the original one. The point is you can actually patch the original software. So instead of obfuscating the C-Sharp code that you write, you actually obfuscate it a layer above and closer to the actual engine execution. And you're going to get that MSIL obfuscated code instead of a typical C-Sharp, you know, using random string and AS tricks or anything like that. At this point, the code is not even functional within the binary. And the beauty of it is what you get after that is you actually hunt for your own method that was modified here. There's a pattern. So obviously I just need a couple of bytes that are unique enough at the beginning of my modified code. In this case, it's O3, 75, 8A, O3, whatever, this whole thing. And what I'm going to do is I'm going to read my own process memory. I'm going to loop through all the memory until I found that pattern, knowing that it was plus three. Well, all I have to do is when I found the actual location is to revert it back to minus three, right? So you're using unmanaged functionality within your managed code to modify the managed code back to the original state if it makes any sense. Hopefully it does ish. And then the beauty of this is you get to the point where you have your MSIL code back to its original state and you don't have any C-Sharp obfuscation because you're level above. So no need to use fancy variable shenanigans or trying to bypass AMSI. You don't care. You're just below that. So they're not going to understand any of that shenanigan because it's just going to be a bunch of random bytes, right? But there's something to consider too. And that's something that people tend to oversee a lot, right? That's cool. You know, you obfuscated your stuff. You have plenty of really interesting concept going on. But the thing is C-Sharp actually going to leave a bunch of artifact in your code that people tend to really forget about. One example here is the PDB. It may sound silly, but for those of you that actually do red teaming, you may actually end up being part of non-malicious database just because of the PEB that you use all the time. If you're using the same VM over and over and you have this, you know, random username or anything like that, you may actually be there because by default, it's always going to be part of your assembly in .NET. So that's definitely something that you need to consider. There's a lot of tools available for that, but you should always clean that. Another really important thing is you can use all the obfuscation in the world. Every time you actually have a function that are used, even if it's C-Sharp or unmanaged function, they're going to end up being unclear in the assembly. So we have an example here where the sample is actually going to do assembly load. And when it performs assembly load, it's going to invoke a specific set of method within the framework, right? So you can end it up with something like mscorelib, load, invoke, GUID attribute, debug, all that stuff, which is known pattern, right? So you may actually come up with a super cool evasion technique and be like, but I got detected right away by, you know, MDE or whatever and me maybe wondering why. It's probably because your imports have some well known pattern and you don't even realize it. That's also something to keep in mind. So if we go back to this kind of code, keep in mind that we modified the MSIL, but we could also technically modify more than that, right? We can get the offset of all of that and make sure that we actually also modify it. So at runtime, it's going to be patched back to the original code. But when you look at it on this, it's just going to be gibberish, right? That's something really important to also consider. It's like, for those of you that are familiar with C, you have the concept of import table. It's pretty much the same, right? There's a lot of product that use your import table to actually create signature. It's the same, right? There's different technique and patterns that you're going to find when you look at malicious code. And this is no exception, right? So make sure that you also assess the final binary to actually get this kind of output. Does it look like something that we know is known to be malicious? If the answer is yes, well, you may have guessed it. You probably have something malicious going on. Same goes with the GUID. There's a GUID associated with your installation. Funny enough, I know cases where legitimate APT group were uncovered because of that. They found that the GUID was used in a malware sample and also a legitimate software because probably the guy forget about it and he ended up compiling malicious code on this production machine or whatever. So this GUID is also the key. Keep in mind that you either replace it with gibberish or you make sure that you make one that looks legitimate but it's not yours, especially for red teamers. You probably have one VM. You generate the same code over and over and over. And if you test the same client or you're an internal red team practice, well, they may ended up starting creating rules just based on this GUID associated with your Visual Studio environment and they're gonna get the tech you. Doesn't matter what you're gonna do. They know it's your signature. It's unique per installation. So that's also something to consider. Point is you can have super complex fancy code with super advanced technique but if you forget about those little details, well, you may actually get detected right away. And funny enough, right? If you don't believe me that these are actual use pattern, if you take a look at Microsoft's security product in memory, just a quick note on that. If you have the trusted installer privilege, you can do pretty much whatever the hell you want with Microsoft detection product. So you're allowed to actually dump the process memory. And if you look for a certain keyword, let's say PDB within the memory. Well, obviously it's a bit of a mess though, but you realize that these are some of their patterns that they know to be malicious, right? Of course, some of them are obviously malicious like a QZminor source release Bitcoin miner It's pretty obvious that it's probably a Bitcoin miner but there's some that may look a bit more subtle like rubbish DN loader RC release. It's probably just another tool and they realize that this pattern is always there because every time that group or that individual create a malicious sample, well, they forget about the fact that they have this specific user. It's like here, for example, job goals release loader, like loader can be literally everything but it was unique enough that they were able to actually make signatures. So back to this kind of code here. Once again, you can come up with some interesting technique and I've been told that I have five minutes left. Thanks you, Martin. You can come up with a really cool technique and realize that they caught you way before they got to your fancy code because you forget about those little details. Another example, release shell code. Out of question, how many times you think you name something shell code in your life and forget about the fact that you name it shell code? Probably a million times. This is an example, it's in there. They're gonna catch you just because of that. Doesn't matter what you did afterward, it's there. So this is something really important to keep in mind, right? At the end of the day, as I mentioned, it's always about being creative and making sure that you check what the code that you create is gonna result in your binary, right? This is a typical reflective loader in C-sharp. It's super simple. It's literally three line of code. You can get unmanaged code in memory or whatever dot in assembly. This is something that I love to use. However, the pattern is super known, right? So if you look at your data in your XC, remember earlier we saw the MS Core Lib load app and get method and all of that. It's always gonna be there. So you need to actually make sure that you get rid of this. Another thing to keep in mind, right? This is a bit small, but the point is this is something that I do often too, is heavily confiscating my code. I have a little tools. If you're interested, I'll be more than happy to share the concept afterward because we're running out of time. But point is, I'm gonna use a switch case concept. So it's all automated and lead generated for me. So it's gonna take a function or a method that I provide and it's gonna create this crazy switch case statement. So this code is massive. It's literally the same as this at the end of the day. The only difference is all the gibberish that was added. There's a bunch of check counter. There's magic going on just to make the code more heavy. However, here's the catch. Doesn't matter how complex it is. You still have the MS Core Lib load pattern in there because this specific method has to be called like that. So there's nothing that you can do unless you hide it, which bring me back to something like that where you definitely wanna hide the code and don't forget to hide what I like to call the import table within your EXE when you're dealing with .NET because patterns are easily detectable there. Another thing that you can do, if you look at the app code, as I mentioned earlier, this is the app code. So the equivalent of assembly and C sharp, the small version is just that, right? The switch case, one that I'll show you is this one. It's definitely bigger. So this help, right? Pattern matching is gonna be different, but if you look inside of that, you may come across part of that pattern too. So definitely it's gonna make pattern matching much more difficult, but still something that can cause you problems. So obfuscating on top of it will definitely make sure that this pattern is not gonna be present in the bigger, more complex function. I guess at the end of the day, I've been saying this for a year. I believe that obfuscation and evasion is really an art. And I think lately in the industry, people are trying to come up with super complex technique, but at the end of the day, sometimes they just forget the root, right? It's like little details will get you cut. So before you're trying to come up with some complex technique where you scan your own memory, patch MSIL code and all of that, make sure you actually understand what's happening under the hood when you generate a C sharp payload or just a piece of C. What are the artifact left in there? And hopefully, well, do they achieve the goal of showing you that there's a bunch of little artifact left in your code? And I guess be creative and enjoy life. And that's it for me.