← All talks

Unmasked! - Fighting Stealthy Execution Methods Using Process Creation Properties

BSides TLV · 202234:30177 viewsPublished 2022-07Watch on YouTube ↗
Speakers
Tags
StyleTalk
About this talk
Unmasked! - Fighting Stealthy Execution Methods Using Process Creation Properties by Philip Tsukerman and Amir Kutcher
Show transcript [en]

[Music] [Applause] [Music] hi hello there dearest attendees uh my name's Philip and I was incapable of providing a correct email for the besides registration which makes me equipped to talk to you about technology and I work for Microsoft hey everyone my name is Amir I'm a senior engineer in Microsoft Defender for endpoint unlike Philip I do remember my email address so I sent the right man and that's it basically right so let's for let's for a moment suppose that you've had the privilege to run a piece of malware on someone's computer uh now don't worry this is the only meme in the presentation and pretty much everything else is like kernel debugger screenshots so do not worry

so if you have this opportunity you mostly do not want to get detected and to do so you mostly don't want to look like I don't know malware.exe or any kind of weird process or executable and it's often preferable for you to seem like you're some kind of legitimate process even more beneficial if you're able to do your malicious stuff from a process that does similar stuff for example if you're connecting to the internet and talking by using SSL or whatever you would like to use a browser or something like that and there are no there are numerous techniques that allow you to do something like that uh the most prevalent category of those techniques

is called process injection which is actually made out of two main categories two main Primitives first is a right primitive meaning that if I want to execute in the context of another process I would like to have my code inside of that process now there's a lot of different apis techniques and whatever that you can use for that you can you can manually allocate allocate memory inside of that process and then write to it you can map shared memory you can do a lot of stuff but in the end there's the right primitive that allows you to have your code inside of another process the other primitive that you need for that is an execution primitive because

oh cool you have your own memory inside of a process but like who's gonna execute it why would that process run your code so there's a bunch of different apis that your piece of malware could use to actually divert the execution for for that process now you could start a new thread in that process and make it start executing your code or you could try to divert at least in Windows the execution of an existing thread using things like set thread context or apcs but this is not the only category of this kind of masquerading attack so another alternative is using some kind of something called process hollowing unlike process injection when you take an arbitrary process and

introduce new code to it in process holding the basically you create your own process with a legitimate binary and you somehow replace the code with a malicious one how it basically work is you create a process in a suspended State and before it actually starts running you unmap all the legitimate memory of of the process and replace it with your own one then after you remap the process you're gonna you're just taking the initial thread and redirects it back to the new starting endpoint entry point of the process after you're completing the redirection you just resume the initial thread and the process starts the execution but it's running the malicious memory so in the last few years we observed a

new static execution technique and which the fundamentals of it is basically unlike process enrolling that use a lot of memory operation I mean you go ahead you remap the files you unload some memory there's a lot of memory operation in between this specific class is focusing on the file system itself and the target of this class is basically created from malicious process from the disk and somehow do not leave any records of it the thing how it's work is by somehow allowing the attacker before the process starts execution to somehow revert the malicious content from the disk and so basically the process is initial is launched from a malicious a malicious image file and then the image

file is being somehow rolled back and a benign content is being overwritten through the file itself and when the process starts the execution itself there's no actual content to be to actually scan so there's a bunch of examples in the last few years we observed one of them is called process Doppler gang for example that uses anti-fs transaction basically they create a file which is the transaction contacts they override a benign file with the malicious one and they create a process from that file but before they start they start the execution of the process they just roll back the identity NTFS transaction they never committed to the disk so the real content array isn't really committed

another another approach within the another technique we've seen the right is called process hypertherapy basically like process Doppler gaming they get they get the same execution primitive they somehow get a writable handle to the process file and they just overwrite it with the benign content and again in the same way they do it they do it before the process execution and again when it when it starts execution we have nothing to scan for and the other the last Technique we can talk about is process ghosting which basically leverages deleted deletable handles to the file and they actually delete the file before the process does execution so when we want to scan it there's nothing on the disk

so let's talk about our motivation for this session so we've seen all those kind of techniques and we want to understand why which OS properties make them work because if you think about it is it a process and it has a backing file which is completely different with for Miss memory which doesn't make a lot of sense right so we wanted to find out which dependencies and which primitive they are using inside the OS to actually understand how they work and once we find all the common ground and all the Primitives they are actually using hopefully we can build a robust detection mechanism that allows to to detect those anomalies and hopefully break the entire class and not just

those techniques so when does antiviruses scan files optimally we would have scanned a file each time would have changed right and for every change we go ahead and scan it and see the actual content the problem is that from performance perspective it's not really acceptable you can't scan file all the time and so how we do it is basically try to scan it in a stable state for example when the file is being opened for use we know it's not doing editing or something we can safely scan it now another option is during the cleanup procedure a cleanup is where the handle for the file is being closed and the user basically says I'm done using

