← All talks

Shatter Reloaded: Reviving Shatter Attacks to Escape Sandboxes and Evade Endpoint Security

BSides TLV · 202040:04107 viewsPublished 2020-07Watch on YouTube ↗
Speakers
Tags
CategoryTechnical
DifficultyAdvanced
TeamRed
StyleTalk
About this talk
Gil Fidel demonstrates a modern revival of shatter attacks—a Windows privilege-escalation technique from 2002—to escape sandboxes and evade endpoint detection and response (EDR) solutions. The talk covers the original attack mechanics, Microsoft's mitigations since Windows Vista, and innovative methods to inject shellcode into high-privilege processes like Windows Explorer by leveraging UI controls and executable heap creation.
Show original YouTube description
Gil Fidel - Shatter Reloaded: Reviving shatter attacks to escape sandboxes and evade endpoint security products BsidesTLV - Tel Aviv - July 2nd, 2020
Show transcript [en]

I'm Gil I'm going to talk about making shutter attacks right again but first I want to say a very big thank you and a big kudos to all the organizing crew and the volunteers for making this happen despite the crazy times you're in so great job guys okay a bit about me sorry I lead R&D in Accenture screw to Israel formerly in Milan and we do development of offensive and defensive tools we do a vulnerability research do some general security related innovation and also AI security so this sounds interesting pink me we're always looking for bright minds to join us now what are what are we going to talk about today so I'm going to give a bit of background

history about shatter attacks how they came to be how they were mitigated in Windows Vista then won't talk about our reincarnation of them in the context of to main use cases the first is breaking sandboxes the second one is very needy ours we're going to talk a bit a little bit about passive integrations and we're going to show demo that will hopefully work so let's start so once upon a time shatter attacks they were introduced in 2002 they were used mainly as a way to do privilege escalation on Windows so the way it would work it would have some win32 service running the system and making the big mistake of showing some user interface some GUI to the user on

the user's desktop and then a user even now guest user would actually send a bunch of special crafted mean Windows messages to this GUI which eventually called code execution in the service in system privileges so the way it would work we have the attacker over here denied it's nice snake have the target process which shows the piece of GUI let among other things includes an edit box and we assume now the dead bugs buffer like the buffer that stores the actual contents of the Edit box is in this constant address so what an attacker would do it will first send a set text message with the shell code he wants to run so since this message copies a

string to the Edit box this shell code should be now free which is not a big problem so he sent this message the shell code is now in the edit box meaning in the distress 2000 hexam then the attacker would need to know this address so this can be this can be done for example by doing some recon in advanced and doing some reversing or debugging and knowing that this is the static address that always holds the buffer of the read box for the specific version of the service for example and then he would simply send a WM timer message and one of the parameters receives is actually the address of the call box it needs it needs to be

executed basically once it sends its message the shell code gets executed and we're all done pretty simple so Microsoft of course patched this well the first batch was actually to not handle WM timer messages for a non previously registered timer which seems kind of vyas and quite funny didn't do it like that in advance so what this means the textual integra now has to first call set timer set timer API in the context of the target process which is something he cannot do unless he already has some code injected into it which kind of it's a perfect purpose so that was good and also Microsoft fixed all of their services not to show any GUI on the usual desktop and of course

and probably updated their best practices guidelines that everybody ignores but that kind of put a stop to the to this simple and vulnerability being used in the world now if we think a little bit in more general what is actually the problem here that caused escalation liability so the first one was the lack of access control so the fact that I process running as a low privileged user even guests could send Windows messages to a high privilege versus even a system process that's one problem the second one was the lack of namespace isolation in terms of Windows sessions and desktops so they're actually no there is no good reason for user processes and services run to be

running on the same Windows desktop at the third one is the fact that the this win32 service expose the user interface directly instead of the way it's done today of running some client utility was you know a little Tricon or something like that which is transient with low privileges as opposed to the service running high privileges and the first one which is like more generic and it's rooted deep in the design of the win32 GUI subsystem is actually the fact that you can send a Windows message across process boundaries that contains a pointer to a call because it's to be executed in the target process well that's a big problem by itself and so with Windows Vista the most of those

issues were fixed so Microsoft introduced so-called you IP I using user interface privilege escalation which basically added a new label to each process and could be either low medium high or system so medium was the default one used for non elevated processes how I was used for elevated ones and low was used for example for the interest Explorer renderer process that we wanted to run with even lower privileges and normal processes and then what is new mechanism would actually do is that you can you could no longer send Windows messages from long term return processes to high integrity level processes that's that so the second second thing it was introduced in Vista its session zero isolation which meant that all

