
Amen.
All right, welcome to B-Sides. Today's speaker, Kyle Adams, evading code emulation, writing ridiculously obvious malware that evades AV. So welcome, Kyle. KYLE ADAMS-
Good morning. Welcome to Evading Code Emulation. I'm Kyle Adams. And I'm going to talk to you today about malware, antivirus, and a cool feature of antivirus called code emulation. Before I do, I want to talk to you a little bit about what inspired me to do the research for this talk. About a year and a half ago, I was about to give a conference talk over in Virginia, and it was to demonstrate a full APT attack vector from start to finish. We had to do something new. It had to be some sort of technology that hadn't been seen before, at least hadn't been seen to this group. I happen to have a USB rubber ducky sitting around, and I figured that would be a great way to
start this thing off. So I coded up the USB rubber ducky, which for those of you who don't know, it's made by Hack5. You plug it in and it's a scriptable keyboard, so you can get it to type whatever you want into the computer once you plug it in. But it looks just like a USB stick. So I programmed that to go download some malware from the internet and install it on the client. And then I decided to do the whole, you know, somebody left their USB stick in the parking lot trick, bring it to the receptionist, and hope she plugs it in. And the idea, even if she has Autorun turned off, even if
she has AV, we should still be able to infect the machine. So it's about two days before the talk, and I'm setting up the VM that I'm going to use to actually give the demo. And it's the first time I'm plugging in this USB rubber ducky. And I actually went out and wrote a whole bunch of malware sample to go test with this. And I figured it would just fly right through, because it's a brand new piece of malware. Nobody should have a signature for it. So it should just get right underneath the AV, no problem. Unfortunately, the first time I plugged in the USB stick, AVG, which was the antivirus I chose to install
on that machine, flagged it as a Trojan. So I was pretty freaked out because I had two days to prep this talk, and obviously if the AV is going to detect the virus, I'm dead in the water. So I spent like six hours frantically trying to figure out how to get around AVG, and I finally did, and I thought I knew how I did it, but I really didn't. It gave me the idea that I really wanted to go back and figure out exactly how code emulation in AVG works. How exactly did I get around it? And what other ways could I have used to get around that?
Then that brings me to why am I giving this talk? pretty interesting information that could be used to go write a virus. So the main reason I'm doing this is because viruses don't just crop up overnight. People actually have to go and sit down and write them. And as security researchers, we tend to look at this from what are the viruses that we've seen and how are they trying to get around us. Not necessarily how are they going to do it tomorrow, just how are they doing it today. And I think it's important for all of us to have the perspective from both the malware author and also from the AV vendor. Because
If we always stay just looking at what they did in the past, we're never going to be able to get in the way and prevent that from happening again. And you guys are in the best position to come up with new ideas to disrupt that process. And after all, we only ever see the malware that failed to evade us. So we're not actually seeing any of the malware samples that figured out the right way to get around our modern technologies. So what I'm going to show you today is writing a piece of malware from scratch. I'm going to show you a demo of the malware, which happens to be a CNC bot. We're going to
test it with a bunch of popular AV clients that you might want to evade. And then we're going to evade some of them because we will get detected. And finally, we'll prove that we got through all of the antiviruses. And I'll give some recommendations on how code emulation could be improved to prevent this from happening in the future. And if we have time, I have a little experiment that I did with what I figured out. And that should prove to be interesting. So with that, I'm going to don the role of a hypothetical malware author. I do not actually write malware for profit. So the first things first is what do we want this malware to do? And the trick here is for this research to work, this virus
had to be as complex as possible and as realistic as possible. So I wanted to make sure this was actually something I could legitimately use to infect people and to extract value out of. So we started with the CNC bot, and the goals here were that we want encrypted network communication, so if the IT guy is watching the traffic coming out of a machine, they can't figure out that it's coming from malware. Or even if they could, they couldn't write a signature to figure out which machines are sending the same packets. We want it to persist. So we want it to auto start with the machine. We don't want it to ever go away. We
want privilege escalation. So generally, we'll install as a user account. We want to get up to an administrator account so that whenever we send instructions, they launch as an administrator. We want it to be stealthy so the user doesn't know it's there. And we want to be able to update it and extend it without having to release new copies of the malware. And then, of course, because it's a CNC bot, we also need a CNC server. This is really where the brains go. The whole power of a CNC bot is really how you delegate the instructions out to it. As long as it's extensible and updatable, you can always make it do more things. But
the power is how do you delegate those instructions to say, you do this, or this group of people over here, you do this. But the goal of this whole talk was not to smuggle a CNC server through AV, so I didn't really spend a whole lot of time here. Just being able to send an instruction to a single bot or to all of the bots and to get the error and connection logs from each of the bots. And finally, utilizing only Microsoft utilities, not because we have to, just because it's fun and kind of humorous that you can do all of this with just the stuff that's installed in Windows right when you load it.
And of course, evading modern antivirus so that we don't get caught.
That's fine, actually. You can leave it off for a moment. So what does writing a virus actually look like? Now, I was going to actually record myself with a camera and show you in high speed writing the virus. But I couldn't figure out how to get the camera to work. So I grabbed a video off YouTube that I think shows the process quite accurately.
So this is Swordfish, 2001. And this is, you know, after having written a virus now, I kind of know what you should do to not get detected and how you should build this thing. Really, all he needs to be doing is moving that cube on the left just a little bit more, and then the virus should fall together and it'll work great. But he's going to spend quite a while doing this. And for those of you who haven't seen the movie, the idea here is he's trying to get a worm for his evil boss to go do something on the internet for him. And this is how he's building it. This is like looking in a mirror, honestly. This is what I was doing all weekend.
The only accurate part about this whole thing, I think, is the fact that he drinks alcohol during the whole process.
He's also offering a computer. He is. With nine monitors. I don't know about you guys, but I can't focus on nine monitors at the same time. Then again, he has nine cubes. There we go. We got our cigarettes and alcohol.
And it's just more of that. Seriously, that's not how viruses are written. It's actually far duller than that. I'm not going to sit here and write it. You guys could watch me write 2,000 lines of code, but it's just not really going to be fun. Let's talk about how we actually go about doing it. We already talked about our requirements. What do we want this thing to actually be able to do? The second question is what language are we going to actually program this in? Again, I wanted a big challenge here. I wanted to make sure that this was actually detected. We could have used C and C++, and that's what most malware authors do. It's actually really effective, and you can hide your code very well. That's
off the table. VBScript, I really don't hate myself. I can do everything that I did in VBScript, but no way in hell am I doing that. And then there's JavaScript. And why not? JavaScript a virus. It should be fun. And for those of you who didn't know, you can actually write a full virus in JavaScript as of 1996. We know what language you want to write it in. The next thing is the tools that we're going to use. We have the WScript engine, which is included with Windows and is always enabled. This is the engine that's capable of running native JavaScript and VBScript, and also has a nice little wrapper called WSF, which allows you to mix and match JavaScript and VBScript in the same file.
Then we have iExpress. The problem with JavaScript, or VBScript for that matter, or even WSF, is that the file extension looks really suspicious. Not only that, but the icons look suspicious. We really want to wrap this thing in something that will make it look a little bit cleaner to the end user, and that's where iExpress comes in. I was actually floored when I found out that Microsoft includes this utility in their operating system. I don't know if any of you have actually heard of it or tried to use it, but if you just type iExpress in the start menu, this tool will pop up. What this lets you do is actually wrap any number of
files you want in a self-extracting cab, which would be great on its own. But then they'll actually execute any instruction you want once the cab is extracted, and they'll clean up all the files when it's done running that process. Basically, it's the perfect malware wrapper. Let's just test to make sure we can actually create a JavaScript file and run it on the operating system. So for those of you who are familiar with JavaScript, the syntax is a little different. It's actually the full JavaScript language, but you don't have things like the alert call. You don't have DOM manipulation or any of that. What you do have is the WScript object, which is your interface into
Windows and into the WScript engine. You can also create ActiveX objects like the HTML HTTP request object you might remember from HTML development. So we write our little script, and all it does is pop up a dialogue saying, you are running a virus. From the command line, I can just double click this JavaScript file on my desktop, but I ran it from the command line. You can see it pops open. You are running a virus. Now we want to test and make sure iExpress works. We go ahead and we wrap our JavaScript file in iExpress. We go through all the little dialogs, tell it when we're done. We want to execute wscript.exe and pass in the file that we just extracted. When we run
that, you'll see it now has testalert.exe. When we run it, we get the same dialog that pops up. We've just verified that we can, in fact, write code with JavaScript and iExpress. We want to make sure we can actually do things to the operating system, because after all, this is a virus. We need to actually interface with the OS. This is just a quick little script I grabbed off the Internet that gets your process list and shows you the first eight things in Task Manager. And so when I wrapped that in iExpress and ran it, you can see I get the top eight processes in the task manager. So where do you go to actually
learn all these things? How do you know how to go manipulate registry keys and things like that through JavaScript? You can go to IRC channels, there's probably a whole bunch that are kind of underground. Tor, there's probably a lot of onion services out there that you could go to. There's Usenet, lots of hacking groups on Usenet, and of course forums on the public internet. But as it turns out, it's much simpler than that. You can go to Stack Overflow and MSDN and get all the information you need. It's surprising because I've been using MSDN and Stack Overflow all the time. I read these people's questions. They're always very helpful. But when you start to wonder,
how am I going to go manipulate a registry to create an auto start process, you have to figure out how you're going to phrase that question so that people don't realize what you're actually doing with it. When you try to look at it in that perspective and you go look back at the questions on Stack Overflow and MSDN, you start to notice there's a lot of people in that boat. So these aren't too bad. I've seen much, much worse, especially ones where people are like, I'm trying to password protect my computer from my brother. I need to know how to hide a process from Windows so that you can't even see it in the task
list, things like that. But here's an example. This guy's asking, how do we execute an application as an administrator from JavaScript? Because remember, we're running as a normal user. We need to elevate. So somebody helpfully just came on and said, oh, here's the two lines of JavaScript, or actually VBScript in this case, that you need in order to launch a process as the administrator. And really all you have to do is change set to var and this becomes JavaScript. They're very, very similar. JavaScript is just better. Here's another one from MSDN. This one is where Microsoft itself actually posted an article on how to create auto start tasks in Windows. And auto start links in the start menu, which is one of the techniques I used, only gets
you user level privileges. You can't escalate to an admin. But if you create a task in the system, you can say this task should run always as an administrator. As long as you created the task as an administrator, you then have persistence as an administrator every time they boot the machine. This is really helpful. We're totally going to use both of those suggestions. What did I learn on Stack Overflow and MSDM this week? We've got creating auto start links as a user, creating auto start tasks as an admin, testing for user elevation, elevating privileges of a script, forcing shutdown of the computer, read, write, delete, and copy files, get system information, HTTP posts and gets,
running a GIF file as a JS file, getting users home directory, creating hidden files and directories, writing a binary file from an HTTP response, launching another process, hidden or visible, and file locking. And this was all I needed to write a virus. Literally four hours of time. So this is probably one of the only viruses you'll ever see a flow chart in UML for. But I'm going to go through it because showing you 2,000 lines of code, besides the fact that I don't want to share that code, this is just easier to see. Basically the workflow here, this is just the embedder, so this is what's going to get us a process that runs on the system, not disrupted, and always starts up every time and as an administrator.
When we execute the script, remember the user just downloaded it or they got it off an email or something like that. They're just double clicking on it, probably just running as a regular user. count in the fact that they might have run it as an admin if they were stupid. So what we want to do is first check to see if we've installed before. Are we resident on the system yet? If we're not, we're going to copy ourselves into the system. We're going to check to see if we're elevated as an admin. I have a laser pointer here. There we go. Then we're going to check to see if we have the auto start user link. So this is in the start menu, the startup folder. is the
easiest way to get something to auto start in Windows. And you can do it as a regular user, you don't need any sort of admin privileges. So we're just going to copy ourselves into that folder and hide the file so the user can't see it. Then we're going to wait three days and prompt the user for elevation. So we're going to wait three days and the next time they boot up we're going to pop up a UAC dialogue and say, do you want to run this process as administrator? And if they say no, that's fine, we'll try again in three days. Eventually they're going to say yes and we'll get through. Once we get admin privileges, the next thing we want to do is create the auto start admin
task. We're going to delete the auto start out of the startup menu and create the auto start task to launch us as an administrator. Then we fall into our payload. If the user ever runs this again, we're going to see that it's already installed, we're going to see that it's running as an admin, and then we're just going to go straight to the payload. So what is the payload? The payload is very simple. It's probably the same for almost every CNC bot out there. But the payload starts by fetching instructions from a remote server. We parse the instructions and then we check, you know, do we have any instructions to execute? If we do, we kind of go in a loop of executing those instructions. Once we're done,
we wait for a period of time. That's configurable. And then we send all of the responses to those instructions back to the server. and then we go ahead and start again and if one of the instructions had said you know script go terminate yourself we'll go ahead and terminate and we won't get any more instructions but otherwise we're just going to keep looping around and around I'm not going to go into too much detail on the actual code here, but just to give you an idea, there's a bunch of different components. This is object-oriented JavaScript, so everything is defined as objects. We have an embedder, which is the whole process I showed you for getting admin access and auto start. And we have the client, which is the CNC
logic. You can swap out the client if you happen to have another virus you want to use the same embedder on. And then there's a whole bunch of support classes in here as well. Just briefly go over those. We have transaction, which is an object that deals with transactions. We want to make sure that this malware doesn't run its payload in two separate processes. We need a way to make sure that we only enter certain pieces of code once. Then we have app state, which maintains a state between executions. When the user reboots the machine, we need some way to know what was the last instruction we executed, how far along the install path are
we, do we still need to prompt for elevation, how much time do we have left before we have to prompt for elevation, things like that. Then we have WinAPI which gives us all the active X objects we need to work with. There's only two, the file system object and the Wscript shell. It gets us the user's directory and their start menu folder. Then we get the downloader which is just a generic class for doing HTTP requests and getting content. We have ENG crypt which is what's going to provide our network encryption. So, eng-crypt, the whole idea here is not really encryption, it's obfuscation. We want to make it hard for the admin to write a signature that will detect the traffic coming out of this bot. And
the way we do that is by taking JSON, which is the path here, running it through eng-crypt, and it generates something that looks like English text. Really, each word here just correlates back to a byte of the input. It's all randomized. If I keep encrypting this thing, it'll keep coming out as different English text. It's obviously not real English, but good luck trying to write a signature that will match that and not regular English text. Then we have the embedder. Again, really all this does is it copies into the user's home directory. It creates the auto start link in the start menu. It waits two to four days. Then the next time they boot up, we prompt for elevation. If we get it, we create the auto start
admin task. And then we maintain app state, we ensure that only one copy is running at a time, and we execute the client, whatever the client happens to be. And then we have our client, which is the actual malware payload. This is that thing that goes and fetches instructions and executes them. So what are the instructions that we've actually included in this bot? Now, as I had mentioned, this is extensible, so we can always add more instructions, but what we have to start with is you can reconfigure the client so you can go check for instructions every second versus every minute. You can install new commands, you can uninstall commands, you can download files off the internet, execute those files, delete those files. You can eval arbitrary JavaScript
within the bots process. You can echo, which is just a debug thing so we can make sure our bots are working. You can exit the bot so you can tell it to terminate itself. You can restart the script in case there's a bug. You can shut down the machine and you can uninstall the bot to remove every trace that the thing created on your system. This is kind of what it looks like. I kind of blacked out the IPs, bear with me. Here's the CNC server and it's a real-time JavaScript single-page app. I can just leave this running and as things happen they'll just pop up on my screen and I can react to them.
Here we have three bots that are registered. Two of them seem to have died because they haven't contacted us in a couple days. One of them is only a second old, so it's still contacting us. You can see here I send the echo command and I get back the response. Same command just says isEchoed equals true. Then I send a command to say go execute calculator. Really this is just a proof of concept here. You can see the calculator will open on the client. And again, just to give you a little visual, this is kind of how it works. We've got our CNC single page app. It's got JavaScript APIs that are going in the background and hitting API.PHP saying, give me all the latest information. Who's been infected?
Who can I talk to? What are the responses to the commands I've issued? And then you've got all the clients that are also contacting API.PHP to say, what instructions should I execute? Here's the responses to my previous instructions. So we'll give you a little demo. It'll be hard to see with the screen this way. But this is our victim's machine. Right now he's just looking at the file, Microsoft Photo Touch-Up Pro 2.4.1. It looks to be from Microsoft. The file is looking somewhat legitimate to this guy. Hover over it, it says Microsoft Corporation is the company. He's cool, he's going to go ahead and run it. I could have actually wrapped some legit app in this thing, but I didn't. All I did was throw up
a dialog trying to print mspaint.exe. Just wanted them to believe the app didn't work. But if you can look over on the right, we now have two bots over there. One of them is the old one. We have a fresh one. Clicked on it. We're going to send it the echo command. So you can see the echoes right there. We're waiting for the response. We get the response back. So now we're going to go try to execute calculator.exe. So we're sending it the execute command, and we're sending it a path of calc.exe. And so give that a second, and you'll see calculator pops up on our victim's desktop. and so we're good to go. Now here's what happens three days later when the user boots up the machine. They
get a UAC prompt. This one says the publisher's unknown. If you actually keep wscript.exe the same name, it'll actually say the publisher's Microsoft Corporation. I actually renamed it to printer.exe because I wanted to hide it, but that turned out not to be a great idea. So now you can see over here I'm now going to launch regedit.exe. Now regedit.exe will not load unless you have administrator privileges. And so you can see now, after we allowed the script to elevate, and now it'll be elevated from now till the end of time, we can now pop open regedit, and you can see I can go in and actually change registry keys and whatever else, which again, I would not be able to do unless I had got administrator privileges.
So what have we done? We've written the CNC bat, we've written the CNC server, we've utilized only Microsoft utilities to pull it all off. but we don't know how it holds up to AV. So here's the candidates that we're going to test against. And now before, when I was doing the original talk and I was setting up this whole environment, I had not done any AV testing. So just assume here that I have not done any AV testing. I don't know what these results are going to be. So we run our AVs, and what the fuck AVG caught us. So again, brand new virus. Nobody has a signature for this thing. How the hell did
AVG catch us? And you'll also notice Symantec, Eset, Avira, and Sophos did not catch us. So take that as you will. So here's what actually happened in AVG. Again, I'd never seen it before. It gave it the name JS here dot dropper. It's not really a dropper, but the fact that they flagged it is a problem for me. And I should have known what was going on just from reading the name of the virus, but I didn't. So we'll keep going under the idea that we don't know its code emulation. We don't know if it's I Express, we don't know if it's just JavaScript, maybe AV hates JavaScript, or maybe it's something I did in the JavaScript. The next thing is I need to figure
out which of these things is causing me problems. Now keep in mind this is exactly the type of thing the malware authors in the wild are actually doing. They run into the same problem, they test their viruses on AV, they'll see which ones catch it, and then they've got to go through this process in order to figure out why it's catching it. Ruling out JS. The first thing I want to do is just make sure if I pack a JS file with IExpress, does anything happen? Do I get detected just for that, even if the JS has nothing in it? And I don't get detected. So it's not IExpress and it's not JavaScript. So the next thing would be, oh sorry, that one was just wrapping calc because
we didn't know if JavaScript would be a problem. The next one is with JavaScript and we don't detect that one either. So not IExpress, not JavaScript. It's something I did in the virus that caused AVG to detect me. Here's all the pieces that we included, all the different classes. This makes up about 2,000 to 3,000 lines of code. It's a pretty chunky virus. We're going to remove all of it. We're going to start with a blank JS file. We're going to go add each one of these classes back into the JS file and see when AVG starts detecting us. Go ahead and do that. Bam! It's the initialization code that got detected. None of the other logic got detected. What is the initialization
code? embedder.start. This really confused the hell out of me because there is no way that there is a signature for embedder.start. That would be flagging false positives like crazy. This really makes me wonder what is going on. This is kind of where I was at the level of the talk. The first thing I want to do is just make sure that, what are they doing here? How are they detecting me? It could be some sort of emulation or static code analysis or something like that. They didn't bother flagging it until I added that line. But is it because I added that line or because that line is getting executed? I don't really know, so I'm going to wrap it in an if statement that will
never evaluate to true, meaning that if I ran this on the desktop, embedder.start would never get called. And you can see we don't get detected. So clearly it's doing something following the logic of this application to figure out if it's malicious or not.
I don't really know if it's emulation or static code tracing, so what we're going to try next is something that would be a little harder for static code tracing to be able to get through. In this case, all I'm doing is writing a function that's going to prepend some value to some random string and then make sure the results of that function match the string with the prepended value. When I run this through, I still get detected. I don't really know what's going on yet, but we'll keep trying. So this time I'm going to try writing something a bit more complicated. This is just a cheap hashing algorithm. It just goes over and permeates some of the bytes of a bunch of zeros. And you can see it's not
very good. It only changed four bytes of the original string. So we ran this through, and the hope is that any sort of static analysis probably won't figure out what's going on here and won't get past that line. But I still get detected. Again, really confusing, not really sure what's going on, but maybe it's emulation. It looks more like emulation. The likelihood they got through that function with just static code tracing is low. This time we're going to try just doing 100 rounds inside of our hashing algorithm. I'm not really expecting much to change. I just wasn't happy with the fact that my hash came out with almost all zeros. Maybe they figured that out.
I don't know. So we're going to go ahead and put that in. And I didn't get detected this time. So right now I could stop. I could just say, put that code in front of your virus. You will not get detected by AVG. But I'm curious why this worked. And this is, by the way, the same thing I did for the conference talk. This is what I figured out. And I had no idea why this worked, which I eventually figured out. But it does work. Again, I don't really know what's going on. Why did they not get through 100 hashing algorithms but got through one just fine? It's really strange. The ideas that I came out with here, maybe it's a rounding error, maybe they're
doing some string splitting, or they don't have string splitting. Maybe it's order of operations. I didn't put parentheses around any of the math that I used. Maybe it takes too long to execute, or maybe there's too many loop iterations. Well, rounding error, there's no floating point math, so that's unlikely. String splitting, I also tried an array instead of using a string of zeros. It didn't have any impact. I added the parentheses around the math. That didn't change anything. And I know this isn't the most expensive function that they have to call, and they somehow got to the malicious bits of this thing, so that's probably not the case either. But loop iteration sounds promising, so let's go explore that a little bit. I know 100 is going to get
past AVG, but I don't really know where that limit starts. The next thing we're going to do is just decrement that one by one and find out when AVG starts detecting me again. We got all the way down to 92 and then I got detected. I know between 92 and 93 I'm somehow hitting a bounds there. If we look at 18 loop iterations inside the main loop and we multiply that by the main loop, we end up with 1656 loop iterations that this thing was capable of getting through. But I don't know what the upper bound is, I just know it's somewhere before 93. So we're going to go run the experiment again, only this
time I don't really know if it's capping my loop iterations, or single loops, or nested loops. So I'm going to collapse this thing down to just one loop. There's no more inner loop anymore. And we're going to try the same test, and we're going to see how many iterations we can get away with. And as we go down this thing, it turns out 1656 was the exact number that we were allowed. So we got lucky on the first try. But I don't know now if it's loop iterations or if it's instructions. So the next thing I'm going to try is adding a couple instructions inside my loop and see how far down I have to
drop that loop iteration count before AVG starts detecting me again. And it's clear I got all the way down to 10 iterations from 93. So clearly instruction count is the issue here, not the number of loop iterations. So now we have to figure out how we're going to use this to actually bypass AVG. And actually, I don't really know how many instructions I'm allowed to execute because it's a little fuzzy. You have two loops. Doing a whole bunch of things in there is like incrementing one instruction. Is it two instructions? Is it three? You don't really know. So the next thing I tried was writing just a single loop that does 80,000 iterations, which should be well above what I'm allowed. And I'm just going to
do what I think is one instruction inside that loop. As you can see, I had to jump around a bit, but eventually I settled at 10,916 instructions that it's willing to execute. If m plus two is two instructions, then that's somewhere double that, but who knows. I now know how many instructions AVG is willing to execute in my virus to figure out if it's good or bad. But what I don't know if it's per function or per entire script. What I did here is took away the function, and now I'm just doing two loops, 10,000 iterations each. If I do the first loop, I shouldn't max out the 10,900 and whatever instructions I'm allowed. But
when I do the second loop, I should max it out, and then AVG should abort. We're going to go ahead and run that, and we get not detected. This proves that we now have an instruction limit on the entire JavaScript file. Once it gets past that instruction limit, there is no more evaluation. Not being able to execute the whole script is apparently not an indication that this is malware. So I wonder if I really actually even need to do anything in my loop. And as it turns out, JavaScript allows you to write a loop that does nothing. So I wrote a loop that just loops around 80,000 times, and then I call in better.start. So the only change to the virus I made was this one for loop. And
not detected.
So there you have it, 26 characters to evade AVG. But why stop there? There's probably a whole lot of things they can't handle, and I find this fun. How does AVG actually work? The more we know about AVG, the more we can figure out about other antivirus engines and how they're doing code emulation, which apparently is not very good to start with. So the next thing we're going to try is sleeping. So AVG has like half a second to test my malware. So maybe if we force them to wait 10 seconds, we're going to totally evade them, because they'll be like, oh, in the first half second, it did nothing bad. So we run that,
and we get detected. This is not really surprising to me because we actually analyze malware at Juniper all the time. We skip sleeps in most cases. So that's a pretty common tactic. AVG is probably just skipping the sleep. So let's get a little bit more defensive about this. Let's say we're going to loop forever until 10 seconds have passed. And to be nice to the CPU, we're actually going to try to sleep inside that loop. Because if we don't, it'll just turn the CPU until 10 seconds go by, and the user's definitely going to notice that. In this case, all we're doing is taking a time delta and we're going to loop until the current
time is greater than 10 seconds past that and we're going to sleep for 100 milliseconds between and then execute embedder.start. And I get not detected. That's great, but then I realize, shit, we're probably exhausting the instruction count again. Because if it skips the sleeps, it's going to do this loop more than 80,000 times. It's going to bail out and we're not going to get analyzed. And what I want here is mutually exclusive evasion tactics. I don't want to always use the instruction limit because that's not interesting. So let's keep going with this. The next thing I'm going to try is taking a time delta. I'm going to sleep for 10 seconds and then I'm going to see if the current time is at least five
seconds past when I took the date delta. So if they skip this sleep, They're going to get into this if statement. It's going to be like a millisecond of difference, and we're going to quit the script right away. And what the fuck? I really don't know how AVG got this at this point, because there's no way they're spending five seconds. I don't know how they could possibly fake the fact that five seconds have passed. The only thing I can think of is that w script.quit is also getting skipped in addition to sleeps. So this time, I'm just going to wrap in better.start inside of my if condition. and now there's nothing for them to skip.
They can't skip a quit. They won't execute in better.start unless at least five seconds have passed. And not detected. So the assholes, they are in fact skipping wscript.quit. But that's a lesson to learn. Don't use wscript.quit to abort your script. And maybe we can actually use that to detect AVG's code emulator in the future. So let's keep on going. That's pretty much how that one wrapped up. We just have the loop with embedder.start inside. So the next thing I'm going to try is the engine. So Wscript is its own JavaScript interpreter. There's V8, there's Rhino, there's a whole bunch of JavaScript interpreters out there. And after doing lots of web development, I've come to the realization that all these
interpreters implement the toString function just a little bit differently. What you're supposed to get when you call toString on an object in JavaScript is the code body of that object. If it's a native object, something they had to implement in something other than JavaScript or just an API that they provided through the browser or through Wscript, they won't actually return the code that made that up, probably because they can't. But they will return you something that looks like code and it'll be just the statement native code. I went ahead and wrote a script that pops the ActiveX object.toString in the dialog to see what it was supposed to be. and there's the escaped version of it right there. All we're doing is checking to make sure that the toString
value returned by activeX object is the one that Wscript is going to give us. And we're not detected. We just found yet another evasion technique. That one took all about 30 seconds. We're going to keep going though because I'm curious what exactly are they returning from toString. Because if I know what engine they're using, maybe I can find an exploit in the engine itself and infect the machine simply by having my virus scanned. Which would just be awesome, but I did not go down that road, so I'm not revealing anything of that sort. And I probably wouldn't be able to give this talk if I did. But right here we have, so we're just going
to check. We're going to do a blind SQL injection attack against this thing. We're going to test character by character, and if AVG detects me, because I'm saying, does the first character start with a percent sign, if AVG detects that, I know it starts with a percent sign. And then I go to the next character. Does it start with percent one? No, percent two, and so forth. And this could take a very long time, so I wrote a script to automate this. You basically just change the code a whole bunch of times, build like a thousand derivatives, throw it in a folder, have AVG scan it, and it'll tell you exactly which ones didn't get
detected. So this one did get detected, so I know at least it starts with the percent sign. So we're going to go ahead and do this, and this took way, way a lot of iterations here, but once we got all the way to the bottom, we figured out that it's returning this %5b object, %20 function, %5d.
But that's active XObject. Maybe they did something funky with that one and not with the other one. We're going to try one more just to be safe. This one is just creating a string, getting the split method off the string, and getting the toString of that. Again, we go through the whole process. It does actually return me something a little bit different. Now I have two samples of what AVG's engine is going to give me for a toString. You can see right here, this is the non-escaped version. Here's what AVG is giving me. The first one is just object function. The second one is function native code. But Wscript looks way more like a browser.
And this is what I'd expect for most JavaScript interpreters, which is at least something that looks like a function that actually has the name of the function in it. So you can see they have function activeX object wrapped with native code, and then function split, again, wrapped with native code. And they're very similar between the two. So just to be safe, to make sure that if they fix one of these, I can still get around them, I'm going to check both. I'm going to check to make sure that both of these two string methods are going to return the correct value. And again, we're not detected. So let's keep on going. Let's try HTTP communications, because I'm pretty sure AVG is not going to be willing
to make an HTTP request while it's scanning the virus. So in this example, all I'm going to do is go hit httpbin.org, and I'm going to make sure I get a response of 200. And I actually do get detected. And this kind of got me excited in the beginning, because if AVG is actually willing to make HTTP requests for me during a scan, I can exfiltrate a lot of data while they're scanning me. Simply by hitting URLs. I showed you the blind SQL injection type approach. I could do that in my code and have it ship the results off to some URL. Again, all just during scanning. I hope to God that's not actually what's
going on here, but it sure looks like it. The next thing I want to do is prove that that is happening or is not happening. Now I'm checking to make sure that at least HTTP bin parentheses one parentheses is going to show up in the response body, because it should. And unfortunately, I'm not detected. So I now know AVG is spoofing the HTTP request. I just have no idea how they're spoofing it. So the next test is to see if there's anything in the response at all, because maybe there's some sort of interesting piece of data that they're throwing in the response. I don't know. So we just check to make sure that the response
text is greater than zero in length. And I'm not detected. So that tells me all they're doing is faking a 200 response, and they're giving me a blank response body. But that's cool. We can totally use that. So now we're just gonna try to succeed on error. So we're gonna go ahead and hit some random URL that couldn't possibly exist, and we're only gonna execute the virus logic if that request fails. And the idea here is that AVG won't know that that URL is bad, and anything that they would do to check would be a security vulnerability. So they really have to just return a 200, and hopefully they will, and we'll evade AVG that
way. And not detected. So we have another way to evade AVG simply by detecting that they're spoofing 200 responses. And just to wrap this up and make it a little bit less obvious, we're going to have the URL randomly generated so that if the admin is looking at all the DNS logs, they don't see the same URL getting hit by a whole bunch of clients and then go investigate. So in this case, it'll just be a random URL every time we execute the script. But again, AVG is not gonna detect us because it thinks the URL is real, it's gonna spoof the 200, and we're only gonna run if we get an error when we
try to make that request. So that's that one. So we got two more to go. So this time we're going to go after the WMI. So this is Windows Management. And this is the interface that they give you to do SQL-like queries against data about the operating system. So they actually have a way to do file indexes and get a list of files and directories and things like that all through Windows Management interface. And that's a pretty hard thing to emulate. So we're going to just check to make sure that the script that I'm running right now, the virus, actually exists on disk where it's supposed to. because if it's running, it's obviously on disk and it should be in that location. And
I don't get detected. So yet again, we found another way to get around AVG, it took about 30 seconds because they don't give us anything in the WMI call. In fact, they actually return every WMI call with zero results, regardless of what it is. So I was going to, if this had worked and I had gotten detected, I was gonna try and see if Wscript was running, but that also worked because everything comes back zero. The next approach, we're going to try messing with the file system object. They didn't really get WMI right. They weren't able to spoof the fact that files existed on disk. But what about the file system object? Because I know my script does a lot of stuff with the file
system object, and that stuff seemed to work just fine. I just want to verify that maybe this technique won't work or will work there too. We're just going to check to see if the script exists through the file system object. And were detected. So file system object appears to be emulated fairly well. So the next thing I want to do is see if the file system object knows where a user file is. Because maybe they have a hard-coded rule in there saying that always say yes if you check for your own path. So this time I just created a file in my user's documents directory and I checked to see if that file existed and
I was still detected. So AVG knows about files on the disk. And this is where I was saying if they made HTTP requests, I could at least the list of files on your drive. because I can tell which ones exist and which don't. They actually do give me that information through the file system object. Presumably I could open these files too. The next thing I want to try is permissions, because file system is a really hard thing to emulate. There's a lot of stuff that goes into the Windows file system. The chances that they actually emulated all of that correctly is pretty low. The first thing we're going to try is writing a file to
the Windows directory, which will only work if you're an admin. And they may emulate the script as an admin, I don't know, but we're going to give it a shot anyway. So what I'm going to do here is only execute the script if trying to write a file to the Windows directory generates a permission denied error. And I don't get detected, because AVG thinks I have permission to touch everything. But that's a problem because if the user is elevated, they're going to be able to write to that directory and I won't run on their machine. So we need something that not even the administrator has access to touch. That's where system volume information comes in. This is a directory on your C drive. If you ever try to open
it, you can't. It's always there, but you can't get into it. Not even the administrator can get into it. All I'm going to do here is just try to write a file to the system volume information. And again, we expect a permission denied error. It will always give me a permission denied error, no matter what environment it runs under. And AVG still thinks I have access to system volume information. So we now have an evasion technique that will work on the user's desktop and through AVG, and pretty much on every Windows derivative. So now we want to try one last technique, and that's trying to write some really slow JavaScript. I know that AVG only
has like half a second to analyze me. We tried sleeping. didn't work, AVG skipped over us, but what if we have a really expensive piece of logic and they have to spend a long time in that piece of logic? The trick here is that we need to waste a lot of time but not go over our instruction limit cap here. I built this little function called slowSplit, and all it does is build up a string about a meg in size and then call the split operation on it. On a good, fast machine, this takes about 1800 milliseconds consistently. All I'm going to do is call slow split, and then I'm going to call my embedder.start. We still get detected, which is weird
because AVG could not have possibly wasted 1800 milliseconds in slow split, but I'm thinking maybe they just have a stellar implementation of splitting, and maybe it did actually get through. This time I'm going to time how long it takes, and I'm going to make sure it took as long as I think it should. We're going to do slow split again, but this time we're going to return a date delta that tells me how much time was spent in that function. I'm only going to execute the embedder if 1500 milliseconds have elapsed. My machine is really fast, so on a slow machine, that should be no problem. It should be well above 1500. But AVG still
detected me. And I'm really confused as to what's going on here, because I know AVG did not spend 1,800 milliseconds in slow split. If they had a great implementation of the splitting operation, they would still need a little bit of time. It wouldn't be 1,500 milliseconds, and therefore we wouldn't get into embedder.start. So I'm very puzzled as to how AVG got around this. But maybe they're using microseconds or something weird like that. Maybe their units are just so large that it was over 1,500 milliseconds from my perspective, but to them it was less. This time we're going to bound it. We're going to say it has to be between a second and two seconds. This
isn't really something I would want to do in the virus because some machines might take longer than two seconds. But we're just checking to see if this will work. I still get detected. Somehow ABG is tricking me into believing that it took between one and two seconds to execute that function. and they returned a verdict in like half a second. So there's something really wonky going on here. So it's time for a sanity check. I need to make sure AVG's not screwing with me, so I'm just gonna put an and false in that if statement. So even if it is over 2,000 here, it's still not gonna execute in better.start because it says false. So
we're gonna run that through, and what the fuck, we're detected again. There's some dark magic going on here, because this statement, for anybody who programs knows, will never execute. And when we put just if false in the original test, that worked just fine. So I'm going to try to see how far I can take this. We're going to say basically what it looked like is that AVG is tainting values that come from functions that take too long to execute. So I created this function again, just slow split, but it always returns false. So if AVG is smart enough, they should see that they don't even need to run that logic. It will always return false.
But in any case, we call tainted variable and we say that has to be true, and we say donkey has to literally equal ass. And only if those two conditions are false will we execute in better.start. And we're not detected. So AVG literally thinks donkey equals ass. And this was probably my most fun to mess with because it's the one that gave me the most trouble trying to figure out. It took a while to figure out they were tainting variables. So we got seven mutually exclusive ways to evade AVG. And the theory here is if you use all seven of these techniques, the likelihood that they're going to block every one of them is pretty low. And the likelihood that other AVs are vulnerable to
the same evasion tactics, at least one of them is pretty high. So it's nice to have all of those in our library. So what we learned about AVG in this little experiment was that they will only execute a finite number of instructions, and then they're going to abort. And aborting early is not a sign of malware. We learned that AVG will skip sleeps, they'll skip quits, they'll fake every HTTP connection with a 200 response. They have no concept of file access rights. They return an empty list for every WMI query. They also have a non-standard emulator. I don't know which one. I think they actually wrote it themselves because I couldn't find any other emulators
that return that same two string values. We know that they won't spend too much time in a function. If they do, they'll taint the value, and if you use a tainted value in any if condition, it will always evaluate to true. Now we're back to our AV testing. We put all of those different evasion tactics into our file right at the top, beforeembedder.start. Actually, not even at the top, right after all of the actual malware code, but beforeembedder.start. It's still interpreting all that logic, it's just not executing it. Now we run it through, and we're clean. So let's give AVG some credit. They did actually detect it. They were the only ones to detect the
non-obuscated, commented, documented virus that I tried to throw through them. But this time, no luck. So I want to go through some recommendations for how we could potentially improve AV so that these techniques don't work, or at least we disrupt them and make them a little bit harder. So the first one is don't have an instruction limit. That's actually insane. I mean, I get that you have to return a verdict within half a second, But you can keep scanning. Users want to know after they've run a virus. So there's no reason to hide that information from the user. So let's, I mean, what I would recommend for them is just emulate the virus, judge a verdict, but let it keep running for at least a day or something
or longer if it's not taking up too much CPU. We all generally have plenty of CPU to expend on security. So let it keep going. Increment the clock if you skip a sleep. If you're gonna go ahead and skip a 10 second sleep, it's single threaded, there's not gonna be any sort of other thread checking to see how long it's been sleeping in the middle. So increment the clock, make the virus think that 10 seconds have actually passed. Avoid special behavior like tainting variables or skipping sleeps and quits and things like that because all of that can be used to detect the engine. I didn't actually try to go forward with detecting wscript.quit but it would have been fairly trivial to go forward with that too. Use
the real WScript engine. The code emulator that AVG uses emulates everything. It emulates the executable IExpress. It sees that it drops a file. It sees that IExpress executes that file. And then it emulates the file. There's no reason they can't run WScript itself inside of that emulator. I don't know why they went about creating their own emulator for this, but they should probably consider going back to WScript. Everything like this should be augmented with sandboxing and cloud services. The fact that I can do all of this testing on my local desktop and I turned off all their cloud features of submitting it somewhere. So you can do all of this before you ever release your
virus to the wild. So we really need better solutions for sandboxing and for cloud analysis so that doing this type of reverse engineering against everything that could possibly detect your virus is not really possible. And we want to potentially use the JS interpreter to normalize the JS code so we can process the JS, interpret it with an interpreter like Rhino or V8, and then we can write the JavaScript back out as the interpreter sees it, which gives us a normalized view of the JavaScript, meaning that we won't have to worry about all the obfuscation techniques that the user is using, and we can run our signatures on that. And had they done that, they might
have noticed some of the activity I was doing was bad simply from signatures. That's just a guess because obviously they didn't. I ran it through without any obfuscation. I still didn't detect it. So next one would be report all unemulatable operations as failures. So if you can't make an HTTP request, which you can't, throw back an error. It's not really that weird for a user not to have an internet connection. It happens. But if you pretend like they always have an internet connection, that becomes vulnerable. Emulate realistic WMI queries. So if you're going to return a process list, don't just return an empty list. Return a list that contains some legitimate looking processes. It doesn't have to be
the ones on the box. Just return something. Test file access rights. If I'm about to go write to a directory, you don't have to write the file that I'm trying to write, but go write something to that directory and make sure the user actually has permission to do it. And if they don't, report that error. Don't let it just go under the wire. And finally, it's important to perform a delta between what the emulator saw and what the user sees when they actually execute the script. I was reading through AVG's documentation, and they seem to make the claim that they would detect this kind of activity when the user runs it, not only just when it gets emulated, but once I got past AVG's emulator, I was able to
run the CNC bot and use it no problem without any more detection. That's clearly not the case. If we took a snapshot of what the thing did during emulation and what it did afterwards, we'd be able to figure out that there were some differences and there's probably some evasion going on. Lastly,
I wanted to do a little experiment. These techniques only work on JavaScript. I tried the same techniques in VBScript, and they don't work. I'd have to go through this whole exercise all over again to find all the differences with the VBScript emulator. But that's okay, because as I said, AVG will actually execute iExpress. It will see that it drops the JavaScript file, execute that too, and then emulate it. If I can break that chain, if I can get their emulator to not see one of those steps, then I should be able to smuggle anything I want using these same evasion techniques. In this case, all I did was write a JavaScript file. I could have used any language for this because this is not malware, this is a wrapper
for malware. All this does is generate something called goodware.wsf that takes anything I want, in this case vbscript, and executes it in the script after doing 80,000 loop iterations. This is what it looks like when it outputs the file. And as I said, WSF lets you do VBScript and JavaScript. So we do our 80,000 loop iterations. We have the lovebug virus, which came out in 95. I ran it through VirusTotal. Every single vendor catches it. So all we did was convert it to ASCII. And a bunch of vendors still catch it when it's just ASCII. But then I just convert the ASCII back to strings, and then I execute it. And because this 80,000 loop iteration is there, When AVG loads this file, they choke on it and
they say, OK, I hit the end of my chain. I ran the JavaScript, nothing happened, we're good to go. And when the user runs it, they're going to plow through those 80 instructions no problem. They're going to decrypt the lovebug virus, and it's going to execute on their machine. So the verdict is not detected. So you can wrap pretty much any detectable virus using this technique to get it around AVG.
AV is necessary. It's the only software that's running on the user's desktop that has the opportunity to see what applications are actually doing. You can do sandboxing, you can do emulation, all of that, but you never get the full picture of what it will really do until you execute it on the user's machine. AV is in the only position to pull this off. It's important that they actually go back and spend a little bit more time making these emulation features and some of the other features they use for detection better because, again, they're the only people that can do it. And AVG actually introduced this feature, I think, back in 2005. So I'm pretty sure
they stopped touching it around 2005. Back then there was a lot of JavaScript and VBScript viruses. Since then we haven't really seen too many of them, though I wouldn't be surprised if they came back. So I don't think they ever touched the emulator after that. And so it's kind of lagged behind and that's why a lot of these techniques are possible. Back in 2005 it's unlikely people were looking at two strings of various functions and stuff like that because they weren't familiar with web development. They didn't know what kinds of things you could do with JavaScript as a language. And if the banner wasn't, you know, Information enough. Do not write viruses. We have a
lot of viruses out there. We don't need to contribute to the problem. So please do not use any of this information to write malicious code, and it's illegal. So here's all the sources if you want. Again, only Microsoft Tools and Antivirus, MSDN and Stack Overflow, and we got Swordfish 2001. So with that, I'll take any questions, and good to go.
Yeah, so dialogues are a pretty effective way to get around things like sandboxes. I did try to pop an alert dialogue, and it didn't do anything. It still detected me. So I'm pretty sure Wscript also skips the echo command as well.
They may be. I think probably not, because they implemented this whole environment on their own. So even if you had Python installed, I doubt it would know how to suck it in and use it. But realistically, who's going to write a virus in a language that you have to install the language on the machine with?
Yeah, but I think a user might notice all the traces of Python getting installed on their machine. Maybe not, I don't know. It's not my choice of language because there's a lot of overhead.
The big difference here is code emulation only works if it's running on your desktop, so you have to install the full AV package. I did also test against Avast, and Avast did not catch it. Avast probably has the largest market share, and it's a pretty complex, decent piece of AV software. They didn't catch it.
Yes. Yes, it is totally possible to have the rubber ducky do anything you want. There's no difference between the rubber ducky and a keyboard plugged into the desktop and doing its own thing. By the way, it's on sale too right outside there if you guys want to pick one up.
I don't recall the name of the company, but I've actually seen a company that does desktop sandboxing, which is basically you can launch this process and it creates a sandbox on your desktop, and you can run anything you want in there, and it has no access to the outside file system. It's just like a VM, but far more lightweight. They can actually wrap pretty much any process with that thing. I think it is heading in that direction, but we're not really close to it yet. All right. Thanks, guys.