WEBVTT

00:00.000 --> 00:04.940
So this talk is called Power Pwning, Post Exploitation by Overpowering PowerShell.

00:05.720 --> 00:08.260
So my name is Joe Bilek.

00:08.320 --> 00:18.080
I'm a security engineer at a large software company or actually devices and services company in Redmond.

00:18.640 --> 00:22.780
You can find me on Twitter and I have a blog and GitHub.

00:25.300 --> 00:26.040
All right.

00:26.040 --> 00:29.980
So if any of you don't know anything about PowerShell, it's pretty awesome.

00:29.980 --> 00:30.580
It's awesome .

00:30.820 --> 00:36.380
PowerShell gives you full access to the Win32 API and all the .NET libraries.

00:37.000 --> 00:45.840
And PowerShell has a remoting functionality which allows you to connect to other systems and run scripts on them.

00:46.040 --> 00:52.160
And these scripts that you run over PowerShell remoting never touch disk on the remote system.

00:53.260 --> 01:05.900
And in addition to that, any scripts that you run 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.

01:06.260 --> 01:20.400
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.

01:23.760 --> 01:25.900
So that's all pretty great.

01:26.160 --> 01:31.320
But I have a lot of pen test tools that I use right now that are all written in unmanaged code.

01:31.760 --> 01:36.180
And I really don't feel like porting all of those tools over to PowerShell.

01:36.240 --> 01:42.100
But I really want to take advantage of PowerShell's forensic benefits because you can be really sneaky if you use PowerShell.

01:42.660 --> 01:58.920
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.

01:59.300 --> 02:01.200
So that's what I'm going to be talking about.

02:04.510 --> 02:11.510
So for those of you who don't know how loading PEs works, this is all documented on MSDN.

02:12.750 --> 02:21.130
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.

02:21.950 --> 02:27.550
So what's going to happen is, first, memory is going to be allocated inside a process for the PE.

02:28.650 --> 02:31.370
Next, you're going to copy the PE into that memory.

02:32.190 --> 02:35.350
Then you're going to do what's called performing base relocations.

02:35.870 --> 02:41.270
So inside of a PE file, there's going to be a whole bunch of memory addresses that are hard coded.

02:41.690 --> 02:46.210
And the PE file and its header is going to supply a base address.

02:46.210 --> 02:50.210
So it's going to tell Windows, this is the memory address that I'd like to be loaded to.

02:50.510 --> 03:00.350
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.

03:00.570 --> 03:02.330
So you're just patching up these addresses.

03:03.390 --> 03:09.350
Next, the PE file that you're loading is probably going to have dependencies on other DLLs, so you need to load those.

03:09.970 --> 03:14.590
And then the last thing that you're going to do is you're going to call the entry function.

03:14.890 --> 03:21.510
So for a DLL, this is the DLL main function, which just lets the DLL know that it's been loaded.

03:22.070 --> 03:34.330
And for an EXE, it's going to be a function which does some initialization and then eventually will call like int main in C++ or C or the equivalent in other programming languages.

03:37.520 --> 03:58.520
Oh, 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 I'm going to do all of those steps in PowerShell.

03:59.200 --> 04:08.080
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.

04:08.080 --> 04:09.680
It has to be a file on disk.

04:10.080 --> 04:15.360
But if I rewrite all those functions in PowerShell, then I don't have that same constraint.

04:15.580 --> 04:20.140
So I can just hard code the PE file in a byte array inside the script.

04:20.500 --> 04:21.540
So this is really nice.

04:21.540 --> 04:25.660
Now I don't need to write executables and DLLs to disk to execute them.

04:25.660 --> 04:30.620
They just need to be encoded in a PowerShell script, which can then be run on remote systems.

04:32.100 --> 04:32.660
Okay.

04:32.660 --> 04:52.620
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 that already exists.

04:52.620 --> 04:58.240
So reflectively loading it, the only change here really is that I didn't call the load library function.

04:58.240 --> 04:59.100
I just wrote my own.

04:59.100 --> 05:00.820
But other than that, everything is the same.

05:00.820 --> 05:04.480
A DLL is still being loaded into a process which already exists.

05:05.100 --> 05:09.660
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.

05:10.440 --> 05:13.960
And this will contain your payload or the code you want to execute.

05:14.520 --> 05:29.380
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 print F or C out and C++, PowerShell can't capture that output.

05:29.940 --> 05:38.180
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.

05:38.840 --> 05:50.300
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.

05:50.800 --> 06:01.200
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.