the file I've done editing it I done reading and that's a really stable point for us to go ahead and scan the file another option is when a section object when we talk about the bit later is being created on top of the file as a part of the process creation for example because it's need to be in the same in a stable state to create a section that's a good place for us to scan as well and the last uh probably more optimal place to scan is when the process is actually created as an antivirus we can see those kind of event and that's a good place for us to go ahead and scan

the file before we allow the process to execute right so to understand how this kind of technique can even exist which is basically some kind of time of check time of use attack because the moment we are able to scan the process or the file it's completely different from what we are expecting to be to be on disk we need to understand what can happen before that last opportunity before the process actually starts existing and to understand that we need to understand how the kernel views process creation or how a kernel antivirus driver could view process creation so process creation notifications or callbacks have been designed for this Express purpose it's to allow a driver

to inspect every single created process and say oh this is good this is bad I want to do something to this now counter-intuitively process creation notifications are not executed when the process object is created but rather when the initial thread is inserted into that process and this is invoked in the context of the Creator process and not the one that is actually getting uh executed now this small distinction might not sound interesting until you start diving into the internals of Windows process creation the modern way to create processes in Windows is through an assist call called anti-create user process now this syscall gets an executable path as an input meaning that oh here's the path to

I don't know cal.exe and whatever and when it enters the kernel it opens the file on its own creates the process the process object inserts the first thread does a bunch of other stuff and then returns this means that from the moment I actually call this function and and when it returns I already have an operational process perhaps not fully operational but something that already has opened the file already created the process object already has code and that code can run and there's really no time of check time of use thing here because um I can't really play around with the file as long as this syscall is executing and the process creation callback as

we've said is executed and invoked during initial thread creation but this initial threat insertion into the into the process happens within the context of the of the syscall so looks like we're okay here we have our process creation callback being executed in this call stack and we can see that anti-create user process is somehow is somewhere near the the base of the kernel of the kernel mode call set so we should be okay no time check time views no race conditions the problem is this is not the only way to create processes in Windows and actually before Windows Vista there was another pair of Cisco's anti-create process and anti-create process EX now this Cisco pretty much breaks every

single assumption an antivirus or an endpoint protection uh solution has regarding process Creation in the kernel first of all you don't really get a path to an executable as it doesn't to an executable as input and instead you have a section object that you need to obtain from previously opening a file on your own creating a section from that file and only then uh submitting it as an argument to the access call this means that between the creation of the file and the execution of the syscall we have arbitrary time that we can use to do whatever uh the the second thing that's interesting about the Cisco is that it creates exactly zero threads in the

process why is that interesting because as we've said the process callbacks the process creation callbacks are executed within initial thread creation so we can open the file do whatever create the process to do whatever and then create the initial thread and after and we have this huge opportunity of arbitrary time in which we can do weird stuff to the to the file uh now let's look at a generalization of all of our attacks of of process ghost interpreter being and doppelganging and also a couple of other unpublished variants so the attacker opens a file in some kind of modifiable state this could be using a right handle this could be using a share right handle uh Delete

access or even just a volatile transaction that could be rolled back after this we introduce malicious content to that file and we don't close the handle and as we've said we can't just scan on every right so this is just one more right nothing happened to this file that puts it into a new stable state so no scanning we then create a section and use that section to create a process again it's still not time for a scan because if you scan at this point for every process you will not be able to you know run any actual application code between your antivirus scans and will make your customers extremely angry so we still cannot scan at this point

we then can revert the changes delete the file we roll back the transaction override the file with whatever and the section bits in the in memory are unchanged because that that is already something that has been mapped and only then can we create the initial thread of the process and now we have a process with malicious content and some weird file or maybe even a non-existent file but something that an AV really cannot find on disk and didn't have any opportunity to scan uh before we did our evil stuff so what do we want to detect here we want to detect two things one is this Legacy process creation API usage because if you use the modern process creation API

you don't have the opportunity for a for a time of check time of use so if we don't have the opportunity to abuse this this is the prosecution is not interesting uh the second thing that we want to to understand is whether this time of check time of use was abused meaning that someone has actually done something weird to that file from which the process was created because it's not enough to have just Legacy apis you actually want to find an attack and when do we want to see this we want to see this at as as late as process creation notifications because we want to be able to stop this malicious process or this

potentially most delicious process from executing and we want to do this using minimal scanning because again if we always can we can't run application code and while this is more secure throwing your computer out of the window is also more secure and we really want to run some kind of code so first let's talk about Legacy process creation so there are common patterns for this kind of for this kind of detection work which really aren't applicable because we want to detect this from the most privileged place we can so that user mode attackers can't really bypass our stuff first of all you could use user mode hooks but you're basically trying to mess around with a process that's