windows services were now running in a distinct window session so previously the first user logon session was session zero which was shared with all the win32 services that was the way in Windows XP for example but starting with Whiston onwards services would run in session zero whether the first user logon session was session 1 and then if there were more users that would get sessions two three etc the third thing might receive did in that regard to preserve better compatibility for services that for some reason still wanted to show GUI directly is that if a service tried to show a piece of GUI the user would receive this funny-looking message and if we did the mistake of

taking view message it would be redirected to a special desktop that contained the UI shown by by the service so that was very good and actually put a stop to shutter attacks altogether because they could no longer be used for escalating privileges and of course there were quite forgotten until we came along not really but let's keep that story so the first use case we're going to talk about it's actually commercial sandboxes so we had a project where we needed to do some fun ability research and assessment of commercial sandbox to find witnesses so this sandbox I've not disclosed its name was was a client but basically the concept was that we want to isolate between trusted and untrusted

processes so untrusted ones which were running inside the side-box are processes that communicate with the internet directly like we can think of for example web browsers stuff with that British trusted processes or the ones that are actually access in process sensitive corporate data like documents etc and have access to the corporate network and basically this sandwich wanted to isolate between them so that it for example some client-side attack gains code execution in the browser to not be able to do any further damage or collecting information so the sandbox it implemented like all the possible isolation you know rather than the Mexican think of so both file system in registry so trusted and untrusted processes had different view of the file

system in registry did network isolation so that trusted processes can access the local network the corporate network where's untrusted processes can access the internet and it also implemented elation for win32 GUI so you couldn't down like copy/paste stuff between them and you couldn't perform GDI operations that taking screenshots and you couldn't send Windows messages between trusted and untrusted processes so we were in quite a bit of a difficult situation there as an attacker because actually the file system najin registry and were implemented using kernel mini filters the required solid and quite difficult to exploit and also the implemented process isolation using object callbacks the OB registered called this API which actually didn't allow processes to open

the processor running the untrusted untrust environment couldn't open a process in the trusted environment for writing so that was the first thing we tried like wanted to for example try injection code to a trusted process but that was denied so we couldn't open a process in which was trusted with right access but we actually were able to open it with read access which proved useful afterwards this will seem so and then we're thinking okay so what what else can we do and or in other in other words what is the weakest link here and not surprisingly there was one which was the way free to you is elation and the reason was the weakest link is it was

actually implemented by user mode hooks since as we know since patchwork came along kernel-mode hooks for api's are not really feasible so the white would work for example let's say I'm running an untrusted process want to send a Windows message to a trusted one so basically the moment I call send message there's an inline hook intercepting it and basically denying the call returning me access denied but since this is a user mode who in the process we are executing we were actually able to remove them Juden quite a generic method of comparing the the entry points of all exported symbols from all loaded modules and then comparing them in memory versus the copy on disk taking relocations into

account of course and we never saw a difference would simply overwrite the memory version with the one from the disk which effectively removed all the holes so now we were actually able to send messages across isolation boundaries so and then wondered how that might be useful so this was before this lecture I banged my head on the wall for a day or two until shatter attacks came along and then basically if you remember we talked about Windows Vista mitigating shatter attacks used for previous escalation but the thing that Microsoft actually didn't address is the fact that you still had Windows messages that could be sent across process boundaries at least processes run in the same

integrity level that contained callbacks which means addresses pointers that are interpreted in the context of a tablet process and executed so this looks like something that can eventually be exploited and actually if you remember we talked in the beginning about basic special tact shatter dive based on the WM timer message but apparently there are a bunch of other windows messages that had the same pattern of having an argument which is a callback so I was stupid enough to go off and enumerate them myself instead of looking for other folks who have already done it but this is what a yeah this is what they found so there are a number of them in which edit controls used for customizing

different behaviors such as autocorrect and start with that the list view control has a bunch of them for certain elements and basically allowing the user to provide a custom compare callback for the sorting and also the tree view had the same so there's a message you can send to a tree view that sorts the elements of the tree and it actually you can give it a callback to the custom compare function which proved very useful as we will soon see so here's a sample for using this tree view message to execute code so this snippet by itself is not really useful to us because it requires and write access to the target process in order to allocate