06:01.860 --> 06:09.000
And PowerShell can then take that pointer and marshal it into managed memory and then it can serialize that and send it back.

06:09.100 --> 06:11.300
So it's just a small change that you need to make.

06:11.300 --> 06:27.400
And on my blog, I actually have a post showing how it takes about maybe five minutes to change Mimikatz from outputting using standard out to outputting to a string that can be, you know, retrieved by PowerShell.

06:30.540 --> 06:42.240
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.

06:42.380 --> 06:49.900
But what we're doing is loading an EXE inside of a process that already exists with PowerShell.EXE running inside of it.

06:50.120 --> 07:03.680
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.

07:03.980 --> 07:09.220
But the problem is that that's actually going to kill the PowerShell process because that's the process that's actually running.

07:09.600 --> 07:15.280
And that's not ideal because you probably wanted to see the output from the EXE that you just ran.

07:15.320 --> 07:17.140
And if PowerShell just dies, you can't.

07:17.580 --> 07:20.620
And so it turns out there's a pretty easy solution to this.

07:22.600 --> 07:26.980
Basically what I do is I call the EXE in its own thread.

07:27.040 --> 07:29.560
I call its entry function in a new thread.

07:29.720 --> 07:36.060
And then you overwrite the exit process function with shell code that just calls exit thread instead.

07:36.780 --> 07:42.740
And so the EXE will start up in its own thread and then when it's done running, it will just nicely kill the thread that you provided for it.

07:42.780 --> 07:44.280
So it's a really clean solution.

07:44.940 --> 07:49.840
And just for reference purposes, I put assembly in here, but we don't actually need to talk about this.

07:52.260 --> 07:52.780
Okay.

07:52.780 --> 07:59.100
So the other problem that I had reflectively loading an EXE is that you're going to want to pass it command line arguments.

07:59.660 --> 08:07.220
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.

08:07.980 --> 08:22.220
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 that stores the command line arguments.

08:22.220 --> 08:35.100
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.

08:35.740 --> 08:38.960
And those are provided by two different DLLs.

08:39.200 --> 08:44.540
And when those DLLs are initially loaded, they actually cache a copy of the command line arguments.

08:44.640 --> 08:55.320
So if you just overwrite the PEB, you didn't overwrite the cached copy that's in the DLLs, and so you're still just going to end up getting the command line arguments that PowerShell was started with, which is no good.

08:56.140 --> 08:59.000
So I had to create solutions for both of these functions.

09:01.180 --> 09:03.740
So patching get command line is super easy.

09:04.280 --> 09:12.580
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.

09:12.680 --> 09:26.340
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.

09:26.640 --> 09:29.000
So very, very simple, right?

09:30.340 --> 09:38.340
The next function, underscore, underscore get command line, is a little more complicated to overwrite.

09:38.460 --> 09:39.480
So I didn't want to do that.

09:39.480 --> 09:48.220
And the reason is it takes a number of parameters and this function actually goes and parses out your command line arguments into RdV and RdC.

09:48.320 --> 09:51.760
I didn't want to go through the trouble of parsing stuff if I didn't need to.

09:51.840 --> 10:00.460
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.

10:00.920 --> 10:03.300
And these variables are just string pointers.

10:03.480 --> 10:11.980
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.

10:11.980 --> 10:19.620
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.

10:23.640 --> 10:24.540
Okay.

10:25.160 --> 10:30.280
So what about remote reflective DLL injection?

10:30.380 --> 10:31.620
That's possible, too.

10:31.620 --> 10:41.780
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.

10:42.140 --> 10:45.240
But it turns out it's still very possible to do.

10:45.440 --> 10:53.440
So the general process that I used for this is I allocate memory in both the remote process and the current process.

10:54.040 --> 10:59.620
And then I load the DLL into the current process, so into PowerShell.exe.

11:00.460 --> 11:05.240
And I'm pretty much just using PowerShell.exe to stage this DLL.

11:05.240 --> 11:09.080
So I want to get it ready before I write it into the remote process.

11:09.680 --> 11:18.020
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?

11:18.180 --> 11:22.820
Except when I load those DLLs, I actually load them in the remote process.

11:23.020 --> 11:26.040
And I get the function addresses for everything in the remote process.

11:27.080 --> 11:39.340
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.

11:40.600 --> 11:52.340
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.

11:52.980 --> 12:00.920
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.

12:00.920 --> 12:03.960
You just give it its own thread, and it will start executing.

12:08.480 --> 12:21.180
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.

12:31.950 --> 12:32.510
All right.

12:32.510 --> 12:32.990
Wait a second.

