Exploiting Interfaces of SEV-ES-protected Virtual Machines
CC Attribution 2.0 Belgium:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Radev, Martin
Supported since Linux 5.10, the AMD SEV Encrypted State (SEV-ES) feature can be used to protect the confidentiality of a virtual machine (VM) by means of encryption and attestation. Although the memory and registers of the VM are encrypted, the VM still communicates with the hypervisor for the emulation of special instructions and devices. Because these operations have not been previously considered part of the attack surface, we discovered that a malicious hypervisor can provide semantically incorrect information in order to bypass SEV-ES. In this talk, I provide technical details on the handling of special operations with SEV-ES, practically show how the original implementation could be exploited, and finally I show how the interfaces were hardened to fix the issues. This talk includes four different attacks which: 1) use virtual devices to extract encryption keys and secret data from a virtual machine. 2) reduce the entropy of probabilistic kernel defenses in the VM by carefully manipulating the results of the CPUID and RDTSC instructions. 3) extract secret data or inject code by forging fake MMIO regions over the VM’s address space. 4) trick the VM to decrypt its stack and use Return Oriented Programming to execute arbitrary code inside the VM.
 Welcome to the talk Exploiting Interfaces of SCVES Protected Virtual Machines. This work was already presented at the ROOTS conference and was co-authored with Matthias Moritzer. The work was done at the Fraunhofer Institute for Applied Integrated Security. The focus of this talk is a recently available feature called AMD SCVES, which provides sound protection for a virtual machine against a malicious hypervisor. However, the virtual machine may still run a general-purpose operating system, for example one based on Linux, which has previously considered the hypervisor as trusted. This can potentially lead to security issues because the virtual machine communicates with virtual devices, which are controlled by the hypervisor. And also the virtual machine requires emulation of few special instructions done by KVM. In this talk, I'm going to show three different attacks for SCVES virtual machines and also how their fixes are done in the Linux kernel. So let's just quickly go through how the virtual machine is protected. So first, there is memory encryption for the virtual machine. While this includes the virtual machine's firmware, bootloader, the kernel and user space applications, however, because the virtual machine communicates with the outside world, for example, the DMA region is not encrypted. The virtual machine selects which memory is encrypted or not by setting a spatial bit, the CBIT, in its private guest page table. Additionally, with SCVES encrypted state, additionally, the architectural state for the virtual machine is encrypted. This includes architectural registers and other state information. And also SCVES is now supported in Linux 5.10 officially. Because the registers for the virtual machine are encrypted, the emulation of instructions becomes more complicated. For example, when the virtual machine executes CPU ID, this will raise a special exception, a VC exception, shown in one, which would invoke the virtual machine's VC handler. The VC handler can then write the necessary information in the GHCB page, which is unencrypted and shared with the hypervisor, and then request emulation from KVM. KVM can read the information from the GHCB page, emulate the instruction, write the new information to the GHCB page, and resume the guest. And then the VC handler can read the new registers from the GHCB page, validate them, and update the architectural state, and execute IRED. However, because emulation happens in KVM, the emulation of this instruction should not be trusted. The threat model, I'm assuming, I know of these attacks is that the attacker controls the host before the virtual machine is launched, and the virtual machine uses a CVS for protection. Additionally, the virtual machine initial state is measured and attested, and also the attacker has access to the virtual machine's kernel image for static analysis. The first attack is based on the fact that the hypervisor may be able to manipulate the sources of entropy for the virtual machine. The Linux kernel inside the virtual machine relies on the source of randomness to generate random values for different probabilistic defenses, for example, kernel address space layout randomization, and also for stack canaries. Here in this talk, I'm just going to focus on KSR. This is a feature which randomizes different regions for the Linux kernel, and it happens during boot. The sources of randomness include the RDR and RDTSC instructions, also the kernel build string, and also the boot parameters structure, mostly initialized by information from QM. The function which determines the random values is called KSR getRandomLong, and here's how it looks like. Initially, the function generates a initial random state using the getBootSeat function, which returns a hash of the build string and the build parameter and the boot parameters. However, because the attacker has access to the busy image, the build string is known to the hypervisor. Additionally, also the state of the boot param structure is known. Afterwards, this code checks whether the RDR instruction is supported, and if that's the case, it samples the random value. However, because this feature is checked using the CPU ID instruction, the hypervisor can say that this feature is not supported, so this code here would be eliminated. Afterwards, the virtual machine checks whether the RDR instruction is available, then samples the timestamp count and mixes this into the random value. However, the hypervisor is always able to control the return value of the RDS instruction, because the virtual machine may need to be moved to another CPU where the timestamp count is different, so it's necessary to be able to modify it. One way to do that is by marking the RDS instruction as an emulated instruction, and this would allow to specify any value. However, with ACVES, this would cause a special exception, which would involve the VC100, and it can be easily detected. The second option, which is much sneakier, is to use a special model-specific register called TSE ratio, and also the field TSE offset in the virtual machine control block structure. The RT-TSC instruction would use these values in the following way. The ratio MSR would be used as a scale, and then the TSE offset would be used as an offset. If the hypervisor sets both to zero, then the RT-TSC would just return zero. I implemented the proof-of-concept exploit for an ACVES virtual machine, where I tried to manipulate the case-award regions and also the Linux entropy pool. First of all, we can't actually set the RT-TSC instruction to return zero for the entirety of the time, because that instruction may be used for various different things in the kernel, and you will get a hang if you do so. So here in my exploit, I rely on page tracking in order to figure out the moment of time, the period of time for which the case-award regions are being randomized, and for all of these places, I can specify RT-TSC return value of zero. And then afterwards, I can set the ratio to 1.0. With that, I was able to manipulate the case-award offsets and set the entropy between boots to zero. And also, I was able to manipulate the real state of the Linux entropy pool, which means that the first sequence, for example, of kernel stack on RIS was always the same between boots. So how the fix for this looks like? This was already added to the Linux kernel. Here this code checks whether the RDRanth is supported, and that's not the case, the virtual machine will simply not boot. So this essentially makes the RDRanth instruction mandatory for ACVES virtual machines. The second attack is based on the forging of fake MMIO regions. So the virtual machine's kernel uses memory mapped IOTO programed touch devices. With ACVES, MMIO is more complicated because the registers of the virtual machine are encrypted, and for that, ACVES introduces this MMIO NPS sequence. For example, when the kernel writes a value to an MMIO address, this address would have to be translated first from a virtual address to a guest physical address using the guest page table. Afterwards, it would be translated using the nested page table. And in this place, the virtual machine requires assistance from the hypervisor to mark the page's reserved. The hypervisor can mark the page's reserved by setting a special bit in the nested page table entry between the 52nd and the 63rd bit. The access will cause a VC exception, which will be handled by the VC handler inside the virtual machine. The VC handler can check what caused the exception, find out what's the MMIO address and value, and write them to the GCCB page, and then execute VMG exit to request KVM to emulate the MMIO access. What I found in the early series of ACVES patches was that the VC handler does not validate the faulting address, which means that the malicious hypervisor can mark any memory of the virtual machine as an MMIO region, which would allow the hypervisor to intercept the MOVAX accesses to leak data or to inject data in the virtual machine. Here I'm showing a proof of concept attack I checked with ACVES. Here a user space process inside the virtual machine allocates a page using M-App and then fuels it with a sequence of knobs, and then afterwards it adds a return instruction on the very end. Afterwards the process finds out what's the guest physical address for this page and reports that to the hypervisor using VM call. That's only to simplify the attack. Afterwards, the virtual machine allocates another page using M-App, but now this is a retry execute page, and then it copies the earlier buffer into this code buffer and calls into the code buffer, and then on the very end it prints out exiting. However, because the hypervisor is able to mark the Arrest Buffer as an MMIO region, it can intercept all the reads and provide any value the hypervisor wants. So if we don't run the attack, we see the output as expected, so we send the guest physical address or the frame number to the hypervisor, and then we execute the sequence of knobs and then we exit. However, if we run the attack, then we see another message that you got pwned, and then the list of files in the directory. The payload here was just a sequence of syscalls to execute that. The fix for this is also really simple. It's already added to the VC-MMIO handler in the Linux kernel. This code here checks that the MMIO access happens to an encrypted page, because that's where only the MMIO regions should be. If it happens that the MMIO access is in an encrypted page, then it means that there's either a kernel bug or there is an attack happening. So in this case, the code will return ES unsupported, and this will cause kernel panic. A similar fix was also added to the open virtual machine firmware. Let's move to the last attack. So earlier I mentioned that the virtual machine has to set a special bit to mark memory as encrypted or not, but I didn't specify how the virtual machine finds out what's the location of the CBIT and also how does a general-purpose kernel, why Linux, determine that it's using ACV at the moment. The code for finding out the CBIT location is shown here. It's in getACV encryption bit. So first the code executes CPU ID to find out whether it's running under a hypervisor, and then afterwards it checks whether it's run with ACV by again executing the CPU ID instruction with the correct leaf. And if it's using ACV, then the CBIT location is extracted and returned. The issue with the code here is that the CPU ID instruction is always intercepted, and the results for these return values should not be trusted. If we, you know, so a place where the CBIT is used is when an identity mapping of memory is created, where the physical address matches the virtual address. This happens in the early Linux boot code. First the ACV encryption mask will be called, which will find out what the CBIT location and then afterwards initialize and then team maps will be called to create a page tables and set it as shown below. This happens by writing to the select to register the variant. If we provide invalid information for the virtual machine, for example, that ACV is currently not used, the virtual machine will crash when it's booting, but it's still interesting to find out why this happens. One possibility is that because the memory previously was viewed as encrypted and the new page table was written there, maybe the page table locker would now access the cipher text and this would cause an invalid page table walk. However, reading the manual, that's not the case because page table walks ignore the CBIT. They always decrypt while doing the walk. Another possibility is that when the instruction gets fetched, maybe it's an invalid instruction because now it's actually possibly accessing the cipher code. However, that's not also the case because instruction fetters would also always decrypt the data, so the instructions will be valid in this case. But still it will be interesting to find out what's exactly causing the crash. So the function call initialize and then team maps gets called, which will store the instruction pointer onto the stack, which is now still viewed as encrypted. Once the guest page table would be created and then written and then set by writing to the CR3 register. However, all of the memory now is viewed as unencrypted, which would mean that any access to the stack would just read the cipher text, but not the plain text. When the code executes the return instruction, then it will jump into invalid address and this will cause the fault. However, the hypervisor can prevent this by, for example, populating the stack with other sets to rob gadgets from the busy image at the precise time. The hypervisor can then use this to do return-oriented programming in the virtual machine. However, the hypervisor can still not do much because, for example, code injection or the creation of new page tables requires that they are written in encrypted memory. However, now all of the memory is viewed as unencrypted. Additionally, no secrets can be stolen because they were written encrypted, but now the memory access will not try to decrypt the data. However, there is still the possibility to continue the attack by relying on another page table, which gets created earlier. For example, the open virtual machine firmware has to create its own page table in order to communicate with the outside world and also for the GHCB page needed for ACVES. We can potentially use that page table so that we have both encrypted and unencrypted memory. This would allow us to do code injection, for example, and continue the attack. One opportunity is to put the stack into the decrypted memory of the OVMF guest page table and then switch to that page table. However, because that memory is not mapped, we can't actually just naively put it there. However, I was still able to find a gadget which accomplishes that in one goal, that's in the busy image. That one first modifies the guest page table, so we're able to switch to the OVMF guest page table and also allows us to modify the stack pointer so we can position the stack into the decrypted memory. Then finally, it also gives us access to the instruction pointer, which means that we can easily continue the attack. Here, I'm going to show our demo for arbitrary code execution with an ACVES virtual machine. While it's booting, this just starts rendering this demo effect and then blitz.ianocore logo. Then after it has finished it, it resumes booting. How do we fix this? It's a little bit more complicated and these are the patches you can look through. Similar fixes were added to both Linux and also OVMF. Here I'm just going to show briefly one of the fixes. First, it relies on the RDRUN instruction to generate a random value and to save this random value into a memory location and also in the RDX register. Afterwards, the new page table would be loaded by writing to the CRT register and then there would be a comparison between the RDX value and also the one stored in memory. If it happens that the new page table is corrupted, this would read the ciphertext from memory and the check will fail and it will go into this red portion at the very end. This portion will cut up the stack and also enter an endless loop. Still, maybe the last interesting thing is to think about what these exploits based on manipulating the CPU ID instruction would work with ACVSMP. For that, I just briefly go through how measurement of the virtual machine works normally. So, the measurement includes the OVMF images and also the virtual machine save area when the virtual machine is before it started. This gets measured and signed by the AMD firmware and then it's given to the hypervisor who can send it to the remote virtual machine user who can then afterwards verify it. With ACVSMP, additionally to the measurement, a special page called the CPU ID pages code which is agreed upon between the hypervisor and the remote user and contains information about well, many of the CPU ID leaks. This essentially means the hypervisor can no longer write about CPU ID because the virtual machine's VC handler when the CPU ID instruction gets executed can just query the CPU ID page for the real information. So, final remarks, here we saw three different exploits for ACVS with Linux kernel based on missing hardening. Here we also assume that the attacker has full privileges which means that these attacks may not be possible if that's not the case. Additionally, all of these issues were fixed with Linux 5.10 and also OVMF. With this near future coming out ACVSMP, it might make sense to for example examine all the interfaces with the outside world and try to reduce them in order to reduce the attack surface, try to add more validation to data provided by the hypervisor and so forth. Also, I would like to thank the AMD team and also your curiole from SUSE who have been very helpful in the communication and also in doing most of the fixes for these things. If you have any questions, now it's a good time to ask. I think we go live very, very soon. So I don't know, maybe it might already be live now. So if anybody can hear this already what I'm saying, I think we see Martin and you can confirm. You have very, very nice presentation and good work. I think one valid question that was asked in the room really is how many of these attacks are really possible because you run Linux in the VM. So whether, okay. Yeah, right. I mean, of course to a large extent the reason for that is that Linux is a, let's say a legacy kernel which has previously tested the hypervisor and also external devices. So of course now if it's operating under another threat model, there will be certain security issues so I can some interfaces not being, not being harder, hardened. Yeah, I mean, but then again, even if it weren't Linux probably similar issues might have been in some other kernel. I mean, even if it's a, let's say a CO4 or some other micro kernel, still that's probably not in the, in the threat model for those operating systems. So then you still have to add some kind of hardening. So you think like it's maybe more a general issue which is due to the fact that kind of the roles are a bit switched to something like, like Linux VM normally would trust the hypervisor. There's no reason to mistrust it and now some needs of VM runs basically at a higher privilege level to some extent. So I think one question I had in that regard is, so you found some issues, but obviously there could be many, many other things that are somehow handled by the hypervisor. And did you, how did you do your work? Did you go through like the whole code or what was your approach basically to find these bugs? Yeah, so it was not a systemized approach. I was just reading stuff on the go and I mean, there are other interfaces which are also not explored. That's exactly the reason why I also said that for ACVS and
 Welcome to the talk Exploiting Interfaces of SCVES Protected Virtual Machines.  This work was already presented at the ROOTS conference and was co-authored with Matthias  Moritzer.  The work was done at the Fraunhofer Institute for Applied Integrated Security.  The focus of this talk is a recently available feature called AMD SCVES, which provides  sound protection for a virtual machine against a malicious hypervisor.  However, the virtual machine may still run a general-purpose operating system, for example  one based on Linux, which has previously considered the hypervisor as trusted.  This can potentially lead to security issues because the virtual machine communicates with  virtual devices, which are controlled by the hypervisor.  And also the virtual machine requires emulation of few special instructions done by KVM.  In this talk, I'm going to show three different attacks for SCVES virtual machines and also  how their fixes are done in the Linux kernel.  So let's just quickly go through how the virtual machine is protected.  So first, there is memory encryption for the virtual machine.  While this includes the virtual machine's firmware, bootloader, the kernel and user  space applications, however, because the virtual machine communicates with the outside world,  for example, the DMA region is not encrypted.  The virtual machine selects which memory is encrypted or not by setting a spatial bit,  the CBIT, in its private guest page table.  Additionally, with SCVES encrypted state, additionally, the architectural state for the virtual machine  is encrypted.  This includes architectural registers and other state information.  And also SCVES is now supported in Linux 5.10 officially.  Because the registers for the virtual machine are encrypted, the emulation of instructions  becomes more complicated.  For example, when the virtual machine executes CPU ID, this will raise a special exception,  a VC exception, shown in one, which would invoke the virtual machine's VC handler.  The VC handler can then write the necessary information in the GHCB page, which is unencrypted  and shared with the hypervisor, and then request emulation from KVM.  KVM can read the information from the GHCB page, emulate the instruction, write the new  information to the GHCB page, and resume the guest.  And then the VC handler can read the new registers from the GHCB page, validate them, and update  the architectural state, and execute IRED.  However, because emulation happens in KVM, the emulation of this instruction should not  be trusted.  The threat model, I'm assuming, I know of these attacks is that the attacker controls the host  before the virtual machine is launched, and the virtual machine uses a CVS for protection.  Additionally, the virtual machine initial state is measured and attested, and also the attacker  has access to the virtual machine's kernel image for static analysis.  The first attack is based on the fact that the hypervisor may be able to manipulate the  sources of entropy for the virtual machine.  The Linux kernel inside the virtual machine relies on the source of randomness to generate  random values for different probabilistic defenses, for example, kernel address space  layout randomization, and also for stack canaries.  Here in this talk, I'm just going to focus on KSR.  This is a feature which randomizes different regions for the Linux kernel, and it happens  during boot.  The sources of randomness include the RDR and RDTSC instructions, also the kernel build  string, and also the boot parameters structure, mostly initialized by information from QM.  The function which determines the random values is called KSR getRandomLong, and here's how  it looks like.  Initially, the function generates a initial random state using the getBootSeat function,  which returns a hash of the build string and the build parameter and the boot parameters.  However, because the attacker has access to the busy image, the build string is known  to the hypervisor.  Additionally, also the state of the boot param structure is known.  Afterwards, this code checks whether the RDR instruction is supported, and if that's the  case, it samples the random value.  However, because this feature is checked using the CPU ID instruction, the hypervisor can  say that this feature is not supported, so this code here would be eliminated.  Afterwards, the virtual machine checks whether the RDR instruction is available, then samples  the timestamp count and mixes this into the random value.  However, the hypervisor is always able to control the return value of the RDS instruction,  because the virtual machine may need to be moved to another CPU where the timestamp count  is different, so it's necessary to be able to modify it.  One way to do that is by marking the RDS instruction as an emulated instruction, and  this would allow to specify any value.  However, with ACVES, this would cause a special exception, which would involve the VC100,  and it can be easily detected.  The second option, which is much sneakier, is to use a special model-specific register  called TSE ratio, and also the field TSE offset in the virtual machine control block structure.  The RT-TSC instruction would use these values in the following way.  The ratio MSR would be used as a scale, and then the TSE offset would be used as an offset.  If the hypervisor sets both to zero, then the RT-TSC would just return zero.  I implemented the proof-of-concept exploit for an ACVES virtual machine, where I tried  to manipulate the case-award regions and also the Linux entropy pool.  First of all, we can't actually set the RT-TSC instruction to return zero for the entirety  of the time, because that instruction may be used for various different things in the  kernel, and you will get a hang if you do so.  So here in my exploit, I rely on page tracking in order to figure out the moment of time,  the period of time for which the case-award regions are being randomized, and for all  of these places, I can specify RT-TSC return value of zero.  And then afterwards, I can set the ratio to 1.0.  With that, I was able to manipulate the case-award offsets and set the entropy between boots  to zero.  And also, I was able to manipulate the real state of the Linux entropy pool, which means  that the first sequence, for example, of kernel stack on RIS was always the same between  boots.  So how the fix for this looks like?  This was already added to the Linux kernel.  Here this code checks whether the RDRanth is supported, and that's not the case, the virtual  machine will simply not boot.  So this essentially makes the RDRanth instruction mandatory for ACVES virtual machines.  The second attack is based on the forging of fake MMIO regions.  So the virtual machine's kernel uses memory mapped IOTO programed touch devices.  With ACVES, MMIO is more complicated because the registers of the virtual machine are encrypted,  and for that, ACVES introduces this MMIO NPS sequence.  For example, when the kernel writes a value to an MMIO address, this address would have  to be translated first from a virtual address to a guest physical address using the guest  page table.  Afterwards, it would be translated using the nested page table.  And in this place, the virtual machine requires assistance from the hypervisor to mark the  page's reserved.  The hypervisor can mark the page's reserved by setting a special bit in the nested page  table entry between the 52nd and the 63rd bit.  The access will cause a VC exception, which will be handled by the VC handler inside the  virtual machine.  The VC handler can check what caused the exception, find out what's the MMIO address and value,  and write them to the GCCB page, and then execute VMG exit to request KVM to emulate  the MMIO access.  What I found in the early series of ACVES patches was that the VC handler does not validate  the faulting address, which means that the malicious hypervisor can mark any memory of  the virtual machine as an MMIO region, which would allow the hypervisor to intercept the  MOVAX accesses to leak data or to inject data in the virtual machine.  Here I'm showing a proof of concept attack I checked with ACVES.  Here a user space process inside the virtual machine allocates a page using M-App and then  fuels it with a sequence of knobs, and then afterwards it adds a return instruction on  the very end.  Afterwards the process finds out what's the guest physical address for this page and reports  that to the hypervisor using VM call.  That's only to simplify the attack.  Afterwards, the virtual machine allocates another page using M-App, but now this is a  retry execute page, and then it copies the earlier buffer into this code buffer and calls  into the code buffer, and then on the very end it prints out exiting.  However, because the hypervisor is able to mark the Arrest Buffer as an MMIO region,  it can intercept all the reads and provide any value the hypervisor wants.  So if we don't run the attack, we see the output as expected, so we send the guest physical  address or the frame number to the hypervisor, and then we execute the sequence of knobs  and then we exit.  However, if we run the attack, then we see another message that you got pwned, and then  the list of files in the directory.  The payload here was just a sequence of syscalls to execute that.  The fix for this is also really simple.  It's already added to the VC-MMIO handler in the Linux kernel.  This code here checks that the MMIO access happens to an encrypted page, because that's  where only the MMIO regions should be.  If it happens that the MMIO access is in an encrypted page, then it means that there's  either a kernel bug or there is an attack happening.  So in this case, the code will return ES unsupported, and this will cause kernel panic.  A similar fix was also added to the open virtual machine firmware.  Let's move to the last attack.  So earlier I mentioned that the virtual machine has to set a special bit to mark memory as  encrypted or not, but I didn't specify how the virtual machine finds out what's the  location of the CBIT and also how does a general-purpose kernel, why Linux, determine that it's using  ACV at the moment.  The code for finding out the CBIT location is shown here.  It's in getACV encryption bit.  So first the code executes CPU ID to find out whether it's running under a hypervisor,  and then afterwards it checks whether it's run with ACV by again executing the CPU ID  instruction with the correct leaf.  And if it's using ACV, then the CBIT location is extracted and returned.  The issue with the code here is that the CPU ID instruction is always intercepted, and  the results for these return values should not be trusted.  If we, you know, so a place where the CBIT is used is when an identity mapping of memory  is created, where the physical address matches the virtual address.  This happens in the early Linux boot code.  First the ACV encryption mask will be called, which will find out what the CBIT location  and then afterwards initialize and then team maps will be called to create a page tables  and set it as shown below.  This happens by writing to the select to register the variant.  If we provide invalid information for the virtual machine, for example, that ACV is currently  not used, the virtual machine will crash when it's booting, but it's still interesting  to find out why this happens.  One possibility is that because the memory previously was viewed as encrypted and the  new page table was written there, maybe the page table locker would now access the cipher  text and this would cause an invalid page table walk.  However, reading the manual, that's not the case because page table walks ignore the CBIT.  They always decrypt while doing the walk.  Another possibility is that when the instruction gets fetched, maybe it's an invalid instruction  because now it's actually possibly accessing the cipher code.  However, that's not also the case because instruction fetters would also always decrypt  the data, so the instructions will be valid in this case.  But still it will be interesting to find out what's exactly causing the crash.  So the function call initialize and then team maps gets called, which will store the instruction  pointer onto the stack, which is now still viewed as encrypted.  Once the guest page table would be created and then written and then set by writing to  the CR3 register.  However, all of the memory now is viewed as unencrypted, which would mean that any access  to the stack would just read the cipher text, but not the plain text.  When the code executes the return instruction, then it will jump into invalid address and  this will cause the fault.  However, the hypervisor can prevent this by, for example, populating the stack with other  sets to rob gadgets from the busy image at the precise time.  The hypervisor can then use this to do return-oriented programming in the virtual machine.  However, the hypervisor can still not do much because, for example, code injection or the  creation of new page tables requires that they are written in encrypted memory.  However, now all of the memory is viewed as unencrypted.  Additionally, no secrets can be stolen because they were written encrypted, but now the memory  access will not try to decrypt the data.  However, there is still the possibility to continue the attack by relying on another  page table, which gets created earlier.  For example, the open virtual machine firmware has to create its own page table in order  to communicate with the outside world and also for the GHCB page needed for ACVES.  We can potentially use that page table so that we have both encrypted and unencrypted  memory.  This would allow us to do code injection, for example, and continue the attack.  One opportunity is to put the stack into the decrypted memory of the OVMF guest page table  and then switch to that page table.  However, because that memory is not mapped, we can't actually just naively put it there.  However, I was still able to find a gadget which accomplishes that in one goal, that's  in the busy image.  That one first modifies the guest page table, so we're able to switch to the OVMF guest  page table and also allows us to modify the stack pointer so we can position the stack  into the decrypted memory.  Then finally, it also gives us access to the instruction pointer, which means that we can  easily continue the attack.  Here, I'm going to show our demo for arbitrary code execution with an ACVES virtual machine.  While it's booting, this just starts rendering this demo effect and then blitz.ianocore logo.  Then after it has finished it, it resumes booting.  How do we fix this?  It's a little bit more complicated and these are the patches you can look through.  Similar fixes were added to both Linux and also OVMF.  Here I'm just going to show briefly one of the fixes.  First, it relies on the RDRUN instruction to generate a random value and to save this  random value into a memory location and also in the RDX register.  Afterwards, the new page table would be loaded by writing to the CRT register and then there  would be a comparison between the RDX value and also the one stored in memory.  If it happens that the new page table is corrupted, this would read the ciphertext from memory  and the check will fail and it will go into this red portion at the very end.  This portion will cut up the stack and also enter an endless loop.  Still, maybe the last interesting thing is to think about what these exploits based on  manipulating the CPU ID instruction would work with ACVSMP.  For that, I just briefly go through how measurement of the virtual machine works normally.  So, the measurement includes the OVMF images and also the virtual machine save area when  the virtual machine is before it started.  This gets measured and signed by the AMD firmware and then it's given to the hypervisor who  can send it to the remote virtual machine user who can then afterwards verify it.  With ACVSMP, additionally to the measurement, a special page called the CPU ID pages code  which is agreed upon between the hypervisor and the remote user and contains information  about well, many of the CPU ID leaks.  This essentially means the hypervisor can no longer write about CPU ID because the virtual  machine's VC handler when the CPU ID instruction gets executed can just query the CPU ID page  for the real information.  So, final remarks, here we saw three different exploits for ACVS with Linux kernel based  on missing hardening.  Here we also assume that the attacker has full privileges which means that these attacks  may not be possible if that's not the case.  Additionally, all of these issues were fixed with Linux 5.10 and also OVMF.  With this near future coming out ACVSMP, it might make sense to for example examine all  the interfaces with the outside world and try to reduce them in order to reduce the  attack surface, try to add more validation to data provided by the hypervisor and so  forth.  Also, I would like to thank the AMD team and also your curiole from SUSE who have been  very helpful in the communication and also in doing most of the fixes for these things.  If you have any questions, now it's a good time to ask.  I think we go live very, very soon.  So I don't know, maybe it might already be live now.  So if anybody can hear this already what I'm saying, I think we see Martin and you can  confirm.  You have very, very nice presentation and good work.  I think one valid question that was asked in the room really is how many of these attacks  are really possible because you run Linux in the VM.  So whether, okay.  Yeah, right.  I mean, of course to a large extent the reason for that is that Linux is a, let's say a  legacy kernel which has previously tested the hypervisor and also external devices.  So of course now if it's operating under another threat model, there will be certain security  issues so I can some interfaces not being, not being harder, hardened.  Yeah, I mean, but then again, even if it weren't Linux probably similar issues might have been  in some other kernel.  I mean, even if it's a, let's say a CO4 or some other micro kernel, still that's probably  not in the, in the threat model for those operating systems.  So then you still have to add some kind of hardening.  So you think like it's maybe more a general issue which is due to the fact that kind of  the roles are a bit switched to something like, like Linux VM normally would trust the hypervisor.  There's no reason to mistrust it and now some needs of VM runs basically at a higher privilege  level to some extent.  So I think one question I had in that regard is, so you found some issues, but obviously  there could be many, many other things that are somehow handled by the hypervisor.  And did you, how did you do your work?  Did you go through like the whole code or what was your approach basically to find these  bugs?  Yeah, so it was not a systemized approach.  I was just reading stuff on the go and I mean, there are other interfaces which are also  not explored.  That's exactly the reason why I also said that for ACVS and
tee_sev_es_0_167.png tee_sev_es_168_551.png tee_sev_es_552_1763.png tee_sev_es_1764_2975.png tee_sev_es_2976_4523.png tee_sev_es_4524_5063.png tee_sev_es_5064_6275.png tee_sev_es_6276_11687.png tee_sev_es_11688_13451.png tee_sev_es_13452_15131.png tee_sev_es_15132_15215.png tee_sev_es_15216_15347.png tee_sev_es_15348_17855.png tee_sev_es_17856_20315.png tee_sev_es_20316_23039.png tee_sev_es_23040_25199.png tee_sev_es_25200_26219.png tee_sev_es_26220_26267.png tee_sev_es_26268_26327.png tee_sev_es_26328_26435.png tee_sev_es_26436_26507.png tee_sev_es_26508_26663.png tee_sev_es_26664_28919.png tee_sev_es_28920_29963.png tee_sev_es_29964_32123.png tee_sev_es_32124_32195.png tee_sev_es_32196_32735.png tee_sev_es_32736_32819.png tee_sev_es_32820_33119.png tee_sev_es_33120_35807.png tee_sev_es_35808_35926.png