memory and excuse me and write to it but this will prove useful to understand what we did further okay so I'm going to go over it quite quickly so basically what this code does it injects code specifically into the registry editor so it first finds the tree view that's inside it it gets the process ID opens the process for all access and then allocates memory which is executable and write the payload which is the shell code we want to run into the target process in the registry editor then it obtains the route item from the tree view and then initializes the tv-tv sorts to be struct which basically contains the parent which is the route

item which represent the sub-tree that we want assert hasn't addressed of the compare function which is narcotized address of her shell code and an altar on which is now you don't care about then it allocates memory in order to copy this TV swords be struct into the table process and finally it sends this TVM sort children CB message to crosses and when it's sent it starts sorting the tree view and the moment it first needs to compare to elements in order to implement the sorting it's actually called the callback but the callbacks points or shell code which gets executed and they're very happy so that's the way it works now as I said before it's not really helpful for us as

it is because we still need to find a way to copy some shell code into the target process moreover we need to do it mean to put the shell code into executable memory because otherwise that will prevent it from running now step back a little bit for looking at into code injection in a bit more general fashion like I said we need to first copy the shell code into some address we know of in the target process and we need to make sure it's in executable region of memory so either copy to excuse memory up front or we need to migrate it into it using some method and eventually we need to trigger execution so at this stage we have the

trigger which is the TV trivial shortened message you still need to find a way to copy or shellcode into executable memory in the process which is what we're going to show now okay so this is a heart of lower view of what we did so first we used an added box in the target process and sent to it WM set text message in order to copy our shell code into the process now we used the unicode flavor of send message and that way we were actually able to inject shell code that does contain us but not double notes because that's the null Terminator for Unicode next we need to find the address of the shellcode so in

the original schedule attacks it was they assumed that dresses is static and they simply hard-coded it hard-coded it but we wanted to do it dynamically and it does at this stage we actually leverage the fact that we have read access to the target process and so we simply scanned we open the target process for reading scanned its memory using visual query ax to get our memory region at a time until we found the pattern until pattern matched the shellcode we copied into edit box so now we had the dress over shellcode and target process memory next we need to migrate the shellcode to some little memory so what we did we used the same memory scanning technique to find the

region of memory which is read write execute we assumed it does exist internal process we'll talk about it in a moment and once we found it we used a status bar to actually migrate the shellcode so what's very nice about the status bar is opposed to dirty box is that actually it has set text and get text messages and argument they receive is not serialized but instead treated as a pointer to data in the target process so you could use set text to copy the shellcode from red box buffer into the status bar and then we could use get text to actually copy the shellcode from the status bar to our target executable address so now we had our shellcode an

executable memory and we could use the treeview in order to execute it so that's the way it would work in on a high level now how did we actually used it so luckily for us Windows Explorer on win7 has all of these controls that we need and what's more important Windows Explorer in the context of this sandbox was actually treated as a trust process so that means that if you are able to inject code into Explorer we win so the winners Explorer so it has an added box right merchants which serves the search bar so it's not strictly an added box like it's it has a different windows class but it does support WM text WM set text with the same semantics

which which is what is what's important for us so it has also a tree view right here and it also has a status bar so it it status bar is actually hidden you cannot see it but it's there and it's useful nevertheless and also what was very important and even crucial was this Explorer on win7 also contains a read/write/execute full region of memory without doing anything special so just a vanilla Windows 7 will have our double disk memory in Windows Explorer and now let's go over the attack sequence in a bit more detail so we have a shell code without double nulls we find the following UI elements in our target process which is explorer.exe we find an

ID box a treeview and a status bar then we also find an RW eeks disputable memory region inside Explorer by simply iterating over all the memory regions and checking their protection sites so now we have this target address that we define as being in the slag space like in the end of the memory region which is all nulls next we allocate Activa sorts be struct and initialize it we allocate the payload buffer which contains both or shellcode and under struct and we copy this shellcode into the edit box using WM set text the Unicode flavor next we scan the target process to find this payload in this dress then we define the the address of the TV source destruct has

been this initial dress plastered on line through the shellcode which brings us here next we migrate the shellcode executable memory so as I said earlier we're using the status bar for this so we're taking we're sending a set text w message to copy the buffer from this initial pair of the dress which is null terminated into the status bar and we use get text W to copy it to target address which is now in executable memory and then finally we have everything in place and we send the TVM soldier drewcb message with the address of our struct over here and it works we have code execution now there are two small pitfalls we need to overcome to actually

