 Good morning DEF CON! How's everyone doing? Right So hope you got a good night's sleep because today We'll embark on a journey, a journey into the realm of ring hopper But first we'll tell you the story of how we almost zero-dade the world So usually a timeline is presented towards the end of the talk, but we'll make an exception It all started on June 1st, 2021 when we initially reported the findings we'll be talking about today on January 19th 2022 Third CC started working on the coordinated disclosure. We'll soon see why this disclosure had to be coordinated By the way, I'd like to use the stage to send a big shout out to VGI and the folks for mastering the challenging disclosure Right about now a year ago. We should have presented our findings in the blackhead and DEF CON conferences But unfortunately we were notified that the issues will not be fixed in time So we had to withdraw our talks, but we did it rather close to the Conferences themselves. So our abstract was still printed in the booklet in DEF CON's booklet Which might have caused some confusion By the way, from the bottom of our hearts, we appreciate the concern Fast-forward to November 8th, 2022 when the last embargo set by Third CC has expired And a note was published which cleared pretty much everything up And now that we're out of the old-day danger zone where we couldn't talk about our findings. Let's dive in So at the beginning there were four privilege rings The most notable are the user mode user land user space ring three it goes by many names and the kernel The lower the number of the ring the more privileges it has and it works in a manner that is a pack to all the rings above it For instance The user mode has no notion whatsoever of what's going on the kernel Over the years new additional rings were introduced for instance the VMM ring minus one is usually where your hypervisor runs and SMM ring minus two. So let's talk about SMM. It started It stands for system management mode and it started as a small and humble processor operating mode That's mostly provided low-level system functionalities such as power management System hardware control and OEMs ran their proprietary device specific code there Over the years the SMM got bigger and bigger and turned it to a huge monstrosity that performed all kind of operations For instance it exposed and in exposes an interface to the SM BIOS Does PS2 mouse keyboard emulation handles USB events and boot and runtime and the list is long Together with the SMM grew the attack surface and it has become so much more interesting for the attacker But this is fine because numerous Registers and MSRs along with some pretty awesome mitigations were created with the sole purpose to keep SMM safe So in order to switch to SMM The kernel has to trigger a special kind of interrupt called system management interrupt or SMI for short Once an SMI is triggered the processor switches to running SMM and the kernel is no longer active And just like with any other interrupt there is an interrupt handler that corresponds to the interrupt number that was triggered and In our case it's aptly named the SMI handler once the SMI SMI handler is done running The processor switches back to running the kernel and the SMM is no longer active SMM resides in a special well guarded Polish portion of the of the RAM called SM RAM system management Tram while all the other entities have no access to the SM RAM whatsoever For instance if the kernel tries to read a bunch tries to read from the SM RAM. It'll get a bunch of a fifths Overall looks like a worthy target target, right? so in our research we Started with an agent running in user mode Escalated to the kernel and from there we hopped to the SMM and We did all that on this Intel knock But as we'll see later on our research goes way beyond this recently discontinued little fella So this is a good time to dive into our findings But before we do so we need to understand what is a time-of-check time of use vulnerability in SMM context So let's say we have an SMI handler that provides the awesome service of Overriding a buffer with the with the value of coffee Since we are SMM we do not trust the operating system so the first thing we have to do is to validate the input and Basically make sure that the output buffer points to a valid location. This will be the time-of-check Then once it was validated we can use it and override its content with the well with the value of coffee This will be the time of use if an attacker can modify this output Buffered the pointer to lead to another location in between the time of check and the time of use It will be basically gain a right primitive of the value coffee So let's see how it works in SMM context So in the normal scenario, we have an input buffer which would lead to an OS owned memory and The SMI handler we simply override the content of it with the value of coffee However, an attacker may try to modify this output buffer to lead to the SMRAM in Between the time of check and time of use and by doing so gain a right primitive of the value coffee to the SMRAM By the way, this is exactly what a validation function does It makes sure that the output buffer is not located inside the SMRAM So let's discuss how we exploit such an issue In a classic time of check time of use exploitation, it will work as follows Let's say we have a system with four different cores and let's assume that the first core does the critical SMI handling It does the validation and usage steps. So between the other cores We will try to corrupt the data Sometimes we may do this before the validation and if so, it will simply will not pass the validation phase Sometimes after the usage and in such scenario, nothing would happen But eventually with some luck we may corrupt the data exactly in between the validation and usage and exploit this issue However, when an SMI triggered all the other cores are going idle and we cannot use them to corrupt the data So how did we do that? The answer was DMA. DMA stands for the direct memory access and this is the way for your peripheral devices to talk with the CPU the operating system and In in precisely those those devices could be your Ethernet adapter or your graphics card and etc So to test this hypothesis we took something that is called PCI-LH It's a cool tool by Ulfresk. This is the small red FPGA device that you see here, which is connected through the M2 bus and Basically, it allows you to initiate arbitrary read and write DMA transactions to the RAM and using it we were actually able to exploit this time check time fuse issue, which was amazing But as some of you may say, it's not good enough. It requires physical access, right? And we it could be considered only as an Evil-made attack or supply chain attack at best and we thought so as well and we wanted to make things even more powerful We wanted to operate them remotely so to do that we utilize something that exists on any platform utilize the storage device and We did it using a cool technique by Rafale that was presented a couple of years ago Which allows you to generate DMA transactions using a storage device So let's see how it works now Remember the previous diagram? Now we had another component into the equation We had your storage device and on top of it we place some malicious file which contains our payload or basically the modified address so Once again We will start by generating a DMA request due to the asynchronous nature of the storage device It will take it some time to process the request Then we will also hook this request such that it will be redirected back to the input buffer and We will trigger an SMI Once we trigger an SMI all the cores are going idle Remember then eventually the SMI handler will enter the critical validation and usage steps And with some luck the data or the file will be returned exactly in between the validation and usage and Will allow us to exploit this time of check time of use issue Okay, so we talked a lot and it's probably a good time for a recap So we discussed what is SMM and how we can communicate with it We also discussed how we can turn those time of check time of use issues into right primitive study SMR and We also discussed how to manipulate DMA transactions to exploit exactly those time of check time of use issues and We also discussed how to execute code inside SMM. Oh well not exactly So yeah, we don't currently have the ability to execute code in SMM But we have something pretty powerful. We have a right primitive to the SM run So let's put that to some good use one of the modules we analyzed was the SM bios DMI edit Dixie driver And our analysis was not in vain. It bore fruits fruits in the image of potential right primitives It's worth noting that this list is not exhaustive and it's far from being the only Vulnerable module However with a bunch of right primitives in a classic scenario you would probably try to find an executable memory region, right and We thought that it would apply to SMM as well but as we discovered SMM has page paging mechanism of its own where all pages containing code are read only and all pages containing data are Non-executable then you would probably try to forge some arbitrary payload out of your right primitives and Even though we have quite a few of them their variety and number does not let us craft anything decent and Lastly you would strive to get unrestricted memory access and Here once again the paging mechanism In the SMM hinders our exploitation so for starters Most of the non SMRAM RAM is simply not mapped to the page table and if that's not annoying enough and it's annoying The page table resides in read-only pages meaning no new pages can be allocated and all attributes are permanent Meaning that if we do manage to execute code in SMM It would be pretty hard not impossible, but pretty hard to do something for instance to the to the OS So as you all probably agree, I hope by now The classic approach might not be very applicable here So let's try to think out of the box and leverage some mechanisms that are unique and internal to SMM so at this point we Jonathan and I Turned to reading some works from the past and one of the works we we found was by Raphael Washtuk and Corey Coneberg from 10 2015 where they write By forcing at three suspend resume cycle and the Tucker can run an arbitrary code and take control over SMM Sounds promising, right? But what's S3 suspend resume and what is S3? So well S3 is a sleep state where the CPU is idle, but the memory keeps on working and Simply put when you put your machine to sleep. It usually enters S3 Let's continue with some more background So a normal boot process is rather time consuming But when we come back from S3 some of this process can be skipped because well the machine has already booted once however, some configuration still needs to be restored and Here the S3 boot script comes to our aid During the normal boot the configuration of the platform is encoded into the S3 boot script And then when we the machine wakes up There is the the code boot and the code boot engine simply executes the The script and restores the configuration and this boot script consists of rather primitive Opcodes like reading writing to IO to memory to PCI, etc. And here's the interesting bit Previously it was stored in a location that it was accessible to the OS Which is exactly the fact that the That the paper with earlier abuses they modified the boot script from the kernel put the machine to sleep Woke it up at this point their malicious S3 boot script was executed and eventually they ran code in SMM So as you could probably guess this This paper was from 2015. So it's already mitigated and nowadays the S3 boot script resides in a container aptly named a logbox and the logbox is a data structure that provides integrity of the data It is located in the SMRAM has a pretty standard Implementation and there is a rather convenient API for reading writing and updating it from ring zero So let's have a look at the update procedure so here we see the function that updates the logbox and It either returns access denied error or some internal status The only way we do not get access denied and well We don't want to get access denied is if the M locked global variable is not set which is kind of nice way of Of me of preventing the kernel from updating the logbox and This global variable Unconveniently resides in the SMRAM, but hey, we have a right primitive there, right? so let's Let's utilize all the information we got and try to execute unauthorized code in XMM So the plan is as follows we use a right primitive to zero out the M locked global variable Then we invoke the update procedure of the logbox and by that Modify the s3 boot script with our malicious version of it We put the machine to sleep wake it up and At this point the s3 boot script will be executed and will be eventually running code in SMM So it's a great idea, but it doesn't it doesn't really work because Right after the machine wakes up and just before the s3 boot script is executed The code will close and lock SMRAM and only then jump to the boot script meaning that if Even though we managed to execute code a rather primitive rather privileged code At a rather early stages of s3 resume We the SMRAM is locked and most of the RAM is simply not mapped to the page table Yeah, we should have read the documentation all right, so our quest continues and let's take another attempt and Let's Take another attempt where we emerge victorious, but as usual some background So each core has its own region of the SMRAM Pointed to by the SM base MSR model specific register each core has an MSR of its own Each each of these regions contains a lot of interesting things, but we'll be focusing on two The SMI handler entry point which is the first code that will be executed upon entering SMM and the safe state Which contains among other things the value of the SM base MSR and As a matter of fact the proper way to modify the SM base MSR is by changing its value in the safe state and This whole operation is pretty special. How special is it? You might wonder it's so special that it has a name SM base relocation from a developer's point of view SM base relocation is pretty straightforward They modify the value of the SM base value in the safe state and some magic behind the scene relocates it or relocates the whole region to the new to the new address Which leads to the following attack idea? So let's allocate some user controlled memory that is accessible to both kernel and SMM There will craft our own SMI handler entry point the first code that will be executed upon entering SMM and Now we'll simply use our right primitive to modify the SM base value in the safe state this will trigger an SM base relocation and Eventually we'll execute code in SMM, right? Yeah, it's a great idea, but it doesn't work It doesn't work at least out of the box and it doesn't work. Thanks to these MSR and in particular the SMM code check enable bit So when this bit is set and the machine is running in SMM If someone tries to execute code outside of SMR, it will simply trigger an exception You can think of it as some sort of a SMAP mechanism But that's not all there is a log bit here that if said this MSR cannot be modified or Changed in any way until the next reboot and while rebooting the machine or cutting power to the CPU Kind of defeats the purpose. So at this point Jonathan and I Said and thought what could be the closest thing to rebooting the machine without rebooting the machine? And then it hit us. We'll just put the machine to sleep and in particular to s3 sleep Recall that the s3 sleep state is a process a state where the CPU is idle, but the memory keeps on working and As the CPU loses power all the entries stored on the CPU are cleared when going into s3 So if you care about all these entrants for instance the MSR we just saw All the data should be copied to the To the memory prior to entering s3 and after the machine wakes up It should be restored to the CPU So let's have a look at our beloved MSM SMM code check enable bit In normal execution it is set When going into s3 it is still set when the machine wakes up and we come back from s3 as the CPU lost its power It is cleared and at the very early stage of the boot process There is some initialization code that initializes This bit. So let's have a look at the initialization code So clearly we we see the right MSR over here With a certain value you'd have to believe us that it comes from the memory And here's the interesting bit This right MSR happens only if the condition in the if statement holds meaning the MSMM feature control supported global variable should not be zero and Remember we have a right primitive to the SMM to the place where this global variable is stored This leads to the full recipe of unauthorized code execution in SMM from kernel So first we use our right primitive to annul the MSMM feature control supported global variable Then we put the machine to sleep Wake it up at this point the function you saw earlier is executed the condition in the if statement does not hold and The SMAP like mechanism is not enabled hooray Now we create our own SMI handler entry point the first code that will be executed upon entering SMM And we use a right primitive once again to modify the value of the SM base MSR in the safe state And thus we trigger an SM base relocation and We trigger an SMI and drum rolls, please We have code execution SMM All right great success So frankly and humbly speaking this method is pretty awesome But it gets even better So the SMI handler entry point starts running in real mode meaning that the page tables Enforcement have not kicked in yet because well, there's no paging and as we crafted our own SMI handler entry point We have full and unrestricted memory access to all of the RAM So for instance, we do not mind the non executable or read only pages or The fact that the paging mechanism is static or the read only Or the fact that the page table resides in read only pages or the non executable stack or any of these mitigations This is pretty awesome however, your mitigations do hinder our exploitation in particular the s3 and SM base log bits when these bits are set The as the CR3 register and the SM base MSR cannot be modified until next reboot So while those mitigations are not fully adopted yet in all platforms we wanted to deal with them as well and To do that we had no lead at this moment. So it decided to dig dig up once again through the pages of history and We stumbled upon this cache poisoning attacks that was presented in 2009 by Rafaela and Joanna and using the cache poisoning click link you're basically are able to arbitrarily read and write to and from the SMR So let's see how this mechanism works. So When we try to write to the SMR from the kernel, we will stumble upon a barrier which Basically makes sense, right? We are not allowed to write to the SMR. So the memory transactions will be discarded However, this was a little bit misleading in practice when we try to write to the SMR We'll go through the cache first and the data will be stored there first and only then it will be written back to the SMR so what if before we before the data is written back to the SMR we will trigger an SMI and CPU will enter SMM it's SMM state the barrier will be removed And with some luck only then the read the write back will occur and will modify the content of the SMR So this would work, but Obviously, this is already mitigated and this is due to a special web MSR That is called SMRR the system management range register and it has a simple thing It basically disables the caching of the SMR But we already know how to disable MSRs, right? so let's see at the code that restores it from the sleep state and this is probably looks familiar and This is why so this is the line of code that actually restores the MSR and Once again, it is dependent upon a global So what if we are now this global set it to zero using one of our write primitives We'll put the machine into sleep. We'll wake it up And when we wake up, we will be able to write to the SMR directly by Resurrecting from the dead the cash poisoning attack. So once again success and we can deal with even those newer mitigations So this is a great time for a demo and Let's hope it will work Okay Perfect. So before we begin, please notice that Intel NUC logo this logo Okay This logo is stored on the SPI flash along with the UEFI firmware So if you are able to execute code inside SMM We will be able to modify this logo The reason for that is because only SMM is accessible to the SPI flash It is the only ones that is able to write to it So now it's time to elevate our privileges We'll begin by crafting a new write primitive of the value of zero Remember, we had a rather complex write primitives and none of them was an actual zero So by changing a couple of them and modifying a couple of globals inside the SMR we were able to craft the value of zero and This will help up in the in the next step of our attacks so now we will try to anal that MSMM feature controls supported global which is responsible for the MSR restoration and If you are actually able to disabled when we return from sleep state the MSR will be annulled so now That we have annulled this global We will read the MSR before we go to sleep state We will see that the value of the MSR is five Which stands for two enabled bits both the enable bit and the lock bit and we'll put the machine into sleep So at this moment we count on the user to return from the sleep I guess no one was ever concerned that is being SMM mega-bridged when his machine suddenly went to sleep and Now that he is back. We will leave the MSR once again and it is zero so now we are able to execute code outside the SMR but How we will do it using the SM base relocation attack so at this point we'll execute the SM base relocation attack and Execute our own shell code it's out the outside the SMR so what the shell code actually does it simply replaces one of the SMI handlers and Now we are only left with triggering it so Now that we have triggered it We will reboot the machine and see the result of what our SMI handler actually did and Voila, we managed to change the logo So to better understand the implications of the issues we have found we need to understand the UEFI ecosystem first So everything begins with the TianoCore project or what some of you may refer to as the EDK2 or Practically the source code for any of your modern UEFI fingers Then comes the IBVs the independent BIOS vendors. They take this source code EDK2 and Practically add the major portion of functionality. They add all the different SMI handlers along the way Then they deliver it to the OEMs to the original equipment manufacturers such as Acer, Asus, Dell, Gigabyte, HP, Intel in the case of the Intel NUX, Lenovo and MSI Then those OEMs deliver it to you guys the end the customers So in the attempt to understand how many devices are vulnerable to ring hopper We found out that only in 2020 they were manufactured more than 200 million devices that are vulnerable to our attack Which is absolutely crazy and probably results in a couple of billions devices that are vulnerable and Obviously means tons of CVs So if things were not bad enough, we want to make things even worse up until now we Attack the machine from kernel to SMM aka ring minus two But we want to make our attack work work from ring three as well from user space and to do that We require three different things. We need to be able to generate DMA transactions We need to be able to trigger SMIs and we need to be able to write a specific physical memory So let's begin by generating DMA transactions So remember the previous diagram where we hooked inside a kernel the DMA response and we made some very complicated DMA generate very complicated DMA and request So figure that it is way way simpler in order to generate DMA request You only have to read the file the data will be returned to us with DMA directly and to make sure that it is Being redirected to the input buffer. We do not need a hook. We simply read it into the input buffer So this was it and how we generated DMA transactions from user space It was rather simple, but triggering SMIs is way complicated And this is because we need to execute a special opcode out B2 that can be executed only from kernel and At this point we got very frustrated and like every frustrated millennial we went on a social media and In specific the ones that if you trip or its name by way free it may free you'll probably get the wrong results So anyway and We stumbled upon one of Alex Matrosov tweets which says that practically Any bias update tools that you'll find there out there probably has some low-level functionality that can be reused for offensive purposes and indeed there was So we am I provides to driver a Linux driver and assigned Windows driver and both of them Exposed an API for triggering any arbitrary SMIs that we desire basically a yachtal So Now that we have done with triggering SMIs we are only left with writing to specific physical memory and I remind you we wanted to make it work both from Windows and Linux So let's start with Windows So the communication with SMM is a little bit more complicated it has a special structure that is called a mailbox which has a Description of all the things related with the communication with SMM and the one address it contains Physical address of a row buffer, which would actually contain the input So in order to access it from SMM we require a physical address Which is basically the virtual address within SMM context and to access it from user space We require a virtual address that maps exactly this physical memory region So let's look at the buffer mapping yachtal inside one of the Windows drivers So it begins by triggering SMID 9 which basically populates the mailbox and along the way the the raw pointer to the buffer and Then in order to access this mailbox It needs to map this physical address So the next thing that it does it basically map this physical address into an accessible virtual address Then at offset 8 it access the pointer to this physical buffer the row buffer and once again map it into a user accessible virtual address and Then returns everything into a user accessible memory. So the user will be able to access those virtual addresses So some of you may notice there is a race condition here in between the time of that the SMI is being triggered and the mailbox is being populated with the Pointer to the to this row buffer and the time that is being mapped to user virtual address So what if we change this physical address in between the time that the mailbox is being populated and the times that this Physical address is being mapped will be AC basically will be able to map any arbitrary physical address that we desire So let's see how it works. We will trigger the buffer mapping yachtal twice on the first attempt We will get an access to the mailbox virtual address So we will be able to modify the mailbox at offset 8 with our own desired physical address And then we will modify the mailbox to exploit this race condition and basically Change this address with our own arbitrary physical address and we will get the corresponding virtual address So this looks great But reality makes things even harder in order to avoid the remapping of the same physical Addresses over and over again. Those globals are being checked which basically makes sense, right? We do not want to map the same address over and over again So at this point we decided to look at the cleanup yachtal and We noticed that the cleanup yachtal only does it is to annul those globals and I will repeat it It only annul the globals. There is no end mapping of this virtual address once or ever So it is still accessible to user space So we will use this fact and we'll make the following exploitation So we'll trigger the buffer mapping yachtal to get a mailbox virtual address. We will trigger the cleanup yachtal to annul the globals We will exploit the file and then we'll exploit the following race condition From two course we will do as follows on the first score We will trigger the buffing yet mapping yachtal once again to populate a mailbox and to map the physical address inside it From within the other course We will use our previous virtual address of the mailbox to modify with our own to modify the top state with our own desired physical address Then we'll check if the newly mapped physical address is the ones that we wanted If not, we will simply repeat the entire process We'll trigger the cleanup yachtal repeat the entire process once again until we succeed so you folks are pretty tired by now, I guess we presented a lot of info but Let's but do you have a moment to talk about Linux? Am I does some pretty let's us do some pretty amazing things from user space Here we open the Linux module at the kernel module, sorry and trigger the M MAP syscall with the Yeah, with the kernel modules file descriptor to map any physical address Which is pretty awesome at this point. We'll have to somehow be able to to generate DMA Transactions for our exploit to work. So just as we did on Windows will simply open a file Read it to the previously mapped memory and hope for the best from the Linux modules side they They initialize the M MAP attribute of the file operations Struck with their own with their own function, which does which actually does the Virtual to physical to virtual mapping using the remap pfn range, which is pretty straightforward It gets a physical address and returns a user accessible virtual address But there are some bad news When we execute this code we get an e-fold and the reason for that is In the way remap pfn range works when running this function Page ranges are managed without struct page. Just pure pfn simply put This will not work. No DMA transactions will be generated here However, we have the ability to map any physical address Any physical address So let's take the code from before and instead of mapping some arbitrary physical address Let's map for instance the address of the Linux kernel or a kernel kernel module At this point that we have the user accessible mapping We will be able to patch ring zero code and gain code execution in kernel and Which is pretty awesome, but it sounds kind of familiar right do we remember how we generated DMA transactions from kernel? So now that we have all three bullets Working from ring three. We have a full exploiter exploitation chain from userland to SMM Which concludes our journey? We first Got the ability to write to the SMRAM then we try to leverage it into code execution in SMM using the S3 boot script and we failed spectacularly and We successfully ran unauthorized code in SMM using and abusing the SM base relocation technique And lastly we elevated the attack to work from userland and thus we managed to hop From user space to SMM We were Jonathan and Benny. Thank you so much for taking part in this journey