Fixed
Status Update
Comments
ma...@google.com <ma...@google.com> #2
Thank you for the report.
I'd agree this is an area that could stand more investment in mitigation hardening. But I think we can talk about that in terms of feature work rather than treating this as an urgent or embargoed vulnerability in LLVM: the technique described is a mitigation bypass that requires an application has already been exploited; and the general pattern of this attack and its mitigation have been discussed since 2003:
https://web.archive.org/web/20090308073846/http://www.ngssoftware.com//papers//defeating-w2k3-stack-protection.pdf
http://www.uninformed.org/?v=5&a=2&t=txt
https://web.archive.org/web/20090314064926/http://blogs.technet.com/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx
http://uninformed.org/index.cgi?v=9&a=4&p=7
I'd agree this is an area that could stand more investment in mitigation hardening. But I think we can talk about that in terms of feature work rather than treating this as an urgent or embargoed vulnerability in LLVM: the technique described is a mitigation bypass that requires an application has already been exploited; and the general pattern of this attack and its mitigation have been discussed since 2003:
m....@vu.nl <m....@vu.nl> #3
Hi, thanks for the quick answer! From our side, it's fine to also handle it as a feature-request; in the end, we would be glad if additional hardening makes it into the LLVM-framework.
However, we would prefer if this report is not made public until other affected parties had their chance to deal with this issue. We can check back with you after we heard from them.
Regarding SEH: We are aware of these attacks. While they are appear similar on a high-level, they differ in the technical details:
Windows SEH exploitation relies on a direct corruption of the linked list used to find the exception handlers. In this report, no such linked list (or other dynamic exception handling metadata) is present on the stack. Instead, we use the unwinder as confused deputy to restore execution context to a different landing pad as originally foreseen, solely based on the saved return addresses on the stack. As a side effect, this attacks even works in presence of SEH hardening techniques and for other unwinding schemes (e.g., the one provided by the Itanium C++ ABI).
Furthermore, we could observe that in the process of unwinding, an attacker may obtain control over callee-saved registers, as these are likewise installed/restored by the unwinder.
However, we would prefer if this report is not made public until other affected parties had their chance to deal with this issue. We can check back with you after we heard from them.
Regarding SEH: We are aware of these attacks. While they are appear similar on a high-level, they differ in the technical details:
Windows SEH exploitation relies on a direct corruption of the linked list used to find the exception handlers. In this report, no such linked list (or other dynamic exception handling metadata) is present on the stack. Instead, we use the unwinder as confused deputy to restore execution context to a different landing pad as originally foreseen, solely based on the saved return addresses on the stack. As a side effect, this attacks even works in presence of SEH hardening techniques and for other unwinding schemes (e.g., the one provided by the Itanium C++ ABI).
Furthermore, we could observe that in the process of unwinding, an attacker may obtain control over callee-saved registers, as these are likewise installed/restored by the unwinder.
ma...@google.com <ma...@google.com> #4
Understood; we'll keep this report private until we hear otherwise from you.
an...@intel.com <an...@intel.com> #5
FYI, we are going to have developers at Intel looking into mitigation for this problem and coordinating with people at Intel who are doing similar work for gcc. If anyone else is interested in coordinating with us, let me know. I'll update here when we have a patch ready for review.
kr...@arm.com <kr...@arm.com> #6
Hi, m.muench, Andrew Kaylor,
I'm wondering if there happens to be any status update on the coordination with other parties or the mitigation work at Intel?
Thank you!
I'm wondering if there happens to be any status update on the coordination with other parties or the mitigation work at Intel?
Thank you!
m....@vu.nl <m....@vu.nl> #7
Hi!
Intel requested an extension of the embargo. It will potentially last at least until early December, or even February. Is there anything we can do from our side for better coordination?
In the meantime, as update from our side: The academic paper mentioned up in the initial post got accepted, and will indeed be presented in February.
Intel requested an extension of the embargo. It will potentially last at least until early December, or even February. Is there anything we can do from our side for better coordination?
In the meantime, as update from our side: The academic paper mentioned up in the initial post got accepted, and will indeed be presented in February.
kr...@arm.com <kr...@arm.com> #8
Thank you for the update m.muench!
I think that it indeed would be useful to share the academic paper you refer to as I expect it might help myself and others here to better judge what the impact of this vulnerability may be and what should be done to mitigate it.
Andrew Kaylor: I'm wondering if any of the mitigation work you're doing is targeting LLVM? If so, I wonder if you need any help with reviews or in target-specific areas?
I think that it indeed would be useful to share the academic paper you refer to as I expect it might help myself and others here to better judge what the impact of this vulnerability may be and what should be done to mitigate it.
Andrew Kaylor: I'm wondering if any of the mitigation work you're doing is targeting LLVM? If so, I wonder if you need any help with reviews or in target-specific areas?
m....@vu.nl <m....@vu.nl> #9
Hi,
absolutely no problem, please find a preprint of the paper as attachment here. As disclosure with other parties is still ongoing, please treat it as confidential for now.
Also, shall we report this separately to ARM @krist...@arm.com? (Sorry, I can't see the full email address in this thread). We originally did not do so, as we deemed this more of a compiler & toolchain issue. However, if it helps anything on your side, we are happy to follow up.
[Deleted Monorail Attachment]
absolutely no problem, please find a preprint of the paper as attachment here. As disclosure with other parties is still ongoing, please treat it as confidential for now.
Also, shall we report this separately to ARM @krist...@arm.com? (Sorry, I can't see the full email address in this thread). We originally did not do so, as we deemed this more of a compiler & toolchain issue. However, if it helps anything on your side, we are happy to follow up.
[Deleted Monorail Attachment]
an...@intel.com <an...@intel.com> #10
Hi, Kristof. I've just returned from sabbatical today, so I'll need a couple of days to get caught up on things. I believe we are planning some changes to LLVM. I'd be happy to share any patches with whoever in this group is interested.
kr...@arm.com <kr...@arm.com> #11
Hi Andrew, I'd just like to explicitly state that I'm happy to help review patches you have ready to be reviewed.
kr...@arm.com <kr...@arm.com> #12
m.muench: thank you for sharing the preprint of the paper. I found it was an interesting read!
I don't think it would add further value to report this separately to Arm, so there is no need to do so.
Thanks!
I don't think it would add further value to report this separately to Arm, so there is no need to do so.
Thanks!
an...@intel.com <an...@intel.com> #13
The following related patch has been committed to the LLVM libunwind: https://reviews.llvm.org/D136667
I'll post an update here when the LLVM patch is cleared to go public.
I'll post an update here when the LLVM patch is cleared to go public.
an...@intel.com <an...@intel.com> #14
pi...@pietroalbini.org <pi...@pietroalbini.org> #15
Hello all,
I see that patches for this have been merged in the LLVM repository, are the details in this issue still under embargo? Otherwise we can make the issue public.
Pietro.
I see that patches for this have been merged in the LLVM repository, are the details in this issue still under embargo? Otherwise we can make the issue public.
Pietro.
m....@vu.nl <m....@vu.nl> #16
Hi all,
The embargo has been lifted last week (Jan 10th), so yes, the issue can be made public. I deleted the pre-print from earlier on in this thread, the academic paper can be found now at:https://download.vusec.net/papers/chop_ndss23.pdf
Best,
Marius
The embargo has been lifted last week (Jan 10th), so yes, the issue can be made public. I deleted the pre-print from earlier on in this thread, the academic paper can be found now at:
Best,
Marius
ma...@google.com <ma...@google.com> #17
t....@gmail.com <t....@gmail.com> #18
Just to let anyone watching know, I've posted https://reviews.llvm.org/D143637 to llvm-commits that should make all the unwind paths in a function explicit and instrumentable if stack-protection is being used. So the patches above ought to trigger more often.
I'm also working on a change to allow the SDAG instrumentation of the unwind path, though it's not as neat since we've dropped `noreturn` information by that point.
I'm also working on a change to allow the SDAG instrumentation of the unwind path, though it's not as neat since we've dropped `noreturn` information by that point.
ar...@gmail.com <ar...@gmail.com> #19
[Comment Deleted]
nd...@google.com <nd...@google.com> #20
Tim, I noticed that your change was reverted in https://reviews.llvm.org/rG91beab69cdac408731da0889954aabe10d93880c .
Is there more to do here?
---
In particular, we're seeing stack canaries show up in unexpected places in C codebases that contain noreturn functions when built with -fstack-protector-strong as a result ofhttps://reviews.llvm.org/D139254 . It's confusing to me why we're checking the stack canary before every call to a noreturn function; that seems like too coarse a fix for this report, which appears specific to the C++ runtime, IMO.
https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=a25982ada523689c8745d7fb4b1b93c8f5dab2e7 looks like the corresponding GCC change. Looks like it only inserts the stack canary for calls to functions that:
1. are noreturn
2. are nounwind in LLVM IR; i.e. either C++ code built with -fno-exceptions, or calls to noexcept functions
I think LLVM is missing that second check, that would limit this to C++ code using exceptions. Let me see if we can narrow down when these stack canaries are inserted in LLVM.
(I only noticed this because we're observing boot failures in the linux kernel as a result ofhttps://reviews.llvm.org/rG91beab69cdac408731da0889954aabe10d93880c ; https://github.com/ClangBuiltLinux/linux/issues/1815 ).
I've postedhttps://reviews.llvm.org/D147975 which I think will more appropriately limit this to C++ code with exceptions enabled (for functions not marked noexcept). PTAL (If that's good, we'll need to backport it to the clang-16 release branch)
Is there more to do here?
---
In particular, we're seeing stack canaries show up in unexpected places in C codebases that contain noreturn functions when built with -fstack-protector-strong as a result of
1. are noreturn
2. are nounwind in LLVM IR; i.e. either C++ code built with -fno-exceptions, or calls to noexcept functions
I think LLVM is missing that second check, that would limit this to C++ code using exceptions. Let me see if we can narrow down when these stack canaries are inserted in LLVM.
(I only noticed this because we're observing boot failures in the linux kernel as a result of
I've posted
kr...@arm.com <kr...@arm.com> #21
It seems all necessary work on this issue has been done.
mu...@gmail.com <mu...@gmail.com> #22
Open
mujumbi hamidu
mujumbi hamidu
Description
During exception handling, the unwinding process is controlled by data located on the stack. Assuming an attacker capable of corrupting stack memory, this results in operation on potentially attacker controlled data and allows crafting powerful memory corruption and control-flow hijacking primitives. Under normal circumstances, this would be mitigated by stack canaries, shadow stacks, or other backward-edge control-flow protections. We found that these checks are missing or not enforced on the unwinding control-flow path.
We have found a variety of exploitation strategies and demonstrated applicability by creating proof-of-concept code execution exploits of real-world vulnerabilities otherwise mitigated by stack canaries.
The core concept is corrupting the saved return address which is used by the unwinder to determine both the call frame information and the landing pads invoked. In the most simple case, this allows diverting execution to other exception handlers, controlling their stack frames and pivoting to ROP using handlers without canaries.
Furthermore, current shadow stack implementations including LLVM ShadowCallStack can be bypassed using this technique as the unwinding process is solely based on the normal stack.
# Impact
Attackers capable of corrupting stack memory and forcing a program to enter the unwinding path (e.g., by throwing an exception) can bypass existing backwards-edge control-flow protections.
# Proof of Concept
To demonstrate the issue, we created the minimal proof-of-concept below. It overwrites both the canary and saved return inside vuln(), and only throws in case an command line argument was provided. The saved return address points into the try block of catches().
```
// Compile with:
// clang++ -fstack-protector-all -fomit-frame-pointer -Wno-array-bounds llvm_test.cpp
#include <unistd.h>
void catches () {
try {
throw 1;
}
catch (...) {
write(STDOUT_FILENO, "win\n", 4);
_exit(1);
}
}
void vuln(int op) {
void * buf[1];
buf[2] = (char *) catches + (size_t) 28; // overwrite the saved return address - depending on your system, the offset may need to be changed
buf[1] = (void *) 0xdeadbeef; // clobber the canary, because our exploit would do that too
// Throw only under certain conditions
// This way, the intended behaviour when clobbering the canary can be observed
if (op == 1){
throw 1;
}
}
int main(int argc, char **argv) {
if (argc > 1)
vuln(1); // Show our proof of concept
else
vuln(2); // Counter-Example:
}
```
We compiled this example with LLVM-14, using the docker environment provided by silkeh/clang. When executed, the behavior looks as follows:
```
$ ./poc
*** stack smashing detected ***: terminated
Aborted (core dumped)
$ ./poc test
Win
```
In other words, only in the first case, where the code triggers the return (rather than unwinding) path using the saved returned address, we observe a (failed) canary check.
# Potential Mitigations
Ensure canary checks are present on the unwinding path. This includes prior to throwing as well as prior to dereferencing the saved return address by the unwinder for each frame.
When unwinding using shadow stacks, ensure consistency between the shadow stack and the main stack or use the saved return addresses from the shadow stack to unwind.
For additional hardening, emit canaries for functions containing landing pads.
# Other parties involved
In parallel, we also disclose this issue to Apple, Microsoft, Intel & Google.
Besides this, we wrote an academic paper about this issue and submitted it to the Network and Distributed Systems Symposium (NDSS) 2023. While acceptance is not guaranteed, this paper may be presented in February 2023.
# Disclosure Timeline
To provide adequate time for addressing this issue, we will be delaying online publication of our work for a standard disclosure period of 90 days. In case this does not work for you, please reach out to us so we can discuss alternative timelines and, potentially, coordinate with other vendors.
# Additional Information
For additional details, we are happy to answer questions and share a preprint of the academic paper.