So this talk is called power pwning, post exploitation by overpowering PowerShell. My name is Joe Bialik. I'm a security engineer at a large software company or actually devices and services company in Redmond. You can find me on Twitter and I have a blog on GitHub. All right. So if any of you don't know anything about PowerShell, it's pretty awesome. PowerShell gives you full access to the Win32 API and all the .NET libraries. And PowerShell has a remoting functionality which allows you to connect to other systems and run scripts on them. And the scripts that you run over PowerShell remoting never touch disk on the remote system. And in addition to that, any scripts that you run over PowerShell remoting never touch disk on the remote system are going to run inside either the PowerShell.exe process if you run it on the local computer or the WSM process if you run it on a remote computer. So this is pretty nice, too, because you don't have to execute any suspicious or unsigned processes, which means that you can generally bypass antivirus and application whitelisting assuming that PowerShell is whitelisted. So that's all pretty great. But I have a lot of pen test tools that I use right now that are all written in unmanaged code. And I really don't feel like porting all of those tools over to PowerShell. But I really want to take advantage of PowerShell's forensic benefits, because you can be really sneaky if you use PowerShell. So the solution that I came up with is to write a PowerShell script that will just reflectively load PEs, which is what an executable in an exe file or a DLL is. In the PowerShell process and execute them. So that's what I'm going to be talking about. So for those of you who don't know how loading PEs works, this is all documented on MSDN. I'm not going to go super in detail, but I want to give you an idea of kind of what Windows is doing when create process or load library is called. So what's going to happen is, first, memory is going to be allocated. Next, you're going to copy the PE into that memory. Then you're going to do what's called performing base relocations. So inside of a PE file, there's going to be a whole bunch of memory addresses that are hard coded. And the PE file in its header is going to supply a base address. So it's going to tell Windows, this is the memory address that I'd like to be loaded to. And if you don't load me to this memory address, then you need to go and change all of the hard coded memory. Addresses in the PE file so that they point to valid memory locations. So you're just patching up these addresses. Next, the PE file that you're loading is probably going to have dependencies on other DLLs. So you need to load those. And then the last thing that you're going to do is you're going to call the entry function. So for a DLL, this is the DLL main function, which just lets the DLL know that it's been loaded. And for an EXE, it's going to be a function. It will call like int main or the equivalent just like programming languages. And I wanted to note that when I say that I'm going to be reflectively loading PE files, what I mean by this, is that, instead of relying on Windows load library or create process to go through and do those steps that I just listed, instead of relying onaced Instead I'm going to do all of those steps in PowerShell. And what this will allow me to do is the Windows functions require that the PE file is stored on disk. So you can't call load library on something that's just in memory. It has to be a file on disk. But if I rewrite all those functions in PowerShell, then I don't have that same constraint. So I can just hard code the PE file in a byte array inside the script. So this is really nice. Now I don't need to write executables and DLLs to disk to execute them. They just need to be encoded in a PowerShell script, which can then be run on remote systems. Okay. So for DLLs in particular, loading a DLL reflectively is really not much different than loading a DLL using load library. In the sense that when you call a DLL using load library, I mean, at the end of the day, a DLL is still being loaded inside of a process. It already exists. So reflectively loading it, the only change here really is that I didn't call the load library function. I just wrote my own. But other than that, everything is the same. A DLL is still being loaded into a process which already exists. So the first thing you're going to want to do is you're going to want to call a DLL function that you exported. And this will contain your payload or, you know, the code you want to execute. One little caveat with doing this is that if your DLL or your EXE, for that matter, outputs data using standard out, so like if you use printf or cout and C++, PowerShell can't capture that output. And the significance of this is that if you run a script locally, it doesn't matter because the output is still just going to be sent through the console host window, so you're going to see the output. But if you're doing PowerShell remoting, the only way that you can see output is if PowerShell can capture that output and serialize it and send it back to the computer that you did the remoting call from. So if you want to be able to see the output, what you're going to need to do is make the DLL function that you're calling return you a character array or like a wide character array and PowerShell can then take that pointer and marshal it into managed memory and then it can serialize that and send it back. So it's just a small change that you need to make. And on my blog I actually have a post showing how it takes about maybe five minutes to change Mimikatz from outputting user ‑‑ outputting user to output. Using standard out to outputting to a string that can be, you know, retrieved by PowerShell. So reflectively loading an EXE is a little more tricky. Because normally when an EXE is loaded, it's loaded using create process and a new process is created specifically for that EXE. But what we're doing is loading an EXE inside of a process that already exists with PowerShell.exe running inside of it. So as an example, normally when an EXE exits, so the EXE that you reflectively loads ‑‑ that you reflectively loaded runs and quits, it's going to go and call exit process because it thinks everything is done. But the problem is that that's actually going to kill the PowerShell process because that's the process that's actually running. And that's not ideal because you probably wanted to see the output from the EXE that you just ran. And if PowerShell just dies, you can't. And so it turns out there's a pretty easy solution to this. Basically what I do is I call the EXE in its own thread, I call its entry function in a new thread, and then you overwrite the exit process function with shell code that just calls exit thread instead. And so the EXE will start up in its own thread and when it's done running, it will just nicely kill the thread that you provided for it. So it's a really clean solution. And just for reference purposes, I put assembly in here, but we don't actually need to talk about this. Okay. So the other problem that I had reflectively loading in EXE is that you're going to want to pass it command line arguments. But the command line arguments that it's going to retrieve are the command line arguments that PowerShell started with because you're running inside the PowerShell process. And so it turns out that ‑‑ well, the first thing I wanted to do was just go inside the process environment block, the PEB, which is where the command line arguments are stored, and just overwrite the string. But that doesn't actually work because the way that EXE normally retrieves its command line arguments is it makes a function call to get command line or underscore, underscore get command line. And those are provided by two different DLLs. And when those DLLs are initially loaded, they actually cache a copy of the command line arguments. So if you just overwrite the PEB, you didn't overwrite the cached copy that's in the DLLs. And so that's a problem. So you're still just going to end up getting the command line arguments that PowerShell was started with, which is no good. So I had to create solutions for both of these functions. So patching get command line is super easy. Get command line is a function that takes no arguments and it just returns you a string pointer to the command line arguments with no formatting or anything done. So all you need to do in PowerShell is just allocate a string on the heap and then overwrite the function call. Get command line with shell code that just returns the string, the address to the string that you allocated. So very, very simple, right? The next function, underscore, underscore get command line, is a little more complicated to overwrite. So I didn't want to do that. And the reason is it takes a number of parameters and get ‑‑ this function actually goes and parses out your command line arguments into argv and argc. I didn't want to go through the trouble of parsing. But what I found is that the DLL that this function is exported from also exports two variables, A command line and W command line. And these variables are just string pointers. And so if you go and overwrite these variables and then you call the get command line function, it will parse the variables that you just overwrote. So that's an even easier solution than the previous one because you just replace those string pointers with pointers to strings that you allocate. Okay. So what about remote reflective DLL injection? That's possible, too. It's a little more painful to do it in PowerShell compared to C or C++, which is what some of the current remote reflectors are written in. But it turns out it's still very possible to do. So the general process that I used for this is I allocate memory. In both the remote process and the current process. And then I load the DLL into the current process. So into PowerShell.exe. And I'm pretty much just using PowerShell.exe to stage this DLL. So I want to get it ready before I write it into the remote process. And what that entails is I go and I look and see what DLLs does this DLL depend on. Just like I did before. Right? Except. When I load those DLLs, I actually load them in the remote process. And I get the function addresses for everything in the remote process. And when you do your base relocations, similarly, you use the memory address for the memory you allocated in the remote process when you're doing your base relocation calculations. And once you've done all this, you end up with the DLL, which is currently in the PowerShell process. But all of the memory addresses have been patched as if it was being loaded in the remote process. So all you need to do is take those bytes and write them into the remote process. And the DLL is pretty much ready to go. You just give it its own thread. And it will start executing. So I just have, once again, more assembly here just for reference purposes for the shell code that you actually write into the remote process. To call get or load library and get proc address. All right, wait a second, how do we go back to that code? All right. Here we go. Everybody take notes. You got this? All right. All right. All right. All right. All right. All right. All right. All right. I know none of you understand why we are here. You know, it sleaze me how many new speakers are at DEF CON this year. I'm beginning to think they're just saying that to fuck with us. Thank you, sir. And did we? Oh, we got our new guy. All right. You got one? Everyone's got one? Yes. What's your name? Rick. All right. Cheers, gentlemen. We'll see you soon. Everybody got that code? Probably not the most interesting slide to leave up there. All right. So let's do some demos. Okay. So I have two VMs here. This one with the gray background is a domain controller and it's going to be targeted. And then I have this demo client. Let's see if I can resize these windows. There we go. There we go. All right. So just as an example of what you can do with this script, so I have this script here called invoke Mimikatz x64. All I did was just hard code the Mimikatz binary into the reflective PE injection script just so it's a little easier to use. So what I'm going to do is I'm going to go ahead and run this. So what I'm going to do here is you can supply computer name. And the computer name right now is DC1, which is my domain controller. Computer name is actually an array, though. So you can supply hundreds or thousands of computers to run this script against. And PowerShell will do it all for you automatically, do the concurrency, blast the script off. So let's go ahead and run this. Hopefully it works because this is live. All right. And the output gets streamed back. So what just happened? All right. So it looks like the domain admin is running with the password 123. That's not very good. But anyway, so what this allows you to do is we just ran Mimikatz on a remote system without ever touching disk on that remote system, without ever triggering any application whitelisting products, without triggering antivirus. As far as the domain controller is concerned, all that happened was someone made a remote PowerShell connection to the server and ran a script that we now have no record of. That's pretty awesome, I think. So this next script, this is called Invoke Ninja Copy, and this is online, too. So a little while ago, I saw a blog post by someone who wrote a tool called SamX. Basically their observation was, by default, not even an administrator can access certain files on a Windows system, like the registry hive or the NTDS active directory database, because LSAS has a lock on the file. So LSAS is the only process that is allowed to have the file open. However, if you know how to parse NTFS, an administrator can get a read handle to the C volume and then just read the NTFS structures on the C volume or D volume and scan to the exact position where the bytes that make up that file are located and read them off. So I thought that was pretty awesome. And I kind of wanted to write that in PowerShell. But then I realized that someone had already written an open source NTFS parser in C++ and put it on CodePlex. So I figured maybe I should just use that and reflectively load it and save my time parsing NTFS files. And that's really the beauty of this script is that you can just take these tools that other people have written and then just turn them into PowerShell scripts that are super sneaky all of a sudden. And so that's what we're going to do here. So I'm actually, I'll go to the domain controller really quick. All right. Great background. We're on the domain controller. And here's the NTDS.dit file. And I'm going to try to open it. Yeah, let's try to open it with Notepad. And we're going to get this error message saying the process cannot access the file. Because it is being used by another process. So even though I'm domain admin, I can't get this file. Bummer. I really wanted it. Okay. So let's go back to the demo client. And we're just going to go ahead and remote PowerShell into the domain controller. And I have here the file path to the NTDS file. And I'm going to copy it to the desktop of my attack box. And we'll go ahead. And run this guy. You can see from the output here, it's just going to copy 5 megabytes at a time of this file. Directly to the desktop. And if I go ahead and open my desktop. We now have a copy of the NTDS Active Directory database file. And once again. So once again. No suspicious processes were run. The only thing that ever touched disk. Well, nothing touched disk. This was all done in memory. And we streamed the file back over the wire. So that's pretty awesome. There's very little evidence left behind that an attacker ever did something here. And just for reference. It took me maybe a couple hours to turn the NTFS parser into a DLL that I could use with my script. And you know. Good to go. So it's really, really fast to turn arbitrary programs into PowerShell scripts. That are really hard to catch. Okay, so. The obligatory detection and prevention slide. So. There's not a whole lot to say here. PowerShell remoting requires administrator access. And it requires open ports. So if you don't want someone to do this. Then don't let them have admin access on really sensitive servers of yours. And don't let them have network connectivity to really sensitive servers of yours. Standard stuff like firewalls. Limiting powerful accounts. Lock down your accounts. Make sure that I can't just remote PowerShell into every server from any server. And it helps defend against this. Of course nobody does that. Or maybe some people do that. PowerShell also has. PowerShell V3 has pipeline logging. Which might help detect this. But it would be really easy for an attacker to turn it off. Or clear the logs if they wanted to. And there's also going to be a lot of noise if you actually use PowerShell. Constrained run spaces can help limit the power of PowerShell. So you can say. Even though someone is allowed to remote PowerShell into the server. There's only specific commands that are allowed to run. So I don't have full access to the Win32 API. I only have access to these 50 commandlets that are deemed to be safe. So you can say. And another one is machine wide profile to log actions to a transcript. Once again. Super easy for an attacker to turn this off. But if an attacker doesn't know that you're doing this. Then you might be able to record what they're doing. So closing thoughts. This is not a vulnerability. All of this is by design. PowerShell is a very powerful language. It has full access to the Win32 API. It has full access to the .NET libraries. So yes. It is able to do this. And pretty much any programming language can do this. If it has access to the Win32 API. And if a programming language has remoting functionality. Then you can use the remoting functionality of other programming languages as well. I think PowerShell is a great way to manage Windows systems. So I hope this talk doesn't scare you away from PowerShell altogether. But I hope that it does encourage you to treat PowerShell sensitively. And make sure that you use it in the right way. Make sure that you configure it correctly. Don't expose PowerShell to all the servers in your environment. And hopefully it also gives you awesome ideas on how to attack Windows networks. Because that's kind of why I'm here. So. I've got some references to helpful documentation. Microsoft documents a lot about how PE loading and DLL loading works. Links to some other reflective loaders. Like Metasploit has a reflective loader for example. That's how it does migration and what not. And then some really good PowerShell related blogs. Exploit Monday especially is super good. Matt Graber has really great documentation on how to do awesome stuff in PowerShell. And just links once again to my blog, my Twitter, and GitHub. And that's all I've got for you.