A couple of months ago, Rafal Wojtczuk came up with a serious exploit that allows you to get superuser rights from an unprivileged process that has access to an X server (that is, from a GUI application running as a regular user). In other words, any GUI program (for example, a reader of PDF files), if it is compromised (for example, with a specially prepared PDF file), can break through all the security barriers to full computer ownership. Even the SElinux sandbox (
SElinux "sandbox -X" ) does not save. And the problem exists for many years - apparently, from the first versions of the 2.6 kernel.
The review of this vulnerability was released on August 17 in
[2] , and I want to tell about it in some places in a simplified, sometimes in an expanded form.
How it works
The main reason for this outrage is known for quite some time
[3] . The problem - especially the Linux memory allocation algorithms. As everyone knows, there are two main places where a process can get a piece of dynamic memory — a stack and a heap. And you can also allocate a piece of address space to which physical memory does not correspond - for this there is a virtual memory map of the process to files in the file system (mmap) or simply to abstract kernel objects (System-V style shared memory). It is not even necessary that the allocated piece of memory physically existed, at least until the moment when we begin to write in it.
')

Memory allocation on x86-32, picture from [3]. On x86-64, everything is very similar, only the scale is bigger :)
A stack is the easiest way to allocate memory. As it grows, it grows from older addresses to younger ones, and if it suddenly turns out that the next page of the stack is over, when trying to write a processor, it throws the page fault, and the kernel allocates several more pages to the stack (or kills the process if it fails to select) . Initially, a stack of memory is allocated (on x86-32) in the upper part of the address space, by default 128 MB, but in reality this memory is allocated as needed, and if suddenly everything else is busy, malloc () and mmap () begin to use Stack memory
As a result of aggressive allocation, it may turn out that one of the selected areas closely adjoins the lower boundary of the stack (this is an exploited property). Suppose now that we have called some deeply recursive function. The stack pointer begins to approach the border, eventually crosses it and ... nothing happens, because immediately after the stack, the memory allocated by us begins. So, we have a part of the stack, which is actually not a stack, but a section allocated in another way.
While everything happened within the boundaries of one process, the maximum that we can do is to spoil this process and do something bad with the user account from which it is launched.
How to break out of your process and get into the X-server
The X server process (that same / usr / X11 / bin / X) works with root privileges. And if we find a way to execute our code in the context of this process, we will achieve our intended goal.
To get a piece of memory in the address space of the X-server, we will use the
MIT-SHM extension. In general, the protocol by which the X-server is exchanged with client programs is large and spreading, and contains numerous extensions invented at different times for different purposes. In particular, MIT-SHM solves a very simple problem. Suppose we need to display a large raster image on the screen. To do this, you must somehow send from the client process to the server a lot of bytes that make up this raster (pixmap). But if the client and server live on the same machine, it is not necessary to spend resources on copying all these bytes into the kernel and back, instead, you can allocate a section of shared memory that will be mapped to the virtual memory of both processes - this is what MIT-SHM does.
We can insidiously use this API for our own purposes and map all available X server memory into our address space like this:
shm_seg_size=shmmax
while shm_seg_size >= PAGE_SIZE
shm_seg=shmget(..., shm_seg_size,...)
XShmAttach(..., shm_seg...)
, shm_seg_size/=2
(shmmax is the maximum possible memory size that can be allocated to shmget, see / proc / sys / kernel / shmmax)
If everything is good, one of the selected areas will be exactly outside the stack space, but we still do not know which one, since the addresses of the shared memory on the server side are not at all like ours. To find a tidbit, we can pile up the pyramid from widgets using conventional X-protocol tools, and then send a command that will cause a recursive walk (in article [2] it’s about some function F that works recursively, you can probably find it by X source;)). As a result, the process will come out of the stack, but no one will notice anything, since the following addresses are already allocated to XShmAttach. And then for our part, we will look through the memory and find a section in which there are non-zero bytes (shmget clogs the selected areas with zeros).
And now let us once again run the notorious recursive function F, and while it works, we will write into the stack what we like, that is, new return addresses. Here you can use classic technologies of exploits, such as
return-to-libc , the main thing is to change the stack in time while the function is executed (yes, I know about
ASLR , but in this article we will assume that we somehow won it;) )
And now what i can do?
Well, first, you can wait for updates. Linus Torvalds (personally!)
Added a patch that creates a barrier from an unallocated page between the stack and the address space available to the heap and mmap. As a result, if there is not enough memory for the stack, the program will try to write to a non-existent address and fall into SIGBUS (which, of course, is also not very good, but is clearly better than privilege escalation). In kernels 2.6.35.2 and 2.6.34.4 this fix has already been added.
If for some reason you don’t want to change the kernel, you can turn off MIT-SHM in the xorg.conf file:
Section "Extensions"
Option "MIT-SHM" "disable"
EndSection
True, it does not save from other likely variations on the same vulnerability.
Links
- [1] The news on The Invisible Things Lab's blog - in fact, the introduction was written based on
- [2] Exploiting large-scale memory management options for Linux by Rafal Wojtczuk ( PDF )
- [3] Gael Delalleu, Large memory management vulnerabilities ( PDF ) provides an excellent overview of known memory vulnerabilities in various operating systems.