GrapheneOS / os-issue-tracker

Issue tracker for GrapheneOS Android Open Source Project hardening work. Standalone projects like Auditor, AttestationServer and hardened_malloc have their own dedicated trackers.
https://grapheneos.org/
357 stars 21 forks source link

Bypassing MTE by exploiting Signal handlers #3608

Closed Striker789 closed 4 months ago

Striker789 commented 4 months ago

Currently GOS uses ASYMMETRIC MTE for the low overhead and to close the soft time constraint in ASYNC MODE. I was having a read though https://googleprojectzero.blogspot.com/2023/08/mte-as-implemented-part-1.html?m=1 Where I had come accross possible MTE bypasses in ASYNC mode and I quote: 'Since SIGSEGV is a catchable signal, any signal handlers that can handle SIGSEGV become a critical attack surface for async MTE bypasses'. Moreover, ‘The concept is simple - if we can corrupt any state that would result in the signal handler concluding that a SIGSEGV coming from a tag-check failure is handled/safe, then we can effectively disable MTE for the process’, hence having MTE as ineffective.

However, if EFAULT is used for both SYNC read and writes this closes the signal handler attack surface: ‘The way that the sync MTE check mode is currently implemented in the linux kernel means that kernel-space accesses to incorrectly tagged user-space pointers result in the system call returning EFAULT. While this is probably the cleanest and simplest approach, and is consistent with current kernel behaviour especially when it comes to handling results for partial operations, this has the potential to lead to bypasses/oracles in some circumstances.’
In regard to the oracle exploits mentioned above, I believe this is more to do with attacks similair to spectre which requires a local attacker. To my knowledge GOS uses SIGSEGV for both read and write, I have not come across the ability for ASYNC to use EFAULT.

If this be true, does GOS offer a mitigation for this to prevent an attacker from exploiting signal handlers, or can it be possible to simply allow all users to have the option to pick SYNC MTE to bypass this attack surface by using EFAULT rather than SIGSEGV?

Furthermore, MTE is not enabled for the kernel, would it be possible to give the user an option to enable a toggle similar to the one perviously offered for OS processes?

Thank you.

thestinger commented 4 months ago

'Since SIGSEGV is a catchable signal, any signal handlers that can handle SIGSEGV become a critical attack surface for async MTE bypasses'. Moreover, ‘The concept is simple - if we can corrupt any state that would result in the signal handler concluding that a SIGSEGV coming from a tag-check failure is handled/safe, then we can effectively disable MTE for the process’, hence having MTE as ineffective.

We can force default handling for MTE crashes and we already have the code for that since we had to add it for compatibility.

https://github.com/GrapheneOS/platform_bionic/commit/cc06ce2c8e323f576794f26b0929d4b885b303bf

We could expand this but it needs to be a specific plan with a specific goal.

Furthermore, MTE is not enabled for the kernel, would it be possible to give the user an option to enable a toggle similar to the one perviously offered for OS processes?

There's not currently a production-oriented implementation for the kernel for us to enable, just KASan, which doesn't really meet our requirements.

Striker789 commented 4 months ago

Thank you for your response @thestinger

We can force default handling for MTE crashes and we already have the code for that since we had to add it for compatibility.

That would be a great approach to limit signal handler attack surface if the project is not in inclined to allow SYNC MTE for read and write.

We could expand this but it needs to be a specific plan with a specific goal.

I believe that appropriate objective for this would come under the main goals of the project, attack surface reduction.

There's not currently a production-oriented implementation for the kernel for us to enable, just KASan, which doesn't really meet our requirements.

The stock OS does offer Kasan through adb, although not recommended due to optimization and performance it still can provide an additional layer of security ‘ Kernel Address Sanitizer (KASAN) is a dynamic memory safety error detector designed to find out-of-bounds and use-after-free bugs.’ https://docs.kernel.org/dev-tools/kasan.html#hardware-tag-based-kasan

Significantly, hardened_malloc would provide greater hardening to the kernel but would require work to port from user-space to the kernel. The importance of further hardening the kernel is due to APTs requiring kernel based exploits for maximum exfiltration. Through this hardening this raises the bar for kernel memory corruption exploits. The new implementation of shadowstack, BTI, kCFI all contribute as well to memory corruption and confusion bugs, but implementing hardware/software based MTE would be immensely impactful.

Hence, would you kindly elaborate on how Kasan does not meet the requirements?

Thank you.

thestinger commented 4 months ago

Hence, would you kindly elaborate on how Kasan does not meet the requirements?

It's written as a debugging tool for finding bugs rather than providing security properties. Userspace ASan often makes exploitation easier rather than harder. We have no reason to believe KASan's memory tagging mode was truly written for the purpose we want to use it or that it serves that purpose well.

Striker789 commented 4 months ago

@thestinger thank you for clarifying and making things clearer.

thestinger commented 4 months ago

We could evaluate KASan memory tagging to see if it would be acceptable but that would take time/resources and the answer is likely that no it won't be acceptable without major changes or just making another implementation. It would be possible to add a production/hardening focused memory tagging implementation to the kernel's slab allocator, etc. Porting hardened_malloc to the kernel isn't possible, but it would be possible to make a new hardening-focused slab allocator. However, the slab allocators don't have a stable API and therefore it would be far harder to maintain. The hardening we could do there is also more limited. It likely makes more sense just to harden slub on a best effort basis and accept the limitations.

Striker789 commented 4 months ago

@thestinger

that would take time/resources

Given the lack of proportionality between the project’s achievements and the minimal resources at hand it is more than fair to say that the team has more than excelled. It would be possible to add a production/hardening focused memory tagging implementation to the kernel's slab allocator

That would be substantially more practical as it would be more of a targeted approach and would also be easier to implement for slub granted the simplified design.

The slab allocators don't have a stable API and therefore it would be far harder to maintain

The team doesn’t necessarily have to update to the newest version of the kernel if there are no security/significant updates provided the complexity in porting for slab allocators due to the unstable API’s as opposed to slub.

It likely makes more sense just to harden slub on a best effort basis and accept the limitations.

That could be viewed as more of a short-term approach as project does already harden kernel allocators through canaries, but could also incorporate guard pages around freed memory regions at the cost of additional overhead which provides deterministic protection against use-after-free vulnerabilities in addition to overflows and underflows.

As discussed above, force default handling of MTE is also significantly important to limit signal handler exploitation to bypass MTE since any sophisticated attacker would view this as a viable target alongside logic bugs. However, despite logic bugs being difficult to prevent in a systematic, the project does offer mitigations such as type based CFI, kCFI etc which does to an extent limit logic bug exploitations. If you didn’t mind I add, another important aspect is stack MTE, however you had stated that it is incompatible with Vanadium due to the garbage collection. Would it not be possible to enable it for user-space and OS processes except Vanadium?

Thank you.