12:32.990 --> 12:34.650
How do we go back to that code?

12:34.650 --> 12:34.990
All right.

12:34.990 --> 12:35.530
Here we go.

12:35.530 --> 12:36.770
Everybody take notes.

12:37.770 --> 12:38.830
You got this?

12:46.700 --> 12:49.400
I know none of you understand why we're here.

12:52.460 --> 12:56.800
You know, it slays me how many new speakers there are at DEF CON this year.

12:56.800 --> 12:59.480
I'm beginning to think they're just saying that to fuck with us.

13:04.960 --> 13:06.020
Thank you, sir.

13:07.980 --> 13:11.120
And did we ‑‑ oh, we got one.

13:11.540 --> 13:11.780
Yes.

13:11.780 --> 13:12.440
What's your name?

13:12.440 --> 13:12.900
Rick.

13:12.940 --> 13:13.880
We got Rick.

13:13.880 --> 13:14.760
Rick, new attendee.

13:14.760 --> 13:16.420
He's representing all of you.

13:16.620 --> 13:17.240
All right.

13:17.300 --> 13:18.380
Cheers, gentlemen.

13:30.640 --> 13:31.920
We'll see you soon.

13:34.660 --> 13:36.020
Everybody got that code?

13:39.810 --> 13:42.230
Probably not the most interesting slide to leave up there.

13:43.370 --> 13:44.050
All right.

13:44.050 --> 13:46.070
So let's do some demos.

14:00.870 --> 14:01.470
Okay.

14:01.470 --> 14:03.410
So I have two VMs here.

14:04.030 --> 14:07.350
This one with the gray background is a domain controller and it's going to be targeted.

14:08.310 --> 14:09.910
And then I have this demo client.

14:11.330 --> 14:13.470
Let's see if I can resize these windows.

14:14.370 --> 14:15.470
There we go.

14:16.130 --> 14:19.310
There we go.

14:19.630 --> 14:20.530
All right.

14:21.550 --> 14:28.630
So just as an example of what you can do with this script, so I have this script here called invoke Mimikatz X64.

14:28.950 --> 14:37.570
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.

14:38.190 --> 14:42.310
So what I'm going to do here is you can supply computer name.

14:43.670 --> 14:47.310
And the computer name right now is DC1, which is my domain controller.

14:47.550 --> 14:59.330
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.

14:59.930 --> 15:01.510
So let's go ahead and run this.

15:01.790 --> 15:04.110
Hopefully it works because this is live.

15:07.030 --> 15:07.910
All right.

15:07.930 --> 15:09.750
And the output gets streamed back.

15:09.750 --> 15:11.990
So what just happened?

15:22.450 --> 15:23.170
All right.

15:23.170 --> 15:27.090
So it looks like the domain admin is running with the password 123.

15:27.130 --> 15:28.590
That's not very good.

15:29.730 --> 15:42.370
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 white listing products, without triggering antivirus.

15:42.970 --> 15:51.670
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.

15:52.050 --> 15:53.610
That's pretty awesome, I think.

15:55.030 --> 16:01.070
So this next script, this is called invoke Ninja copy and this is online, too.

16:01.930 --> 16:07.170
So a little while ago I saw a blog post by someone who wrote a tool called SamX.

16:07.170 --> 16:24.270
Basically their observation was by default not even an administrator can make , 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.

16:24.270 --> 16:27.890
So LSAS is the only process that is allowed to have the file open.

16:28.310 --> 16:45.850
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.

16:46.410 --> 16:48.050
So I thought that was pretty awesome.

16:48.950 --> 16:58.170
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.

16:58.710 --> 17:05.210
So I figured maybe I should just use that and reflectively load it and save my time parsing NTFS on my own.

17:05.630 --> 17:13.310
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.

17:14.250 --> 17:16.270
And so that's what we're going to do here.

17:16.270 --> 17:19.070
So I'm actually, I'll go to the domain controller really quick.

17:19.810 --> 17:20.530
All right.

17:20.530 --> 17:21.290
Great background.

17:21.290 --> 17:22.570
We're on the domain controller.

17:22.750 --> 17:24.930
And here's the NTDS.dit file.

17:25.150 --> 17:26.830
And I'm going to try to open it.

17:28.430 --> 17:30.510
Yeah, let's try to open it with notepad.

17:30.850 --> 17:36.930
And we're going to get this error message saying the process cannot access the file because it is being used by another process.

17:37.050 --> 17:40.210
So even though I'm domain admin, I can't get this file.

17:40.230 --> 17:40.950
Bummer.

