An Ancient Kernel Hole Is Not Closed
grsecurity.net
grsecurity.net
Very interesting discussion that will likely continue as more people wrap their heads around this tough problem.
[1] https://blog.qualys.com/securitylabs/2017/06/19/the-stack-cl...
Readers shouldn't have to solve a puzzle to read a title.
This is explained in the article.
But reading the article has never been a barrier for HN editing a headline to make it less accurate in the past, so why start now? :)
Here's the title of the article:
An Ancient Kernel Hole is (Not) Closed
I thought that I copy-pasted the article title to be the title of this submission.
Did I omit the parenthesis? If so, it was unintentional, and I apologize.
Thanks for the heads-up!
I remember these from pre-dotNet days but based on the second link it looks not much has changed in VB.net.
[1]https://msdn.microsoft.com/en-us/library/aa264523(v=vs.60).a... [2]https://docs.microsoft.com/en-us/dotnet/visual-basic/languag...
Edit: Looks like a fix for LLVM being proposed (https://reviews.llvm.org/D9653) is just to probe every page that a stack allocation covers, rather than just the final one. Doesn't sound tough at all.
Open Source Security (the company behind grsecurity) is still existing, so it's got enough customers to support the grsecurity developers (both the ones we know, as well as (presumably) PaX Team).
It seems to me like grsecurity aren't interested in upstreaming their patches. That's one reason (among others) why their patches are distributed as one big file, instead of (for example) a patch set or a Git branch.
Why are they not interested? Well, it seems to me like Linux devs may not have placed as high a value on security, when compared to other metrics (such as performance). I understand what it's like to be advocating for a position, not finding acceptance after a long try; finally saying "screw it" and going their own (commercial) way.
So any 'debate' related to grsecurity is going to include people (including LWN commenters) who feel very strongly against grsecurity's position, and how they should be more open, and free, and willing to compromise (if that's the right word) to get the grsecurity/PaX Team patches upstreamed.
And then you have very strong personalities in the kernel dev community. That makes compromise very difficult.
It my opinion, it's the same reason why having a conversation related to politics is nigh impossible: Your strong views affect what you say, how you say it, what you hear and how your perceive it.
And if you need a screencap, get the one of him flipping the bird to camera, but don't give it any context (I believe that's the rule for that particular image).
While you can prevent the heap from being allocated over the stack, the stack may grow "autonomously" during normal processor instructions where the OS can't intervene.
The classic fix was to put a "guard page", i.e. a segment of memory marked as forbidden, so if stack would grow into there, it'd trigger a page fault and then the process can be stopped.
However, while stack usually grows in small increments, you may allocate some very large data on the stack and not write or read anything in that buffer - so if you do it correctly, the end of the stack will "jump over" that guard page, leaving it in the middle of the stack but not touching it in any way; and the end of the stack will overlap with the heap, enabling all the fun exploits again.
The vulnerability is in the userland process. However, the kernel can utilize a stop-gap measure that makes exploitation difficult in most scenarios (but this is arbitrary).
However, for that fix to be useful, every single distribution needs to recompile every single userland package and distribute it to every single user - i.e., instead of downloading a security patch, you need to download a new version of every executable on your hdd.
If the distro guys just did for the next version (Atleast the guys like Debian, Fedora etc from whom most other distros are derived) wouldn't most systems be protected?
It would take a while but it wouldn't take 10 years atleast. Mass adoption is impossible but incremental changes are. If critical executables are patched then we have atleast a partially protected system right? (I am not an expert but this sounds a bit more plausible than breaking the entire userland)
> The exploits and proofs of concept that we developed in the course of our research are all Local Privilege Escalations: an attacker who has any kind of access to an affected system can exploit the Stack Clash vulnerability and obtain full root privileges.
https://blog.qualys.com/securitylabs/2017/06/19/the-stack-cl...
We had to deal with something very similar to this when I was working on REALbasic. When targeting Windows, if the compiler happened to generate a function prolog which would allocate more than one page of stack space for local variables, we had to generate extra instructions which would touch each intermediate page, in order, or the kernel would trap the first access beyond the guard page as out-of-bounds. It was an interesting bit of semi-security.
The problem is with stack growing into memory previously allocated with mmap, including but not limited to mmap being used to back up large malloc allocations. If this happens, writing to objects in the mmap-ed area can be used to overwrite stack frame of the current function, including its return address, and hence control code execution after returning.
In 2005 it was discovered that when a process fills up all its address space with allocations, Linux will allow an mmap call to allocate memory directly near the top of the stack so that any stack expansion will run into this allocation. This was not fixed.
In 2010, Rafal Wojtczuk developed a working exploit against Xorg using this principle. The problem was finally fixed by enforcing a one page gap at the top of the stack where mmap allocations were forbidden and preventing the stack from growing too close to the nearest mmap allocation. TFA author couldn't resist bitching about some technical issues with this patch which were promptly fixed.
Unfortunately, one page gap is not enough because it can be "jumped over": a sufficiently vulnerable application can be tricked to mmap memory directly adjacent to the gap and then create a large stack allocation (alloca, variable length array or just a fixed-length array larger than 4kB) which covers the whole gap. As long as this allocation isn't used, the kernel has no way of knowing that it exists. If the application then is tricked into calling another function before using this allocation, a new stack frame is created behind the big allocation and the safety gap, in mmap area.
void recurse (int m, int n) {
char arr[n]
recurse(m--, n)
}
If 'm' and 'n' are user-controlled, 'recurse' is executing in thread A, and thread A's stack is above thread B's stack, then you can cause 'recurse' to recurse down to the point that it makes its way into thread B's stack region. Normally, this is prevented by 'guard pages' in between the two thread's stacks. However, if you have a variable length array, as in the example, it's possible to allocate enough memory to just skip right over the guard page. Once you've done this, you're now writing directly into the other thread's stack, with all the fun stuff that entails.Implemented the 1MiB guard page and glibc hardening.