veteran eyes it didn't be able to use it robustly the first one is that actually the compare function which compares pairs of elements in the tree view while sorting can be called multiple times which is probably not something we really want we don't really want her shell code to run multiple times so to overcome this on 64 bits we simply add this bit of assembly which repend it to a shell code which basically overrides the beginning of itself with the right instruction it returns immediately the second pitfall so what so what that means actually that after the first time it runs the next time the compare function is invoked it doesn't do anything it doesn't run or shell code

instead it simply returns which is what we want now the second pitfall is that actually in order to have all the controls on the windows controls we need we need a file explorer window to be open which is not always the case like there are folks like me using total commander so they will not have file explorer windows open at any time so top comte's we actually found out that you can use shall execute and pass it an SW hide flag so that we can upload file explorer without the users seen anything and then we open it and check the shellcode run it and close the window afterwards so this is it and then it worked it worked

we were able to break the sandbox were very happy now on to the second use case second thing we thought we could do with this technique is to actually perform code injection into Explorer that EDRs will not attack because they're probably not familiar with this technique and doesn't do anything suspicious so if thought it it might fly so and as well know the challenge with EDRs if we're talking about college action is that they're actually very sensitive to processes open open other processes and write into their memory etc so I wanted to implement college action without this and of course our attacks came to help once more the Ross however an extra challenge since we wanted to do this on Windows 10

this time and the issue now was that there is actually no read write acts with memory in Windows Explorer on Windows 10 which is what you would expect and now it is the case but we still had a few ideas so if you remember we use the TVM so trilled rune CB which receives a call back to compare function and then we decided to look into it in a bit more detail so this is the struct it has the parent which is subtree we want to sort it has the compare function which is or callback and this is the signature of the compare func so it receives three arguments the last one is actually the same l prom from the team

source be strapped so we can control it easily the other two are the l / am i an L prom fields of the tree view items been compared so we could actually control them as well because we are able to inject data into the process so we can inject a TV item struct into the target process and then we can we can send the set TV item message so since we can control the L params of the tree view we can actually control these two arguments so it might be difficult to control their order because it's difficult to say what will be the order of the comparisons that this sorting algorithm does but as we see in next slide doesn't really

matter so the bottom line is we can call some function and any function we want and pass it three arguments we control or almost control and then we started looking for function that we can call this might help us gain some executable memory to use so the first first one we thought of was of course we showed a log so virtual lock looks good the first three arguments we can control the problem is the last one which is the most important one which contains the protection flags is actually out of a reach that's the first argument so we were said the runner-up was we shall protect so this looked a lot more promising because actually the first

three arguments are the important ones the first to identify the memory region it's offset in size address in size the third one contains the protection slag this is good and the last one like it's simply an out forum to receive the old protection slugs so we thought ok we'll person out there or hope just an out will be passed by itself and then it would work but unfortunately Microsoft didn't really want anyone to do that as the documentation clearly says that if you pass an all or and involve the dress the function will fail so we were said once more now there are a bunch of other ritual alok or visual protect like functions a lot more than I expected

and they're all they'll have even more arguments so even worse than the first two but then one of my team members run he had a bright idea he said okay there's a function called hip created that creates a new heap and actually its first argument has some optional optional slacks options and one of them is hip create enable execute which basically allows the user to create an executable heap so I guess it might be useful like for just-in-time compiler stuff like that and of course it was very useful to us and since hip create receives free arguments we were very happy this time so what we did we actually set all the L per on fields of all the elements in the

tree view with the value of heap create enable execute so as you remember that no mind as a remember the compare function the first two arguments are the L params of the tree items being compared so we said if we cannot really control the order but we can simply pass this same value in both of them into work and then basically were able to call heap create using the TVM so treason CB trick and we were able to create an executable heap which means we now had read write execute memory and we're back to the same scenarios on Windows 7 however it was a nozzle another small challenge so it turned out that actually the status bar it does

some filtering basically what it does it filters the white character counterparts of the non-printable small ASCII characters which means that for each white character of the form B which is some byte and 0 0 if B is between 1 and 1 F except for 9 which is a tab character then basically transforms it into 2000 which is a space character it does some sensitization so when we only wanted to as in the context of the sandbox when you only wanted to copy a shellcode it was fine because you know you can craft a shellcode without special characters of your choosing but in this case it was a problem and another problem was that we also had some double nos or even four