17:41.150 --> 17:42.410
I really wanted it.

17:44.370 --> 17:45.010
Okay.

17:45.010 --> 17:46.990
So let's go back to the demo client.

17:47.630 --> 17:53.930
And we're just going to go ahead and remote PowerShell into the domain controller.

17:54.270 --> 17:57.630
And I have here the file path, the NTDS file.

17:57.730 --> 18:00.710
And I'm going to copy it to the desktop of my attack box.

18:03.210 --> 18:06.130
And we'll go ahead and run this guy.

18:09.220 --> 18:17.240
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.

18:18.280 --> 18:24.760
And if I go ahead and open my desktop, we now have a copy of the NTDS Active Directory database file.

18:24.760 --> 18:29.760
And once again ‑‑ so

18:35.260 --> 18:37.940
once again, no suspicious processes were run.

18:37.940 --> 18:41.300
The only thing that ever touched disk ‑‑ well, nothing touched disk.

18:41.900 --> 18:43.640
This was all done in memory.

18:43.780 --> 18:46.200
And we streamed the file back over the wire.

18:46.440 --> 18:47.500
So that's pretty awesome.

18:47.500 --> 18:51.340
There's very little evidence left behind that an attacker ever did something here.

18:54.430 --> 19:05.150
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.

19:05.150 --> 19:13.030
So it's really, really fast to turn things into PowerShell ‑‑ turn arbitrary programs into PowerShell scripts that are really hard to catch.

19:18.420 --> 19:19.000
Okay.

19:19.000 --> 19:22.860
So the obligatory detection and prevention slide.

19:23.780 --> 19:29.220
So there's not a whole lot to say here.

19:29.560 --> 19:33.860
PowerShell remoting requires administrator access and it requires open ports.

19:34.040 --> 19:44.440
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.

19:44.520 --> 19:52.800
Standard stuff like firewalls, limiting powerful accounts, right, like lock down your accounts, make sure that I can't just remote PowerShell into every server from any server.

19:53.200 --> 19:55.200
And it helps defend against this.

19:55.680 --> 19:59.500
Of course, nobody does that, but ‑‑ or maybe some people do that.

20:00.860 --> 20:11.220
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.

20:12.360 --> 20:15.340
And there's also going to be a lot of noise if you actually use PowerShell.

20:16.380 --> 20:18.960
Constrained run spaces can help limit the power of PowerShell.

20:18.960 --> 20:25.680
So you can say even though someone's allowed to remote PowerShell into the server, there's only specific commands they're allowed to run.

20:25.800 --> 20:28.080
So I don't have full access to the Win32 API.

20:28.080 --> 20:31.540
I only have access to these 50 commandlets that are deemed to be safe.

20:32.780 --> 20:38.540
And another one is machine‑wide profile to log actions to a transcript.

20:38.920 --> 20:42.200
Once again, super easy for an attacker to turn this off.

20:42.200 --> 20:47.740
But if an attacker doesn't know that you're doing this, then you might be able to record what they're doing.

20:50.740 --> 20:53.420
So closing thoughts, this is not a vulnerability.

20:53.860 --> 20:55.460
All of this is by design.

20:55.760 --> 20:57.560
PowerShell is a very powerful language.

20:57.560 --> 20:59.880
It has full access to the Win32 API.

20:59.960 --> 21:02.220
It has full access to the .NET libraries.

21:02.360 --> 21:05.120
So yes, it is able to do this.

21:05.120 --> 21:10.460
And pretty much any programming language can do this if it has access to the Win32 API.

21:10.920 --> 21:17.800
And if a programming language has remoting functionality, then you can, you know, use the remoting functionality of other programming languages as well.

21:18.640 --> 21:21.320
I think PowerShell is a great way to manage Windows systems.

21:21.320 --> 21:36.080
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 configure it correctly, don't expose, you know, PowerShell to all the servers in your environment.

21:37.140 --> 21:42.440
And hopefully it also gives you awesome ideas on how to attack Windows networks, because that's kind of why I'm here.

21:43.700 --> 21:55.920
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.

21:56.120 --> 21:59.040
That's how it does migration and whatnot.

21:59.200 --> 22:03.540
And then some really good PowerShell-related blogs, Exploit Monday especially is super good.

22:03.540 --> 22:08.140
Matt Graber has really great documentation on how to do awesome stuff in PowerShell.

22:09.720 --> 22:14.440
And just links, once again, to my blog and my Twitter and GitHub.

22:15.040 --> 22:16.600
And that's all I've got for you.

