start	end	text
0	4940	So this talk is called Power Pwning, Post Exploitation by Overpowering PowerShell.
5720	14680	So my name is Joe Bilek. I'm a security engineer at a large software company or actually devices
14680	22780	and services company in Redmond. You can find me on Twitter and I have a blog and GitHub.
25300	29980	All right. So if any of you don't know anything about PowerShell, it's pretty awesome. It's
29980	36380	awesome. PowerShell gives you full access to the Win32 API and all the .NET libraries.
37000	42660	And PowerShell has a remoting functionality which allows you to connect to other systems
43440	49760	and run scripts on them. And these scripts that you run over PowerShell remoting never
49760	56540	touch disk on the remote system. And in addition to that, any scripts that you run are going
56540	61700	to run inside either the PowerShell.exe process if you run it on the local computer or the
61700	69440	WSM process if you run it on a remote computer. So this is pretty nice, too, because you don't
69440	74560	have to execute any suspicious or unsigned processes which means that you can generally
74560	80400	bypass antivirus and application whitelisting assuming that PowerShell is whitelisted.
83760	89500	So that's all pretty great. But I have a lot of pen test tools that I use right now that are all
89500	95220	written in unmanaged code. And I really don't feel like porting all of those tools over to
95220	100080	PowerShell. But I really want to take advantage of PowerShell's forensic benefits because you
100080	106500	can be really sneaky if you use PowerShell. So the solution that I came up with is to write a
106500	113040	PowerShell script that will just reflectively load PEs, which is what an executable in an
113040	119960	exe file or a DLL is, in the PowerShell process and execute them. So that's what I'm going to
119960	129470	be talking about. So for those of you who don't know how loading PEs works, this is all
129470	137210	documented on MSDN. I'm not going to go super in detail, but I want to give you an idea of
137210	142970	kind of what Windows is doing when create process or load library is called. So what's
142970	149330	going to happen is, first, memory is going to be allocated inside a process for the PE. Next,
149330	153410	you're going to copy the PE into that memory. Then you're going to do what's called
153410	159150	performing base relocations. So inside of a PE file, there's going to be a whole bunch of
159150	165110	memory addresses that are hard coded. And the PE file and its header is going to supply a
165110	169310	base address. So it's going to tell Windows, this is the memory address that I'd like to be
169310	174350	loaded to. And if you don't load me to this memory address, then you need to go and change
174350	179510	all of the hard coded memory addresses in the PE file so that they point to valid memory
179510	185390	locations. So you're just patching up these addresses. Next, the PE file that you're loading
185390	190750	is probably going to have dependencies on other DLLs, so you need to load those. And then the
190750	196570	last thing that you're going to do is you're going to call the entry function. So for a DLL,
196570	202690	this is the DLL main function, which just lets the DLL know that it's been loaded. And for an
202690	208910	EXE, it's going to be a function which does some initialization and then eventually will
208910	218700	call like int main in C++ or C or the equivalent in other programming languages. Oh, and I
218700	225380	wanted to note that when I say that I'm going to be reflectively loading PE files, what I
225380	232040	mean by this is that instead of relying on Windows load library or create process to go
232040	237480	through and do those steps that I just listed, instead I'm going to do all of those steps in
237480	243740	PowerShell. And what this will allow me to do is the Windows functions require that the PE
243740	248180	file is stored on disk, so you can't call load library on something that's just in memory. It
248180	253960	has to be a file on disk. But if I rewrite all those functions in PowerShell, then I don't
253960	259480	have that same constraint. So I can just hard code the PE file in a byte array inside the
259480	264880	script. So this is really nice. Now I don't need to write executables and DLLs to disk to
264880	269440	execute them. They just need to be encoded in a PowerShell script, which can then be run on
269440	279700	remote systems. Okay. So for DLLs in particular, loading a DLL reflectively is really not much
279700	286360	different than loading a DLL using load library in the sense that when you call a DLL using
286360	291200	load library, I mean, at the end of the day, a DLL is still being loaded inside of a process
291200	296620	that already exists. So reflectively loading it, the only change here really is that I didn't
296620	300540	call the load library function. I just wrote my own. But other than that, everything is the
300540	305880	same. A DLL is still being loaded into a process which already exists. So the first thing
305880	308860	you're going to want to do is you're going to want to call a DLL function that you
308860	315620	exported. And this will contain your payload or the code you want to execute. One little
315620	322600	caveat with doing this is that if your DLL or your EXE, for that matter, outputs data using
322600	328640	standard out, so like if you use print F or C out and C++, PowerShell can't capture that
328640	333420	output. And the significance of this is that if you run a script locally, it doesn't matter
333420	336920	because the output is still just going to be sent through the console host window, so
336920	342380	you're going to see the output. But if you're doing PowerShell remoting, the only way that
342380	347800	you can see output is if PowerShell can capture that output and serialize it and send it back
347800	352860	to the computer that you did the remoting call from. So if you want to be able to see the
352860	357160	output, what you're going to need to do is make the DLL function that you're calling
357740	363720	return you a character array or like a wide character array. And PowerShell can then take
363720	368360	that pointer and marshal it into managed memory and then it can serialize that and send it
368360	372840	back. So it's just a small change that you need to make. And on my blog, I actually have a
372840	378800	post showing how it takes about maybe five minutes to change Mimikatz from outputting
380120	387400	using standard out to outputting to a string that can be, you know, retrieved by PowerShell.
390540	396800	So reflectively loading an EXE is a little more tricky because normally when an EXE is
396800	401120	loaded, it's loaded using create process and a new process is created specifically for
401120	407400	that EXE. But what we're doing is loading an EXE inside of a process that already exists
407400	415040	with PowerShell.EXE running inside of it. So as an example, normally when an EXE exits,
415040	420120	so the EXE that you reflectively loads ‑‑ that you reflectively loaded runs and quits,
420120	424540	it's going to go and call exit process because it thinks everything is done. But the problem
424540	427880	is that that's actually going to kill the PowerShell process because that's the process
427880	433160	that's actually running. And that's not ideal because you probably wanted to see the output
433160	438700	from the EXE that you just ran. And if PowerShell just dies, you can't. And so it turns out
438700	446980	there's a pretty easy solution to this. Basically what I do is I call the EXE in its own thread.
447040	452440	I call its entry function in a new thread. And then you overwrite the exit process function
452940	458580	with shell code that just calls exit thread instead. And so the EXE will start up in its
458580	461620	own thread and then when it's done running, it will just nicely kill the thread that you
461620	467620	provided for it. So it's a really clean solution. And just for reference purposes, I put assembly
467620	469840	in here, but we don't actually need to talk about this.
472260	477440	Okay. So the other problem that I had reflectively loading an EXE is that you're going to want
477440	481780	to pass it command line arguments. But the command line arguments that it's going to
481780	485480	retrieve are the command line arguments that PowerShell started with because you're running
485480	492240	inside the PowerShell process. And so it turns out that, well, the first thing I wanted to
492240	497740	do was just go inside the process environment block, the PEB, which is where the command
497740	502220	line arguments are stored, and just overwrite the string that stores the command line arguments.
502220	508380	But that doesn't actually work because the way that EXE normally retrieves its command
508380	513620	line arguments is it makes a function call to get command line or underscore, underscore
513620	520820	get command line. And those are provided by two different DLLs. And when those DLLs are
520820	525260	initially loaded, they actually cache a copy of the command line arguments. So if you just
525260	530860	overwrite the PEB, you didn't overwrite the cached copy that's in the DLLs, and so you're
530860	533820	still just going to end up getting the command line arguments that PowerShell was started
533820	539000	with, which is no good. So I had to create solutions for both of these functions.
541180	546540	So patching get command line is super easy. Get command line is a function that takes
546540	550920	no arguments and it just returns you a string pointer to the command line arguments with
550920	555180	no formatting or anything done. So all you need to do in PowerShell is just allocate
555300	562440	a string on the heap and then overwrite the function call, get command line, with shell
562440	567740	code that just returns the string, the address to the string that you allocated. So very,
567740	573120	very simple, right? The next function, underscore, underscore
573120	579480	get command line, is a little more complicated to overwrite. So I didn't want to do that.
579480	584860	And the reason is it takes a number of parameters and this function actually goes and parses
584860	589360	out your command line arguments into RdV and RdC. I didn't want to go through the trouble
589360	593760	of parsing stuff if I didn't need to. But what I found is that the DLL that this function
594430	601340	is exported from also exports two variables, A command line and W command line. And these
601340	606800	variables are just string pointers. So if you go and overwrite these variables and then
606800	611980	you call the get command line function, it will parse the variables that you just overwrote.
611980	615920	So that's an even easier solution than the previous one because you just replace those
616480	619620	string pointers with pointers to strings that you allocate.
623640	632140	Okay. So what about remote reflective DLL injection? That's possible, too. It's a little
632140	638320	more painful to do it in PowerShell compared to C or C++, which is what some of the current
638320	645240	remote reflectors are written in. But it turns out it's still very possible to do.
645440	651440	So the general process that I used for this is I allocate memory in both the remote process
651440	659620	and the current process. And then I load the DLL into the current process, so into PowerShell.exe.
660460	666480	And I'm pretty much just using PowerShell.exe to stage this DLL. So I want to get it ready
666480	673080	before I write it into the remote process. And what that entails is I go and I look and
673080	679520	see what DLLs does this DLL depend on, just like I did before, right? Except when I load
679520	684220	those DLLs, I actually load them in the remote process. And I get the function addresses
684220	690920	for everything in the remote process. And when you do your base relocations, similarly,
690920	696920	you use the memory address for the memory you allocated in the remote process when you're
696920	703780	doing your base relocation calculations. And once you've done all this, you end up with
703780	709080	the DLL, which is currently in the PowerShell process, but all of the memory addresses have
709080	714340	been patched as if it was being loaded in the remote process. So all you need to do
714820	720060	is take those bytes and write them into the remote process, and the DLL is pretty much
720060	730100	ready to go. You just give it its own thread, and it will start executing. So I just have,
730100	735180	once again, more assembly here just for reference purposes for the shell code that you actually
735180	741180	write into the remote process to call get ‑‑ or load library and get proc address.
751950	755770	All right. Wait a second. How do we go back to that code? All right. Here we go. Everybody
755770	773680	take notes. You got this? I know none of you understand why we're here. You know, it
773680	777460	slays me how many new speakers there are at DEF CON this year. I'm beginning to think
777460	789140	they're just saying that to fuck with us. Thank you, sir. And did we ‑‑ oh, we got
790400	795760	one. Yes. What's your name? Rick. We got Rick. Rick, new attendee. He's representing all
795760	816020	of you. All right. Cheers, gentlemen. We'll see you soon. Everybody got that code?
819810	825470	Probably not the most interesting slide to leave up there. All right. So let's do some
825470	845490	demos. Okay. So I have two VMs here. This one with the gray background is a domain
845490	852030	controller and it's going to be targeted. And then I have this demo client. Let's see if I
852030	863910	can resize these windows. There we go. There we go. All right. So just as an example of what
863910	869450	you can do with this script, so I have this script here called invoke Mimikatz X64. All I did
869450	876410	was just hard code the Mimikatz binary into the reflective PE injection script just so it's a
876410	884650	little easier to use. So what I'm going to do here is you can supply computer name. And the
884650	889110	computer name right now is DC1, which is my domain controller. Computer name is actually an
889110	893250	array, though, so you can supply hundreds or thousands of computers to run this script
893250	898370	against and PowerShell will do it all for you automatically, do the concurrency, blast the
898370	907290	script off. So let's go ahead and run this. Hopefully it works because this is live. All
907290	923830	right. And the output gets streamed back. So what just happened? All right. So it looks like
923830	930930	the domain admin is running with the password 123. That's not very good. But anyway, so what
930930	936110	this allows you to do is we just ran Mimikatz on a remote system without ever touching disk on
936110	940790	that remote system, without ever triggering any application white listing products, without
940790	946070	triggering antivirus. As far as the domain controller is concerned, all that happened was
946070	950750	someone made a remote PowerShell connection to the server and ran a script that we now have no
950750	959030	record of. That's pretty awesome, I think. So this next script, this is called invoke Ninja
959030	965990	copy and this is online, too. So a little while ago I saw a blog post by someone who wrote a
965990	972430	tool called SamX. Basically their observation was by default not even an administrator can
972430	979490	make, can access certain files on a Windows system like the registry hive or the NTDS active
979490	986470	directory database because LSAS has a lock on the file. So LSAS is the only process that is
986470	992890	allowed to have the file open. However, if you know how to parse NTFS, an administrator can get
993010	999090	a read handle to the C volume and then just read the NTFS structures on the C volume or D
999090	1004870	volume and scan to the exact position where the bytes that make up that file are located and
1004870	1010090	read them off. So I thought that was pretty awesome. And I kind of wanted to write that in
1010090	1015930	PowerShell but then I realized that someone had already written an open source NTFS parser in
1015930	1022410	C++ and put it on CodePlex. So I figured maybe I should just use that and reflectively load it
1022410	1027270	and save my time parsing NTFS on my own. And that's really the beauty of this script is that
1027270	1030710	you can just take these tools that other people have written and then just turn them into
1030710	1035730	PowerShell scripts that are super sneaky all of a sudden. And so that's what we're going to do
1035730	1040790	here. So I'm actually, I'll go to the domain controller really quick. All right. Great
1040790	1045830	background. We're on the domain controller. And here's the NTDS.dit file. And I'm going to try
1045830	1051650	to open it. Yeah, let's try to open it with notepad. And we're going to get this error
1051650	1056130	message saying the process cannot access the file because it is being used by another
1056130	1062410	process. So even though I'm domain admin, I can't get this file. Bummer. I really wanted it.
1064370	1070490	Okay. So let's go back to the demo client. And we're just going to go ahead and remote
1070490	1078030	PowerShell into the domain controller. And I have here the file path, the NTDS file. And I'm
1078030	1089700	going to copy it to the desktop of my attack box. And we'll go ahead and run this guy. You
1089700	1095220	can see from the output here, it's just going to copy 5 megabytes at a time of this file
1095760	1102540	directly to the desktop. And if I go ahead and open my desktop, we now have a copy of the
1102540	1116660	NTDS Active Directory database file. And once again ‑‑ so once again, no suspicious
1116660	1121300	processes were run. The only thing that ever touched disk ‑‑ well, nothing touched disk.
1121900	1126900	This was all done in memory. And we streamed the file back over the wire. So that's pretty
1126900	1131340	awesome. There's very little evidence left behind that an attacker ever did something here.
1134430	1140870	And just for reference, it took me maybe a couple hours to turn the NTFS parser into a DLL that I
1140870	1147990	could use with my script and, you know, good to go. So it's really, really fast to turn things
1147990	1152030	into PowerShell ‑‑ turn arbitrary programs into PowerShell scripts that are really hard
1152030	1167940	to catch. Okay. So the obligatory detection and prevention slide. So there's not a whole
1167940	1172920	lot to say here. PowerShell remoting requires administrator access and it requires open
1172920	1178920	ports. So if you don't want someone to do this, then don't let them have admin access on
1178920	1182860	really sensitive servers of yours and don't let them have network connectivity to really
1182860	1187440	sensitive servers of yours. Standard stuff like firewalls, limiting powerful accounts,
1187440	1190700	right, like lock down your accounts, make sure that I can't just remote PowerShell into
1190700	1196740	every server from any server. And it helps defend against this. Of course, nobody does
1196740	1204400	that, but ‑‑ or maybe some people do that. PowerShell V3 has pipeline logging, which
1204980	1209780	might help detect this, but it would be really easy for an attacker to turn it off or clear
1209780	1214380	the logs if they wanted to. And there's also going to be a lot of noise if you actually
1214380	1219100	use PowerShell. Constrained run spaces can help limit the power of PowerShell. So you
1219100	1223540	can say even though someone's allowed to remote PowerShell into the server, there's only
1223540	1228080	specific commands they're allowed to run. So I don't have full access to the Win32 API.
1228080	1234080	I only have access to these 50 commandlets that are deemed to be safe. And another one
1234080	1240520	is machine‑wide profile to log actions to a transcript. Once again, super easy for
1240520	1244740	an attacker to turn this off. But if an attacker doesn't know that you're doing this, then
1244740	1253420	you might be able to record what they're doing. So closing thoughts, this is not a vulnerability.
1253860	1258260	All of this is by design. PowerShell is a very powerful language. It has full access
1258260	1265120	to the Win32 API. It has full access to the .NET libraries. So yes, it is able to do this.
1265120	1270460	And pretty much any programming language can do this if it has access to the Win32 API.
1270920	1275000	And if a programming language has remoting functionality, then you can, you know, use
1275000	1277800	the remoting functionality of other programming languages as well.
1278640	1282180	I think PowerShell is a great way to manage Windows systems. So I hope this talk doesn't
1282180	1286760	scare you away from PowerShell altogether, but I hope that it does encourage you to treat
1286760	1292760	PowerShell sensitively and make sure that you configure it correctly, don't expose,
1292760	1298400	you know, PowerShell to all the servers in your environment. And hopefully it also gives
1298400	1302440	you awesome ideas on how to attack Windows networks, because that's kind of why I'm here.
1303700	1309220	So I've got some references to helpful documentation, Microsoft documents, a lot about how PE loading
1309220	1314660	and DLL loading works, links to some other reflective loaders, like Metasploit has a
1314660	1319880	reflective loader, for example. That's how it does migration and whatnot. And then some
1319880	1323940	really good PowerShell-related blogs, Exploit Monday especially is super good. Matt Graber
1323940	1331220	has really great documentation on how to do awesome stuff in PowerShell. And just links,
1331220	1336600	once again, to my blog and my Twitter and GitHub. And that's all I've got for you.