completely controlled by an attacker and they can always unhook Your Hooks it might take a bit of effort but there's no vulnerability involved they will always do this if they need this another thing that you can do is call stack analysis the user mode call stack can also be forged if I wanted to look that there was some kind of arbitrary call stack I can just push the same addresses to the stack itself and then jump to whatever code I wanted to execute from so I can push all of my arguments uh or I actually can push a bunch of addresses push up push all of my arguments jump to whatever Cisco stuff I want and then I have an

arbitrary uh user mode call stack kernel mode calls directs are also problematic because there's a lot of ways to create processes and threads from the kernel and operating systems change constantly so we can't really rely on one call stack remaining the same so this is also quite unreliable we could try to look at the the bits in memory versus the bits versus the bits on this at some point but this is uh again a strong perf concern so we do not want to do that we want to see if there's any way to detect this with zero scanning and with zero Reliance and user mode so to do this we've discovered uh we can use something called NTFS extra file X

to create parameters this is a Windows File system feature that allows the kernel and other file system drivers or mini filter drivers to supply some kind of context to the to the file creation operation of the file why do we want this because for example it's useful for a file system to know whether a file was open remotely or whether a file was opened by some kind of weird kernel feature or file system features such as the prefetcher so that the operating system itself can treat it somehow differently now extra create parameters are basically just some kind of key Value Store where the key is the guide that said that tells you which kind of extra create

parameter this is and the value is some kind of struct that well whoever needs to read that X to create parameter knows how to use uh now something funny about extra create parameters that is that there's a couple of documented kernel mode Windows apis that check whether this ECP extra p8 parameter originated from user mode or from kernel mode and this actually checks a bunch of internal kernel structures in the in the RP and the request package for the open request and checks whether there's a special flag there and this sounds interesting but there's actually no user mode functionality then can't Supply that so this entire function is enough and it's a no-op and if you're in user

mode you can't really Forge any kind of extra create parameter now why is this interesting because during modern process creation and to create user process actually opens the file that we Supply as a path using and appends a specific extra create parameter to it it was actually made for for AVS but not for the express purpose of our attack and this thing actually contains the token of the Creator process but that's not as interesting for us what's interesting for us is that we need to supply to the Legacy API an open file handle or even a section created from an open file handle and if we're opening this from user mode there's no way we can make this look as

if it has this ECP so right now we have an unforgeable Way by user mode to differentiate between modern process creation and Legacy process creation meaning that now if we don't see this ECP in in the open request for the file we know that oh there's a chance uh time of check time of use attack uh could be abused but as I've said this is not enough and we need to understand whether this attack was abused so as we understand that basically Legacy process allows the attacker the gap of the window that it can modify the file before it actually inserted the initial thread and gets scanned we still not understand why there's a process and

a backing file which are complete not the same which is not make a lot of sense so let's dive a bit to the windows memory manager the first object we're going to talk about is section object a section object is basically an object that represents a shared memory across the operating system there are basically two types of section objects one of them are file backed basically shared memory that is created on top of a file it can be a data file just like any a text file or Word document which is basically the same as you see on the disk and there's also can be an image file that you can you can back up like in process for

example the other type of section are page fire section page five section are basically a shared memory that is not backing by any file at all basically it also calls like memory section and basically you can share a bunch of memory Pages between different processes and it works just fine so when we talk about section mapping how we're going to actually going to map this section into memory there are two type of section mapping one of them is data mapping basically you want to view you you want to write your to your wall document and you want to map it the data that's in the memory it's the same as in the file it's basically a reflection

every byte offset in in the memory is the same in as in the file but unlike data file and mapping an image file is a bit more complicated because it's highly rely on the PE format if you recall P format it contains a lot of section there's a text section with basically the code which is readable and executable there's the data section which is the read and write and all those kind of sections should be reflected differently in memory because in the process memory there's not it's not like one bulk of memory read that right execute every section Define the layout of the memory so there's a more complexity for mapping image files the other structure we're going to talk

about is the control area so when each each time process want to open a shared memory to map it so you can use it it goes ahead and create the section object each process has its own sector object reference but we need how to connect all them together so all the section will point out to the same a pages so we can we can actually share data so each time a section is initially created there's a controller area that is created by the memory manager as well and the control area basically holds and maintains all the metadata we require to go and actually manage those short section either it's the segments it's the permission for the for the file

um how many views are all currently mapped on the control area everything that the memory manager needs to know is centralized in the control area now it's important to note that because as we've said there are different type of mapping mapping data in images there's a control area created for each type of mapping there's a single control area for all the data mapping pointing out to one file and there's also an image control area that's pointed out to the image mapping and the last is a prototype it is segments basically itself basically it holds an array for this section this section and it's just an array of ptes but it's not a normal pities it's our prototyped PTS

