Hacker News new | past | comments | ask | show | jobs | submit login

The classic vulnerability is that you manage to grow the heap or the stack so that they collide (they start at opposite ends and grow towards the middle) then you get all kinds of exploits possible by stack overwriting the heap or heap overwriting the stack.

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.




you may allocate some very large data on the stack and not write or read anything in that buffer

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.


OK, if I understand correctly, there is no privilege escalation. Eg., running this exploit in user code doesn't get you root. When they described it as a "kernel hole" I assumed it was much worse. So the word "kernel" is there because the design flaw is in the kernel, not because it allows a complete system takeover (unless it's against a privileged program).


No, this does mean privilege escalation (pretty much all recent proof of concepts for this problem are local user-to-root escalation) because it allows you to engineer a stack/heap overwrite in many non-buggy binaries that run as root.


The other article says that this is a privilege escalation:

> 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...


Only if you find an exploitable setuid program.


Yes, it's a kernel deficiency which only allows taking over userspace processes and only if you are able to cause them to allocate the right memory and write the right data to it.


> So the word "kernel" is there because the design flaw is in the kernel

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).


Not really. This time it's CPU and kernel failing to provide expected behavior in some corner cases which makes otherwise correct userland programs vulnerable to attacks for no fault of their own.


It is impossible for the CPU (without architectural changes, e.g. stack segment) and kernel to provide expected behavior in the general case. They do not know whether the userland process is trying to use the stack or just access some other mapped region. Indeed, as long as these accesses do not land upon unmapped space (or space mapped with permissions that conflict with the access), the kernel doesn't have any idea what's going on.


Then why is nobody trying to fix the vulnerability at the source ie. userland process?


There exists a fix for the vulnerability at the source, namely, a compiler feature that will add extra code that will "touch" every 4k page of the stack if large amount is allocated.

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.


Can't the changes be gradually introduced? We change distros every year or reinstall the OS every time a new version of the distro is released. That's a newly compiled set of executables that are being installed every year.

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)


It could be limited to only programs that are setuid.


This applies generally to any program running as root (or a user with other privileges than yourself) if you can find a way to allocate a large enough segment of memory.


So setuid programs and network servers, for example.


To butcher an LWN quote, "there are potentially millions of buggy programs, but only one kernel". It's a measure to prevent a class of exploit, rather than depending on people writing safe C code (which I believe decades of software engineering have proven we are collectively incapable of doing).


This has nothing to do with the (un)safety of C code. The programs do not have to be buggy at the source level. The compiler is the bug.


To do that, you need to have a compiler that has the right features, and then you need to recompile all your software.


Ok so if I have an application that makes a large stack allocation, it's safe as long as that memory is read/written at least once?


Only if it's accessed at least once in each page (4K), AIUI.


Right. If I simply zero the memory with `memset` then I guess it should be ok.


Unless the compiler decides that zeroing the memory doesn't do anything useful and optimizes it out.


Initializing your stack buffer probably does count as something useful, but in case it doesn't, try memset_s(3).


No: See http://en.cppreference.com/w/c/string/byte/memset#Notes -- if you don't read from it, the call may be optimized away.


Like my brethren say it's probably a good idea to use a "secure" memset that won't have an accident ^W ^W ^W be optimized out.


Or keep >4kB objects off stack.


I mean, that's probably a good practice. In my case I sort of had this idea that largish objects should be placed on the heap but I couldn't really figure out a good reason why, or what the limit should be, so I guess this vuln proposes both a reason and that the page limit is a good threshold.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: