start	end	text
0	4160	Hi there, my name is Bill and thanks for coming out to my talk.
5120	11340	So, I'm 19 years old. I'm a rising sophomore at the Rochester Institute of Technology.
11340	17060	I love Windows internals. I'm mostly self-taught with some guidance and I have a strong game
17060	22640	hacking background, which is why I'm so interested in the Windows kernel. So, what is this talk
22640	29980	about? Well, in this talk we'll be going over loading a rootkit, communicating with a rootkit,
30660	35110	abusing legitimate network communications in order to communicate with our malware,
35820	42020	an example rootkit I made and the design choices behind it, executing commands from kernel,
42020	46000	and tricks to cover up the file system trace of your rootkit.
46900	52120	So, when you hear me say rootkit, I'm going to be referring to kernel level rootkits for Windows.
52560	56560	So, why would you want to use a rootkit? Well, there's a lot of reasons.
56560	61360	Kernel drivers have significant access to the machine. Unlike in user mode,
61360	65280	you pretty much have the same, you have the access to anything and everything.
66220	70580	Kernel drivers have the same privilege level as a typical kernel antivirus.
70600	74720	Generally speaking, you share the same access to the same resources.
75300	79540	There are less mitigations and security solutions targeting kernel malware.
79540	83820	If you can load kernel code, there's a lot you can do to cover up your tracks.
84700	89420	Antivirus often have less visibility into the operations performed by kernel drivers.
89860	92820	This is because a lot of antivirus rely on user mode hooks
92820	97380	in order to gain visibility into potentially suspicious operations.
97380	102800	In kernel, however, you can't directly hook the NTOS kernel functions due to patch guard,
102800	106420	reducing the visibility antivirus has significantly.
106980	112620	And finally, kernel drivers are treated with a significant amount of trust by antivirus.
112620	114880	Let's take a look at an example of that.
115820	120160	So whether you're running a consumer antivirus or corporate EDR,
120160	125480	chances are whatever solution you run treats kernel drivers with a significant amount of trust.
125500	128820	Here I have two examples from Malwarebytes and Carbon Black,
128820	132680	specifically their pre-processed thread callback functions.
132740	137780	These functions are called whenever a handle is created to a process or a thread.
137780	142280	In the case of Malwarebytes, they check to see if the process ID is less than 8,
142280	147740	and if the operation is for a kernel handle, then it will not continue processing that handle
147740	153660	creation. In the case of Carbon Black, if your handle is for a kernel handle,
153660	159180	or if the caller is not from user mode, it will not process that handle being created.
160060	163040	Let's talk a little bit about loading a rootkit.
163500	166680	So the first option you have is to abuse legitimate drivers.
167020	171800	There are a lot of publicly available vulnerable drivers out there.
171800	176800	And with some reversing knowledge, finding your own zero day in one of these drivers can be trivial.
177580	183320	Examples of vulnerable drivers include Capcom's anti-cheat driver, Intel's NAL driver,
183320	188660	and even Microsoft themselves. Now the reason I put vulnerable and zero day in quotes
188660	193980	is because oftentimes these drivers require administrative privileges to communicate with
193980	200840	them. Following Microsoft standards, Ring 3 administrator to Ring 0 is not a security boundary,
200840	206040	so technically they're not vulnerabilities, but that doesn't mean we can't abuse them.
207400	211160	Abusing legitimate drivers has quite a few benefits as well.
211160	216240	You only need a few primitives to escalate privileges. Finding a vulnerable driver is
216240	221560	relatively trivial. A good place to start is OEM drivers. And it's difficult to detect
221560	226760	due to compatibility reasons. For example, let's say a driver had a vulnerable component
226760	232880	that could potentially be exploited. The problem antivirus face is that if the legitimate application
232880	239340	also uses that vulnerable component, how do you detect a malicious program requesting
240360	246080	this vulnerable component versus the legitimate application requesting that vulnerable component?
246080	252060	It becomes a tricky problem. There are some drawbacks for this method as well though.
252060	257640	The biggest issue I've had with this method is compatibility. Oftentimes the primitives you get
257640	263900	might not be a lot and even then can have stability issues such as race conditions.
263900	269780	Now this is a significant threat if you're writing red team tooling because you do not want your
269780	274300	malware to blue screen a victim. That's probably the worst case scenario. It would be a pretty big
274300	279420	indicator of compromise and you're probably going to get caught. This is why I generally stay away
279420	287340	from this method. Now another method is to just buy a code signing certificate. This is completely
287340	293140	valid for some red teams especially for targeted attacks and primarily because you're not going to
293140	297940	have any stability issues. It's the normal way of getting a driver signed is just to buy your own
297940	305300	certificate under your company's name. But it will reveal your identity and also it can be blacklisted.
305300	310460	Now this blacklisting doesn't happen so much nowadays. This is something that I know is being
310460	316740	worked on by antivirus vendors and even Microsoft themselves. It's not widely spread yet so
316740	323160	you're probably not going to see too much blacklisting happening nowadays. Another option is just to use
323160	327920	someone else's certificate. You'd be surprised at the number of publicly available leaked certificates
327920	334020	online. A good place to start if you're looking for one is game hacking forums. Now leaked
334020	338840	certificates have almost all of the benefits of just buying your own certificate without the
339660	346740	anonymization. But the leaked certificate you use can be blacklisted in the future. It goes back
346740	353000	to the same point about buying your own certificate. And if the certificate was issued after July 29th
353000	360780	2015, you can't use it on secure boot machines running Windows 10 1607 or higher
361640	365820	unless it is an EV certificate, which is probably not going to happen.
367120	373500	In most cases, Windows doesn't even care if the certificate appears to be expired or even revoked.
373560	378680	This is because what you see in the digital signature section of a driver is not what the
378680	383980	kernel checks when determining whether or not to load a driver. So this view you see here
383980	389760	when it says a certificate was explicitly revoked, that's what WinVerify Truster is returning, not
389760	395040	necessarily what the kernel cares about. So if you do come across a certificate that's expired
395040	401840	or revoked, chances are you can still use it for signing a driver. Now if you don't want to use one
401840	406540	of the publicly available certificates out there, you can also try to find your own. One method I
406540	412300	found interesting was to use OpenS3Buckets to search for private keys. I used a site called
412300	417860	GreyhatWarfare which searches these OpenS3Buckets and I was able to find over 6,000 results
418560	424600	for the pfx and p12 extensions, which is definitely a place to look at if you're trying to get your own
424600	431540	certificate. And the best part about this method is that it's undetected by the bulk of antivirus.
431540	436320	Now I'd understand if antivirus had trouble detecting a certificate that was released
436780	441280	a month ago because that's recent, but some of these certificates have been out there for years
441280	447840	and yet even the most next generation cutting-edge solutions fail to detect this basic threat.
448920	454240	Let's talk about communicating with a rootkit. A tried and true method of communicating with
454240	460040	your malware is to just call home to a C2. Now firewalls can block or flag these requests
460040	468640	that go to suspicious IPs or ports, and even for more complex methods such as DNS exfiltration,
468640	472300	there are some solutions being developed such as advanced network inspection
472300	476820	that can try to catch some of these methods that attempt to blend in with the noise.
479180	485620	Some malware takes the route that the C2 connects to the victim directly to control it. Now this is
485620	492240	extremely easy to set up, but at the same time it's extremely easy for a firewall to block this,
492240	497380	and it's very difficult to blend in with the noise given that you're using one port exclusively.
498760	504900	A more advanced method I've seen is to hook into an application's network communications directly.
504900	510140	Now this is very hard to detect, especially if you're mimicking a legitimate protocol,
510140	515260	but it's not very flexible because in a lot of environments you're going to have different
515260	519960	ports exposed, different services running, and so if that one port or service isn't exposed,
519960	523560	you can't use that line of communication, which isn't very reliable.
524960	531540	So what I wanted when choosing a communication method was limited detection vectors,
531540	538200	flexibility for a variety of environments, while making the assumptions that some services will be
538200	543380	exposed, which is especially true in corporate environments that have active domain services
543380	548780	running. But inbound and outbound access may be monitored, so it's going to have to be a method
548780	557520	that has a low detection vector opportunity. Now application-specific hooking was perfect for my
557520	563520	needs, except for the flexibility. Is there any way we could change application-specific hooking
563520	569600	to where it isn't dependent on any single application? Well, it turns out there might
569600	575960	be a way. So what if instead of hooking one application directly, we hooked network communication
575960	581720	similar to tools such as Wireshark? So this means that we would hook every packet that reaches any
581720	588980	port on the system, and we're able to inspect it. And then what we did is on our C2, we created a
588980	595540	custom packet that had a magic constant value. And right after that magic constant, there was
595540	599860	information we want to pass to our malware. So what we could do is we could send this malicious
599860	606200	packet to any port on the victim machine, and then the victim machine running our malware would check
606200	612180	every packet incoming and see, hey there's a packet with a magic constant, and we'll be able to use
612180	618680	it as a reference point to extract information from our C2. So using this method, we could
618680	625880	communicate from our C2 to our malware by abusing any legitimate port on the victim machine. Let's
625880	630810	talk about how we would actually do this, and specifically hooking to user mode network stack.
630810	637550	So a significant amount of services on Windows rely in user mode. And how can we
638060	644930	globally intercept this traffic? Well, networking relating to Winsock is handled by afd.sys,
644930	651270	otherwise known as the ancillary function driver for Winsock. Reversing a few functions inside of
651270	656860	mswsock.dll revealed that a bulk of the communication was performed through ioctals.
656860	662780	If we could somehow intercept these ioctals, we could snoop in on the data being received.
663200	670140	So how do ERPs know where to go? So how does a kernel determine what function to call of what
670140	676600	driver? Well, first it'll obtain the device object associated with the file object by calling
676600	683160	ioget related device object. Now for our purposes, this will just be retrieving the device object
683160	689420	member of the file object structure. If the driver associated with the device supports FastIO,
689420	694800	it'll dispatch the request using the FastIO dispatch table, part of the driver object
694800	701640	structure. If the driver does not support FastIO, then it'll allocate and fill out an ERP and
701640	708800	forward that ERP to the driver by calling iocalldriver. Now there's a few standard methods
708800	717420	of intercepting ERPs, and the first method is to replace the major functions table or array
717420	724060	that is part of the driver object structure. Now this major function array contains pointers to
724060	730720	dispatch functions, and the index for this array directly corresponds to the major function code,
731320	736920	which is relevant for that dispatch function. So for example, what we can do is if we wanted to hook
737400	742800	a certain major function code, we could replace that index in the array with a pointer to our
742800	749680	own function, which would redirect ERPs for that major function to our driver instead. Another
749680	756920	option is just to perform a code hook directly on the dispatch function itself. So when picking a
756920	761340	method, there's a few questions you want to ask yourself. How many detection vectors am I exposed
761340	767880	to? How usable is the method, both from a compatibility and stability perspective? And
767880	774780	how expensive would it be to detect the method? Well, for hooking a driver object in memory,
774780	779860	you're going to be exposing yourself to memory artifacts. And from a usability perspective,
779860	784360	you're going to be quite stable, mostly because driver objects are well documented.
785100	789160	And from a detectionist perspective, though, it wouldn't be crazy difficult for
790120	795080	antivirus to detect because there's only going to be a handful of driver objects out there because
795080	800940	there's only a handful of drivers loaded at once. And all an antivirus would need to do is enumerate
800940	806660	these driver objects and check the major functions array for signs that the dispatch function is
806660	814240	outside of the driver's bounds. For hooking a driver's dispatch function directly with a code
814240	819160	hook, you're going to be exposing yourself to memory artifacts. But unless the function is
819160	823940	exported, you're going to need to find it yourself. And there's some a couple of ways to do this,
823940	828840	but it's something to especially consider if the underlying driver file might change between
828840	834800	Windows operating system versions, which aft.sys does. And also, not all drivers are going to be
834800	840560	compatible with this method due to patch guard. And this is also HVCI incompatible.
840560	846580	Now, how expensive would it be to detect? Well, that can vary. So there's quite simple ways of
846580	851320	detecting hooking that are varying inexpensive. There's other methods that can get pretty
851320	857200	expensive. Now, one inexpensive method could be if they know that you're hooking this one function
857200	862480	in this one driver, they could check the bytes of that driver function to check for tampering.
862480	870540	That's pretty inexpensive. But another method is if the antivirus can enumerate every driver loaded,
870560	877260	and specifically, the executable sections of that driver module to check for differences between
877260	886920	what's on disk and what is on the actual what's in memory. Now, I wanted a method that was
886920	894700	undocumented, stable, yet relatively expensive to detect. So what if instead of hooking the
894700	901420	original driver object, we hook the file object structure instead? Well, if you recall to one of
901420	907720	our previous slides, the way that the kernel determines what device is associated with a
907720	914180	file object is by calling IO get related device object. And for our purposes, this is the device
914180	920020	object member of the file object structure. Now, what is stopping us from overriding this
920020	925200	device object pointer inside of the file object with our own device? Well, it turns out absolutely
925200	932600	nothing. So what we can do is we can create our own driver and device objects, patch our copy of
932600	938580	the driver object using a common method such as replacing the major function table. And then we
938580	944680	can replace the device object pointer inside of the file object with our own device. So let's
944680	950200	talk about how we would do this. Well, first, we need to find file objects that are to the device
950200	958160	AFD device object. But how can we actually find these objects? Well, the Windows NTOS kernel
958160	963320	exposes this great function called ZW query system information, which allows you to query a lot of
963320	968060	different information about the system. And one of the classes you can query for is called system
968060	973940	handle information, which allows us to enumerate every handle opened on the system, including the
973940	979860	process ID that the handle is associated with, and the pointer to the kernel object associated with
979860	986300	that handle. Now, if we open the AFD device ourselves, we can easily determine if a file
986300	991740	object is for the AFD device by comparing the device object member with the previously opened
991740	998500	AFD device. So then we can see, okay, this file object is associated with the AFD device. Now,
998500	1004320	before we can overwrite the device object member of the file object, we need to do some preparation
1004320	1010280	first. And specifically, we need to create our fake objects. Fortunately, the kernel exports
1010280	1016920	the function we can use to create our own objects. We can call obcreate object passing in an IO
1016920	1022620	driver object type and IO device object type respectively, and then copy over the existing
1022620	1029200	object data using a function like mem copy. Now with our fake objects created, we're almost ready
1029200	1035360	to set the device object of the file object. But first, we need to hook our driver object. And
1035360	1040720	the way we can do this is by using the standard major function hook method. Except remember,
1040720	1045640	we're performing this on our own copy of the driver object, not what a normal antivirus
1045640	1052300	could retrieve. So it's actually relatively safe. Now to prevent race conditions between
1052300	1059180	our hook function and the device object member, we need to replace it using an interlocked exchange.
1060000	1066040	Now, one thing to remember here is that if when you replace the file objects device object,
1066040	1074320	you can have a normal the Windows kernel call that file object at any time. So you're going
1074320	1079080	to want to use an interlocked exchange in order to make sure the device object you use in your
1079080	1084960	hook is set at the same time the device object is replaced inside of the file object.
1085620	1091000	So now that we've actually hooked the file object, there's not much work left.
1091020	1096740	Inside of our dispatch hook, we need to check to see if the major function code being called
1096740	1102440	is hooked. And if so, we need to pass the original dispatch function, the original device object,
1102440	1108500	and the ERP to our hook function. Now the trick here is also if we receive the major function
1108500	1114640	code cleanup, we need to replace the device object member of the file object with the original
1114640	1122140	device object. This is to prevent issues during teardown. So from a detection perspective,
1122140	1126980	we're going to be exposing ourselves to memory artifacts. And from a usability perspective,
1127400	1130900	most of the functions we're going to be using for this is semi-documented
1130900	1137500	and unlikely to change significantly. And finally, how expensive is it to detect the method?
1137500	1142160	It's going to be pretty expensive because an antivirus would need to replicate our hooking
1142160	1148160	procedure. And so they would have to enumerate every file object to detect if the device object
1148160	1155060	has been tampered with, which is also add some complications. So now let's talk about how to
1155060	1163020	spec Darukin. Darukin I wrote abuses the user mode network stack. So now using our file object hook,
1163020	1169240	we can intercept ERPs to the AFD driver. This will allow us to inspect all user mode traffic
1169240	1175460	and send and receive our own data over any socket. To review our existing plan, we're going to hook
1175460	1180600	user mode communication similar to tools such as Wireshark. And then we're going to use our C2
1180600	1188160	to place a special indicator inside of a packet we sent to any port on the victim machine.
1188200	1195340	And this way, our C2 can then communicate with our malware through any port, any service that
1195340	1201800	is exposed. Now, how can we actually retrieve the contents of the packets that are received
1202620	1208400	when the WCA receive function is called? Well, when you call that function, it's going to send
1208400	1214800	an IOCTL called IOCTL AFD receive. And specifically, it'll pass the AFD receive info structure in the
1214800	1220860	input buffer. Now this buffer, now this structure contains some flags, but what we really care about
1220860	1227160	is the WCA buffers, which is essentially an array of arrays. And these buffers actually
1227160	1233400	contain the bytes that are received from the packet, which we can use then to look into.
1233740	1237780	So let's talk a little bit more about how the Spectre Rookit was designed.
1237860	1243620	Starting with our packet structure, you can prepend any data you'd like, as long as it
1243620	1250240	doesn't contain the magic constant at the start of your packet. And after any prepended data you'd
1250240	1255800	like, you can place a magic constant, which will act as a reference point for the malware.
1256040	1262240	And after the magic constant, you can add a base packet structure, which will have basic
1262240	1268260	information, as you can see on the right, about the packet length and the type of operation being
1268260	1274680	requested. After the base packet structure, you have an optional custom structure. Now this,
1274680	1278680	again, custom structure is optional, so it might not be there in the first place.
1278680	1284460	But this will vary depending on the operation being requested. And after this optional custom
1284460	1290900	structure, you can have any data you'd like. Now this model allows for quite a bit of flexibility,
1290900	1295980	because you can first send any packets you'd like, even one that doesn't contain the magic.
1295980	1301740	And then you can send a packet that has any prepended data or any appended data with the
1301740	1307560	magic content inside, malicious packet inside. And then you can, after that packet, send any
1307560	1313240	packet you'd like. So the key, what I was trying to get at here, is that the Specter Rootkit design
1313240	1319980	allows for quite a bit of flexibility in how you structure your packets. So what happens when the
1319980	1324660	Specter Rootkit receives a packet? Well, it's pretty simple. First, it'll search the buffers
1324660	1331220	for the magic constant. If the buffer contains the magic, it'll go ahead into the processing stage.
1331320	1336540	And if the buffer does not contain the magic, then it'll just ignore the packet.
1336540	1343400	Now, before dispatching the packet, there's a few steps we need to take. First, if we have enough
1343400	1348420	bytes to fill out a base packet structure, we'll try to fill out a custom structure as well, using
1348420	1354160	the bytes we already received. In any case, if we do not have enough bytes for either the base packet
1354160	1360160	or the optional custom structure, we'll receive the rest. And finally, we'll dispatch the packet.
1362140	1367880	Now, before we go any further, let's talk about the concept of packet handlers inside of the
1367880	1374900	Specter Rootkit. So the Specter Rootkit contains this general packet handler class that exposes a
1374900	1380980	virtual process packet function. Now, this base packet handler class has a default constructor
1380980	1386280	that receives a pointer to the current packet dispatcher instance. And the process packet
1386280	1392230	function receives a pointer to the packet itself. We'll talk more about the dispatcher later.
1393320	1400440	Now, an example of a packet handler included with the Specter Rootkit is the ping packet handler.
1400440	1406180	This handler is quite simple. It is just used to determine if a port slash machine
1406180	1413160	is infected. All it is, is a bare bone magic and then base packet structure. And this base
1413160	1418640	packet structures has its type or operation set to ping. And when the Specter Rootkit, or
1418640	1424580	specifically the ping packet handler, receives a packet, all it will do is send back an empty
1424580	1433760	base packet with the operation set to ping. Now, once a packet is completely populated,
1433760	1439580	the packet dispatcher will allocate a packet handler depending on the requested operation.
1439580	1445480	Then it'll call that packet handler's process packet function. And finally, it'll free the
1445480	1450900	packet handler. Now, the reason the packet dispatcher model is really nice is because
1450900	1458780	by passing a pointer to itself, to any packet handler, any packet handler can then recursively
1458780	1466160	dispatch a brand new packet. Now, before we get into how this dispatching works and recursively
1466160	1472820	dispatching, to give you an example of how the flow of a ping packet goes, is first the
1472820	1477800	Specter Rootkit receives that packet. It'll recognize that there's a magic constant there.
1477800	1483820	It'll then fill out the base packet and the optional custom structure for that packet. Now,
1483820	1487900	since it's a ping packet, there's no optional custom structure, and it'll only make sure to
1487900	1492680	fill out the base packet. Then, during the dispatching phase, it'll allocate the ping
1492680	1499160	packet handler. It'll call the ping packet handler process packet function, and then it'll free it.
1499160	1505280	Now, the process packet function, the ping packet handler, will send back just a simple base packet
1505280	1511920	containing the ping operation, which will indicate to the C2 that this port is infected with the
1511920	1518060	Specter Rootkit. So, the best way I can explain the recursive nature of the packet dispatcher
1518060	1523980	is through another example called the XOR packet handler. Now, the XOR packet handler takes in an
1523980	1529520	optional custom structure called the XOR packet structure, and this XOR packet structure has
1529520	1537620	the XOR key and a XOR content array. Now, what happens is that if the C2 wants to request a
1537620	1543180	certain operation, but it doesn't want to send the exact same packet as it did previously,
1543180	1548580	it can take the packet, the base packet structure it wants to send over, and actually put it into
1548580	1554760	the XOR content array. Then, it'll generate a random byte key, which it'll use to perform a
1554760	1561540	XOR operation on every byte of the XOR content array. Then, it'll send this XOR packet to the
1561540	1569320	Specter Rootkit. When the XOR packet handler receives this packet, it'll use the XOR key in
1569320	1576580	order to de-obfuscate the XOR content array. And then, it'll take that XOR content and call
1576580	1583800	the dispatch function recursively to dispatch that brand new packet. Essentially, this model of the
1583800	1589640	Specter Rootkit allows you to create infinite layers of encapsulation, or layers of obfuscation,
1589640	1593340	allowing you to create variants, even if you're requesting the same operation
1594300	1597360	by applying a layer of XOR obfuscation.
1599660	1606040	So next, let's talk about executing commands, a common feature seen in a ton of Windows malware.
1606680	1612240	Now, before we get into starting a process from a kernel driver, we need to understand how do you
1612240	1617220	actually do that from user mode in order to see what functions we have to re-implement in the
1617220	1625260	kernel. So, in user mode, the first thing we need to do is create an unnamed pipe. We'll use this
1625260	1632360	pipe in order to obtain the output of our process. Then, we'll set the startup info structure, and
1632360	1638640	specifically the standard out and standard error handles, to our names pipe. And we'll set here,
1638640	1643660	we can also set window flags, such as hide to window, so that the victim doesn't see
1643660	1649340	the process being created. Next, after the startup info structure is populated,
1649340	1654360	we can forward this to create process, to actually create the command prompt process.
1654360	1659620	And then we can wait for it to exit using wait for single object. And finally, we can read the
1659620	1665520	output of the command by simply calling read file on the unnamed pipe we created before.
1666100	1671100	Now, let's talk about how we would do this from kernel mode. Something important to remember is
1671100	1675940	inside of a kernel driver, you don't have access to many of the same function you have access to
1675940	1681580	in user mode, because the kernel 32 DLL doesn't exist in kernel mode. Instead, you have to call
1681580	1689480	the NT or ZW variants of functions. And kernel 32 DLL inside of user mode also calls these
1689480	1697060	functions, but it acts as a simplified layer. So first, we need to actually create our pipe. And
1697060	1703260	the way we can do this is by replicating what the kernel 32 DLL does itself. So what create pipe
1703260	1710100	does is first, it'll open the names pipe device, if it hasn't done it before, then it'll use this
1710100	1716820	names pipe device and set the root directory object attribute to the handle to the names pipe
1716820	1723660	device, then it'll call NT create names by file to actually create that pipe. And here, it'll only
1723660	1729620	create a read handle to pass in as a handle strictly used for reading from the pipe. It'll
1729620	1735560	then call NT open file on that read handle in order to open a write handle as well.
1737080	1743020	So now that we've re implemented the create pipe function in kernel mode, we need to create the
1743020	1750500	actual process. And we'll use the same function ZW create user process that kernel base uses itself.
1750900	1757300	Now, we're going to need to replicate the entire process of entire process that kernel base does
1757300	1762760	itself. And so we'll need to first pass in an attribute list. And the only attribute we need
1762760	1768340	really pass is the PS attribute image name attribute, which will specify the image file
1768340	1775840	name for the new process. Next, we have to fill out an RTL process parameters structure for the
1775840	1782180	process. In this structure, we need to set our window flags and the output handles to our pipes,
1782180	1788380	similar to what we did with the user mode startup info structure. But we also need to specify the
1788380	1794940	current directory, the command line arguments, the process image path, and the default desktop name.
1795200	1801900	From there, all it takes is a call to ZW create user process in order to start the command prompt
1801900	1807860	process. Once the process has exited, we can easily read the output of the command by calling
1807860	1814660	ZW read file on the read handle we obtained for the pipe. Now let's talk about what you can do
1814660	1821280	to hide your rootkit. So introduction to many filters. So many filters driver allow you to
1821280	1826880	attach to volumes and intercept certain file operations. This is performed by registering
1826880	1834000	your mini filter with the filter manager driver. So this reference from Microsoft documentation
1834000	1840660	shows an example of user requesting file IO. First, the IO manager forwards the request to the file
1840660	1846880	system. While this request is being forwarded, the filter manager driver intercepts this request
1846880	1854480	and calls the registered mini filters that I've registered with it in order to, for example, modify
1854480	1861440	the request being performed before being sent to the file system itself. So many filters essentially
1861440	1869460	allow you to edit file operations before they actually happen. And many filters can be used to
1869460	1876000	mask the presence of our rootkit on the file system. For example, a mini filter can redirect all
1876000	1881820	access to a certain file to another file. We can use this to redirect access to our rootkit file
1881820	1887460	to another legitimate driver file. Now again, going back to picking a method, there's a few
1887460	1892800	questions you want to ask yourself. How many detection vectors am I exposed to? How usable
1892800	1897460	is the method from both a stability and compatibility perspective? And how expensive
1897460	1903320	would it be to detect the method? Well, the easiest way to abuse the functionality of a mini filter
1903320	1908740	is to follow the documented procedure and just become a mini filter yourself. So here are the
1908740	1914860	requirements for the function fltRegisterFilter. You're going to need to create an instances registry
1914860	1921380	key under your service key. And under that instances key, you need to create an instance name key,
1921380	1926580	which can be whatever name you'd like. This is going to be the name for your filter instance.
1926580	1933860	Then under the instances key, you need to add a default instance value, which is a string and is
1933860	1941080	set to the instance name you created in step 2. Then under your instance name key, you need to
1941080	1948440	add the altitude and flags values. And the altitude is pretty much what the ordering is
1948440	1955520	that many filters get called. The higher your altitude is, the first in line you are
1955520	1962740	for your mini filter to get called. So how many detection vectors are you exposed to?
1962740	1967880	You're going to be exposing yourself to registry and memory artifacts. How usable is the method?
1967880	1972040	Well, you don't really have concerns from a stability or usability perspective, because
1972040	1978560	this is just how legitimate drivers register as a mini filter. But at the same time, it's pretty
1978560	1985520	easy to detect this method, because besides the registry artifacts, an antivirus could easily
1985520	1990720	enumerate registered filters and their instances through documented functions, such as FLT
1990720	1998920	enumerate filters or FLT enumerate instances. So another option is to just hook an existing
1998920	2004160	mini filter. And there's a couple of routes you can take. First, you can just do a basic code hook
2004160	2010940	on an existing mini filters function. You can overwrite the FLT registration structure,
2010940	2018980	which is passed into FLT register filter before the victim driver calls it. Or you can edit a
2019200	2024900	existing mini filter instance through DCOM to replace the function that gets called with your
2024900	2032240	own. Now one of the easiest ways to intercept callbacks to an existing mini filter is just
2032240	2037180	again a code hook. Now this can be as simple as a jump hook, but it comes with quite a few
2037180	2042940	drawbacks similar to what we saw when we were talking about intercepting ERPs. You're going
2042940	2047100	to be exposing yourself to memory artifacts, and unless the function is exported, you're going to
2047100	2052620	need to find yourself, which can be difficult if the underlying image changes between operating
2052620	2058920	system versions. Not all drivers are compatible with this method due to patch card. And finally,
2058920	2065740	this is also HVCI incompatible. Now similar to what we saw with hooking ERPs, it's going to be
2065740	2071600	potentially inexpensive because if a antivirus knows you're hooking a specific function in a
2071600	2077100	driver, it can easily check the bytes of that driver function to determine if there's any
2077100	2085260	patching performed. Now another way of obtaining access to a mini filter, existing mini filters,
2085260	2090060	Now what you can do is you can enumerate filters and instances, as I mentioned before,
2090060	2096620	by calling the functions fltenumeratefilters and fltenumerateinstances. Now the function that gets
2096620	2102680	called for a certain filtered operation, for a mini filter, is specified in an array called
2102680	2108400	callback nodes, which is part of the filter instance structure, which you can find by using
2108400	2114680	the previously mentioned functions. Now the callback nodes array index is directly associated
2114680	2121080	with the operation that is being filtered. If you find a callback node array entry for the
2121080	2126100	operation you want to target, you can replace the pre-operation and post-operation pointers
2126100	2132900	to your own function. Now one note here is that you will also want to replace the FLT registration
2132900	2140300	structure that is part of the FLT filter structure. Now this is because the FLT registration structure
2140300	2146600	will contain the original function pointer. And what an antivirus could then do is check to see
2146600	2151860	if there's any discrepancies between the callback nodes array and the FLT registration structure,
2151860	2159160	part of the filter instance, sorry, the filter itself. Now from a detection vectors perspective,
2159160	2164760	you're going to be exposing yourself to primarily memory artifacts. And from a stability perspective,
2164760	2171380	the only concern you should have is that the FLT instance structure itself is undocumented. Now
2171380	2177200	finding an FLT instance is easy through documented functions, but the structure at the FLT instance
2177200	2182280	structure itself may change across operating system versions. Now how expensive would it be
2182280	2186860	to detect the method? Well it would be inexpensive because all an antivirus would need to do
2186860	2192960	is occasionally enumerate registered filters and their instances in the callback nodes array.
2193680	2198920	So let's say you want to protect a certain file, such as our rootkit file. What's an example of
2198920	2205660	redirecting access to it? Well first, I'm assuming that you hooked a minifilter's pre-create operation
2205660	2212680	callback. Inside of that pre-create operation callback, you can use FLT get file name
2212680	2218660	information in order to get a normalized path to the file being accessed. Then if the path contains
2218820	2226100	a protected file, such as the path to our rootkit, you can replace the file path being accessed by
2226100	2232880	calling io replace file object name on that file object. Then you need to set the status being
2232880	2240420	returned to status reparse and return FLT pre-operation complete so that the file system
2240420	2247940	will then redirect access to the legitimate driver file. So for example, you could use this
2247940	2253760	to change file access to your rootkit file to another legitimate driver that might be in the
2253760	2259100	same directory so that when a user or program inspects it, it'll actually be looking at the
2259100	2266520	contents of a completely different file. Okay, wrapping up. I'd like to give thanks to Alex
2266520	2273240	Ionescu, a longtime mentor who's very experienced with Windows internals. The React OS
2273240	2278900	is an amazing reference for Windows internals, especially for undocumented functions, functions,
2278900	2284380	and structures. And thanks to Nemanja Mlazmajic and Vlad Ionescu for helping me review this
2284380	2291600	presentation. Thanks for sticking around. You'll be able to ask questions in my DEF CON Q&A session,
2291600	2297360	which will be later today. Make sure to follow me on Twitter and look at my blog.
2297360	2303080	And you can check out the Spectre rootkit, assuming everything went well, at the URL below.
2304220	2306920	Thanks for coming out to my talk again. Have a great one.