prototype pts are special kind of pts they are not really used by the hardware by the MU to go ahead and just execute code they are specifically used by the memory measure to Cache the physical Pages for the shell memory now each time a process wants to go ahead and map if the memory measure goes ahead and Proto pts so if you can if ptes that's going to be

now let's talk let's talk a bit about section and backup file miscoherency so we know that the fact is that those techniques cause the process to run malicious code while the backend file is completely different and for obvious reasons there's some sort of miscoherency between the memory and the backing file but if you recall creating a section image requires a handle to a file section image cannot be created on the page file they handle so you cannot really you cannot really create second image that are totally on memory not upon file but when does this section is created is its responsibility of the memory manager to guarantee that the memory and the backing file will be coherent for all

the light of the section because eventually when there's paging eye of from you need to move the pages back to the file and then we need to read from the file back we want when we want to restore execution so it's the memory magic guaranteed to provide it but as you've seen in all those techniques the attackers somehow used a modified handle like using transaction object writable file a file they don't use the read-only handles and if you think the image section but it knows that the handle I mean it can't guarantee that it will file and then the next paging IO the process will so in that point of the section creation doing those guarantees so what it's

actually story to be backed up by the page file instead of the file it goes ahead to the physical pages make sure they go back to the page file and not the file itself so it's basically decouples between the two because it know it can't guarantee the coherency and the result is that you have an image section which is backed up by the file there's a file path and it all seems good oh so what we and that we have a suspicious that rather buy some arbitrary memory pages that have nothing to do with the file so as the memory manager goes ahead and inspects the file object and check if it deletable any what we call writable

references it's automatically decides let's move it back to the page file and it does it in the section creation we can do the same thing and look in the process file object which is the same file objects in the section and see if it was open with writeable a whatever access deletable access anything thought of that and understand that if this how the fellow memory manager decided that it can guarantee the currency and everything should be moved back to the paging file so we can mark the process highly suspicious as we know there's most of the chances it's really running on memory only right so now we have two Primitives for detection the first is how we detect

this Legacy API usage and as we've said if there's no uh process create anti-create user process ECP this means we are using almost in every case except for a couple of very scopable edge cases we are actually using the Legacy API and if you're somehow able to forge this thing because this is not a user mode functionality at all this probably means you have some kind of Kernel read write primitive which well you can do other better stuff with a read write primitive in the kernel uh so we can safely assume that if you don't see this ECP and the in the file creation uh operation then we are talking about this Legacy process creation API and this gives us

opportunity for a time of check time views and just as the mirrors has explained right now we know that a file has probably been abused because weird stuff happened to the file to the file backing the the actual image section of the process because if this section coherency thing is broken and you can make something that doesn't make the memory manager Mark all of those pages as dirty you have a much more significant problem because now this is an OS stability problem because if my code gets paged out and then Windows tries to take this code from an entirely different file I can override the kernel I can overwrite I don't know some kind of driver I can override

pretty much every piece of code that should be protected and this is a much more significant uh security and stability problem so as long as we assume there's no super cool zero day Happening Here we can assume that again no kind of user mode thing can forge this information and those two things together as we've seen from our data as we've seen from analyzing the code of EOS uh are pretty much a Surefire detection for every single one of the techniques we've mentioned and for any potential variation on this schema because as we've said if you can't forge this information from any from the user mode and if you have any other way that you can basically

pull out the the memory rug from a from under the from under the process and do something weird to the file the the memory manager will know because it has to know so you don't get a blue screen so together with that we now have this super stable 100 uh through positive detection uh that really can be bypassed by user mode um and we've as I said we've tested this against a couple of other unpublished variants um and while there is a way to bypass this this is actually you know using some kind of driver and again you have a kernel read write primitive so do other cooler stuff uh if you're using a militia malicious driver

uh so we at least believe this this entire class of techniques could be called by our stuff and if you want to be do something else you have to actually innovate it do something novel and not another variations of I know process bamboozling or whatever and that's it thank you for listening [Applause] I have a ton of time for questions

um so there's a couple yes of course so we uh we were asked about false positives and examples for false positives so one such example is process cloning which is the weird name we Microsoft people call Fork um now when this happens uh a lot of weird stuff happens there's a bunch of apis that clone the process and then don't use the ACP even even though it's the modern API there's there are other apis that create a new process using anti-create process ex but then this process can never run code so it's not interesting for our case because go ahead open a new process that can't run code I mean I won't stop you but but yeah uh so those are the two

main ones that's all that's all operating system functionality um you have iodide I suppose uh anyone else well it's time for us to take a drink then [Applause] [Music] [Applause]