consecutive nails that were that we needed to pass around this part of the struct for setting the helper arms in the tree view etc so what we did in order to overcome this is to actually come up with a very careful procedure for copying data into the process through the Edit box in status bar so more concretely in order to overcome multiple consecutive nulls we actually found when we looked for an executable memory region and we made sure that we find the region which contains which is all zeros which was very easy because we used to create to contain it so the heap was zeroed first and then if we needed to copy a bunch of

nulls would simply escape to the offset after in the else don't you need to copy them they're already there now in order to overcome the filtering by the status bar we did the following so let's assume this is the source buffer want to copy so AABB can be copied nicely but 0 5 0 0 will be sanitized to 2000 which is bad and thence this is also fun and etc so we would first copy a IBB to offset 0 in target which works but then instead of copying in 0 5 0 0 which would be filtered we actually copy vb 0 5 again to offset 1 and it would look like this so we override the BB character again

but doesn't really it's there's no problem with that so we now have 0 5 and then we jump to copying 0 0 CC 2 of the 3 so eventually we have the entire source buffer copied so basically in order to copy a filtered white character of the form b00 first copy B with the previous one and then we copied 0-0 with the next one and basically that was 8 now it worked so we were able to set up all the memory and target process in order to invoke hit create with the correct parameters and then once we called it we had our W keys memory and then it was back to the win 7 scenario

that we already talked about and we were able to inject code into Explorer now just to give you a glimpse of how it looked like when the EDR was looking at our at us doing this so this is really our so just to create a baseline for the for this test we did a good old create remote thread load library just to see that the alert is triggered so it shouted you know alert severity and inject inject code and now nap etc and those are the low-level events and this is the high level alert the application injector injected code into system process as we would expect from any self-respecting EDR then we used our injector which is shatter eggs over here

so there are no alerts so we don't show it now there are a few events here but there isn't for this mainly is because we implemented the proof-of-concept in Python used by to exit to package it so the first all but the last event here actually caused by PI to eggs and doing stuff and last one is actually not in process so if you remember we did use open process to open the target process Explorer for reading so this is dr did complain about this but didn't sound alert so we consider it to success yeah okay so we're almost done so let's do a quick recap so we took a very old security-related design issue in Windows that wasn't

really fixed so fixes in Vista please contain it but the root cause for this for the shatter attack wasn't really addressed so in the root cause is the fact that pointers been passed in Windows messages are a giant mess so you have pointers for some messages that are actually treated as strings and they're serialized and copied to target process you have pointers to strings that are simply treated as pointer to data in the remote process which helped us migrate the shellcode from one place to another and sometimes which is the most dangerous thing you have pointers you have a sudden you have arguments to Windows messages which are actually passed around and they're treated as

pointers to callback callbacks in the target process that are simply executed and you can give any address you want and to get executed so the combination of them gave us all the primitives we needed to implement code injection so we could allocate read write execute memory using hit create you couldn't write to an arbitrary address we can mine we can copy code around from place to place and we couldn't execute a specific point where we wanted which is more than you actually need and now in terms of possible implications for this sub the most trivial one would be to say okay let's hook send message and and block the dangerous Windows messages or sanitize the callbacks or some like that

the problem is that use remote hooks like we said and not really could not really treat it as a security mechanism because they can be disabled but luckily for us Windows actually does provide a very robust method for filtering Windows messages which is the set windows houki acts api with the appropriate wh types I wouldn't implemented but it should work so I think that that would be like the best mitigation for this and now let's see a demo I hope it works okay so you have your VM we're going to run shatter run from Explorer which should run someone explorer now that the sample shell code you'll see now basically all it does is pop up a

message box just so that you can show and demonstrate this message box is actually opened from Windows Explorer so let's run it hope for the best because so it says that of course didn't work let's try this again okay not work so it says that no read/write/execute memory was found so it created it it's actually sound man I feel it I'm a dog the message box is here the Explorer does so if you take spy plus plus yeah fine some people in below that and go to the process spies a bit slow and it's empty no nice ocean yacht I do need some help alright so you see this this is open from Explorer so what

happens now is actually since our code and send message with a certain message actually ways for the code for the shell code we call because the Explorer is currently trying to sort it in this stuck in the callback so good okay finishes you're all good obviously we can put any shell code we want there the shell code would probably want to use it for real probably often a new thread and then execute in it so it doesn't block anything but you get the idea okay look here we're actually done so thank you very much this is my email you can scan this QR code it also contains my email so if you have any questions suggestions remark I'd be

very glad to hear them send me a line thank you very much