|
|
Subscribe / Log in / New account

Attacking hardened Linux systems with kernel JIT spraying

The "main is usually a function" blog has a discussion on the use of "Jit spraying" techniques to attack the kernel, even when features like supervisor-mode execution prevention are turned on. "JIT spraying is a viable tactic when we (the attacker) control the input to a just-in-time compiler. The JIT will write into executable memory on our behalf, and we have some control over what it writes. Of course, a JIT compiling untrusted code will be careful with what instructions it produces. The trick of JIT spraying is that seemingly innocuous instructions can be trouble when looked at another way."

to post comments

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 18:19 UTC (Sun) by patrick_g (subscriber, #44470) [Link] (7 responses)

> PaX's KERNEXEC feature implements in software a policy very similar to SMEP. And indeed, the JIT spray exploit succeeds where a traditional jump-to-userspace fails. (grsecurity has other features that would mitigate this attack, like the ability to lock out users who oops the kernel.)

Does it mean a PaX hardened kernel is **more** vulnerable than a mainline kernel (with BPF JIT disabled)?

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 18:36 UTC (Sun) by spender (guest, #23067) [Link] (3 responses)

That's not what it means. For more information, please see:
http://en.wikipedia.org/wiki/Reading_comprehension

PS: at the risk of making the kernel even more vulnerable, please see the following:
http://grsecurity.net/~spender/jit_prot.diff

-Brad

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 19:44 UTC (Sun) by patrick_g (subscriber, #44470) [Link] (1 responses)

> That's not what it means. For more information, please see:
> http://en.wikipedia.org/wiki/Reading_comprehension

Thanks. Your usual condescending tone.
I'm not a native english speaker so perhaps you could explain more thoroughly why I'm wrong? According to the article, BPF JIT is disabled by distributions so the JIT spraying attack cannot work. Concerning PaX's KERNEXEC the author wrote "JIT spray exploit succeeds" so I wrongly thought it was a weakness in PaX.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 18:40 UTC (Mon) by iabervon (subscriber, #722) [Link]

This attack succeeds on PaX "where a traditional jump-to-userspace fails"; on mainline, the traditional jump-to-userspace succeeds, so JIT spraying is unnecessary.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 23:08 UTC (Sun) by NightMonkey (subscriber, #23051) [Link]

spender/Brad, you might be right, but lets keep LWN free of lame put-downs. Keep it civil, please. Thanks.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 19:02 UTC (Sun) by robert_s (subscriber, #42402) [Link]

No - that's not what it means at all. This article is _not_ saying SMEP makes anyone any less secure, it's just saying it's quite easy to work around in certain circumstances.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 23:45 UTC (Sun) by yann.morin.1998 (guest, #54333) [Link]

> > PaX's KERNEXEC feature implements in software a policy very similar to SMEP. And indeed, the JIT spray exploit succeeds where a traditional jump-to-userspace fails. (grsecurity has other features that would mitigate this attack, like the ability to lock out users who oops the kernel.)

> Does it mean a PaX hardened kernel is **more** vulnerable than a mainline kernel (with BPF JIT disabled)?

What I understood (not being a native english speaker either, as you know ;-) ) is:

- JIT disabled: no issue, as it's not even possible to attack the JIT, it being disabled
- JIT enabled, with PaX' KERNEXEC: JIT was successfully subverted
- JIT enabled, with SMEP: unknown, but probably similar to PaX' KERNEXEC, as the thechnique is the same

Hop,
Me.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 16:56 UTC (Mon) by randomguy3 (subscriber, #71063) [Link]

A PaX hardened kernel with JIT disabled is (probably) more secure than mainline (in this regard).

A PaX hardened kernel with JIT enabled is (probably) just as insecure as mainline (in this regard). This is because the JIT can be used to work around the extra security that PaX provides.

A PaX hardened kernel is (probably) never less secure than mainline (in this regard).

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 20:12 UTC (Sun) by cyanit (guest, #86671) [Link] (18 responses)

Doesn't all this talk of "you know that guy who can write arbitrarily to kernel memory? It's unbelievable, but he can pwn the b0x! OMGOMGOMG" sound ridiculous?

Perhaps we should stop people from being able to write to kernel memory in the first place, no?

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 20:24 UTC (Sun) by deepfire (guest, #26138) [Link] (17 responses)

Do you have ideas on how to accomplish this?

Do you argue against mechanisms to prevent exploitation until we do?

On what grounds?

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 11:37 UTC (Mon) by cyanit (guest, #86671) [Link] (16 responses)

Make the build fail unless the C code can be statically proved to have at least the same invariants that Java provides, plus ideally being deadlock-free, providing real-time response if desired, and other goodies.

Switch to an augmented version of the C language with annotations to help the theorem prover or to another existing language if it turns out to be necessary to be able to prove correctness.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 13:24 UTC (Mon) by Wol (subscriber, #4433) [Link] (12 responses)

lol lol lol !!!

Good luck with ACHIEVING that!

It's a nice idea, but I guess the proof would take longer to run than the heat death of the universe, that is, if the proof itself doesn't contain bugs ...

The problem, as you seem to have missed, is that the kernel needs to be able to write to kernel memory ...

Cheers,
Wol

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 13:41 UTC (Mon) by cyanit (guest, #86671) [Link] (11 responses)

Well, if you wrote the kernel in a type-safe garbage-collected language (e.g. Java), then the static proof would be trivial since it is by construction impossible to violate the language invariants (assuming the VM and low-level support code is correct).

So, that's the solution if everything else fails.

Without garbage collection, you can still prove (with help from the programmer if needed) that everything is either reference counted with no cycles, pointed to by a single pointer with lifetime tied to the contained structure, or otherwise provide a proof that it is correctly handled.

Without full type safety, you can still prove, for instance, that memcpy only writes the (dst, dst + size) and that since dst points to an array of size, it is safe, and so on for more complex stuff.

The real reason is that apparently nobody cares enough to do the work.

Annotating the kernel to prevent exploits

Posted Nov 19, 2012 14:39 UTC (Mon) by ebiederm (subscriber, #35028) [Link] (5 responses)

A version of l4 has been proven so it is definitely possible.

The techniques and tools are not yet fully mature. So I would say that is a little more than lack of people caring that has not seen this happen. Especially when you are talking such a large code base.

I was playing with using frama-c which reportedly is one of the better frame works for connecting C code and a prover and it could not handle the C99 of my toy test program. Getting a tool like that to run successfully on the kernel source base with no annotations looks to be a significant undertaking.

Not that this problem is unique to tools like frama-c. Even the clang front end to llvm (an actual production c compiler) has has trouble building the linux kernel.

Annotating the kernel to prevent exploits

Posted Nov 19, 2012 22:39 UTC (Mon) by vonbrand (subscriber, #4458) [Link] (4 responses)

The seL4 is a microkernel, of which some 7500 lines of C code have been verified rigurously (assuming the compiler and the underlying machine are correct). That is many, many orders of magnitude away from even the most spartan Linux configuration.

state of the art in formal proofs of kernels

Posted Nov 20, 2012 4:26 UTC (Tue) by pjm (guest, #2080) [Link] (3 responses)

Even then, so far they only claim to have proven [subject to questionable assumptions such as the compiler conforming to the formalization that they've written themselves] that their C implementation has the same behaviour (and hence same set of bugs) as their implementation in a higher-level language. They haven't claimed to have proven anything (else) about the behaviour of that higher-level-language implementation. So for example, even if the seL4 microkernel contained a JIT compiler, they wouldn't have proven anything about the output of that compiler (to which the kernel presumably passes control while in kernel mode).

OTOH, that program equivalence would certainly reduce the opportunities for exploits (e.g. by ruling out any buffer overflows that don't occur in the higher-level language), and at least it's a bit easier to prove properties of code in a higher-level language than a lower-level one. Mathematical proofs increase confidence, but there's always a gap between a mathematical model and the real world.

state of the art in formal proofs of kernels

Posted Nov 20, 2012 11:45 UTC (Tue) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

There's research towards proving that compiler output is correct by using type-annotated assembly language. It might be actually possible to check whether high-level invariants are not mis-translated by the compiler.

The next frontier is to prove that hardware itself is correct :)

state of the art in formal proofs of kernels

Posted Nov 20, 2012 13:33 UTC (Tue) by ebiederm (subscriber, #35028) [Link] (1 responses)

There is compcert a formally proven C compiler written in coq.

Hardware design developed formal method for their logic ALUs and FPUs a long time ago. Although that clearly has it's limits. Especially timing.

The next frontier is for program proofs to stop being news and instead partial program proofs increasing program reliability to the point where any program updates except for features become news.

How we go from proofs of concept to useful proof tools is a question I don't yet see answers to.

state of the art in formal proofs of kernels

Posted Nov 20, 2012 19:14 UTC (Tue) by dlang (guest, #313) [Link]

> ...increasing program reliability to the point where any program updates except for features become news.

Given that people don't even bother to define what acceptable input is, I don't expect this to ever happen.

Not to mention that this would require anticipating all possible internal state, another thing that is not going to happen.

And then you need to have someone think through what should happen in all these combinations of cases, and not have any logic errors in what the 'proofs' are trying to show.

> Hardware design developed formal method for their logic ALUs and FPUs a long time ago. Although that clearly has it's limits. Especially timing.

And when Timing issues dominate, the 'correctness' generated by such proofs is pretty meaningless.

Math is not reality, they sometimes have a resemblance to each other, but that's just a happy coincidence.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 16:00 UTC (Mon) by khim (subscriber, #9252) [Link] (3 responses)

Well, if you wrote the kernel in a type-safe garbage-collected language (e.g. Java), then the static proof would be trivial since it is by construction impossible to violate the language invariants (assuming the VM and low-level support code is correct).

Unfortunately this small addition at the end makes the whole thing useless: simple interpreter mode for languages like Java are too slow and thus "VM and low-level support code" is typically comparable in complexity to OS kernel (in some sense it is an OS kernel).

The real reason is that apparently nobody cares enough to do the work.

No. The real reason is that it takes time and does not pay.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 16:40 UTC (Mon) by drag (guest, #31333) [Link] (2 responses)

It's better to have buggy code that works now then it is to have something that may work in 30 years.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 20, 2012 3:07 UTC (Tue) by liam (guest, #84133) [Link] (1 responses)

Is there any reason why this work couldn't be done it parallel with kernel development?
There are parts of the kernel that are only touched rarely (ex. block/deadline-iosched, though there are probably better examples).
It seems as though this type of analysis might be good long term project.
Of course, if it is truly infeasible for any but the least interesting parts of the kernel then it is a waste of time.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 20, 2012 19:15 UTC (Tue) by mathstuf (subscriber, #69389) [Link]

> Of course, if it is truly infeasible for any but the least interesting parts of the kernel then it is a waste of time.

I suppose that since there's a high chance of it happening, the question boils down to: "What wins when the decision is between ABI compatibility and provably secure?"

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 17:22 UTC (Mon) by NAR (subscriber, #1313) [Link]

Don't you think that assuming the VM and low-level support code is correct is a little too strong precondition?

The real reason is that apparently nobody cares enough to do the work.

I remember that back at the university proving that even a very simple concurrent program is correct took 30-40 minutes. And that model did not have shared memory or integer overflows...

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 20, 2012 8:55 UTC (Tue) by cmccabe (guest, #60281) [Link] (2 responses)

Have you been frozen in a cave for a few years? Java has been the subject of a bunch of zero-day vulnerabilities lately.

By all means, continue burbling on about the magical, deadlock-free, realtime, garbage collected in kernel space, 1000 miles-per-gallon programming language, but at least try to pretend that you read the article and/or recent news.

It's also funny that you're advocating using a (presumably JITed) garbage collected programming language in the kernel, and this vulnerability exploits the BPF JIT.

Being unfairly fair

Posted Nov 20, 2012 11:56 UTC (Tue) by man_ls (guest, #15091) [Link] (1 responses)

To be fair, this vulnerability exploits a combination of JIT and direct execution. If all kernel code was JITted with the same VM, then this kind of attack would be useless.

To be even fairer, to the point of unfairness, Java may have had vulnerabilities e.g. in executing protected code; but no buffer overflows. In C, every time a pointer is not checked for null before jumping, or an array index is not checked to be within bounds, there is an opportunity for a security vulnerability. I would trade 1000s of vulnerabilities for a handful any time, if it was even feasible to run a kernel in a VM.

In real life a kernel cannot run in a VM because it would need a kernel to run the VM -- or the VM would become the kernel. This is the way of the microkernel, which is slow. Embedding a VM inside another VM has no advantages and only slows things down even more.

On the other hand there is no reason why a kernel cannot be written in an object-oriented, reference-counted language. I have been thinking for a long time that it would be a worthwhile project, but for some reason have not found the time to do it in my spare time. Perhaps Golang would be a worthwhile instrument for the task.

a kernel cannot run in a VM

Posted Nov 20, 2012 21:46 UTC (Tue) by Wol (subscriber, #4433) [Link]

That was my immediate reaction.

Sorry to say it, but cyanit doesn't seem to understand the difference between a kernel and a VM.

A VM provides a *virtual* computer so that the programs don't need to give a fig what the real hardware is.

A kernel must interface directly with the hardware and cannot afford to ignore any figs.

Running a kernel in a VM is likely to vanish in a puff of smoke as it gets lost in a mobius loop!

Cheers,
Wol

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 20:41 UTC (Sun) by dilinger (subscriber, #2867) [Link]

The end of the post has relevant information, for those concerned about the practicality of such an attack (and not so much the details):

"I'll admit that this is mostly a curiosity, for two reasons:

SMEP is not widely deployed yet.
The BPF JIT is disabled by default, and distributions don't enable it.
Unless Intel abandons SMEP in subsequent processors, it will be widespread within a few years. It's less clear that the BPF JIT will ever catch on as a default configuration. But I'll note in passing that Linux is now using BPF programs for process sandboxing as well."

Also:

"I don't have a CPU with SMEP, but I did try a grsecurity / PaX hardened kernel. PaX's KERNEXEC feature implements3 in software a policy very similar to SMEP. And indeed, the JIT spray exploit succeeds where a traditional jump-to-userspace fails. (grsecurity has other features that would mitigate this attack, like the ability to lock out users who oops the kernel.)"

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 18, 2012 23:36 UTC (Sun) by bersl2 (guest, #34928) [Link] (5 responses)

It does sound like a win for !X86, though, since it requires(?) non aligned instructions.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 18:19 UTC (Mon) by farnz (subscriber, #17727) [Link] (4 responses)

I wouldn't bet on it being impossible if you required aligned instructions, personally. There are some very bright people working in the field of exploiting bugs, and while it may be easier to demonstrate with x86's variable length unaligned instructions, those people will be looking for a way to make it fail on other architectures of interest.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 19:53 UTC (Mon) by nybble41 (subscriber, #55106) [Link] (3 responses)

The real problem is being able to branch in to the middle of a JIT module. It should only be possible to call the beginning of a top-level function.

One way to ensure that (though it may come with unacceptable overhead) would be to initially mark the pages containing the JIT code as non-executable, and check the exact address causing the exception whenever something branches to it. The page would need to be marked non-executable again when the JIT code is finished, and there would be a small window of opportunity while the code is executing.

The other option, of course, is to run JIT code in one or more dedicated, high-priority user-mode threads rather than calling it directly from kernel mode. Naturally, this would add the overhead of two context switches to each JIT call, which may also be unacceptable.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 20:08 UTC (Mon) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

Page faults are WAY too costly for that.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 20:51 UTC (Mon) by nybble41 (subscriber, #55106) [Link] (1 responses)

> Page faults are WAY too costly for that.

Understood, though surely it must depend on what the JIT module is being used for? Page fault can't be _too_ costly given that they're used for copy-on-write, stable pages, lazy initialization, certain forms of I/O mapping, and IIRC some forms of system call, but of course the whole point of using interpreted byte code or a JIT in kernel mode is to avoid as much overhead as possible.

As an alternative to running a full userspace helper process, a page fault would probably be acceptable. As an alternative to waking up the application to do it's own packet filtering, as in BPF, you're probably right that it's too high.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 20:54 UTC (Mon) by dlang (guest, #313) [Link]

in the case of copy-on-write and lazy initialization, the page fault is accepted because of two things.

1. the work that needs to be done is significant enough that the page fault cost is relatively small in comparison.

2. In a very large percentage of cases, the page fault never happens, and so both the page fault and the initialization are avoided.

In the case of JIT modules, the page fault is expensive compared to the work being done, and the probability of triggering the page fault is high.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 2:07 UTC (Mon) by alison (subscriber, #63752) [Link] (8 responses)

The particular exploit works on x86 with a JIT for packet-filtering. Would a similar flooding scheme work with the Renderscript JIT that Android uses for graphics? Could an attacker flood memory with shaders or overlays or similar by sending instructions to Renderscript?

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 2:36 UTC (Mon) by aliguori (subscriber, #30636) [Link] (7 responses)

It's not an exploit actually.

It presumes that you have found *another* explicit that allows you to set the RIP while in kernel mode to an arbitrary address. To demonstrate this, they created a kernel module that let userspace do this. They effectively created their own exploit.

SMEP is a new x86 feature that improves security by preventing the kernel from writing to userspace address spaces that it doesn't explicitly allow itself to write to. Kernel exploits typically rely on redirecting RIP to a userspace address since you can easily put your target exploit code in userspace.

This blog post points out that JITs allow userspace to generate executable kernel space areas that could be used by future exploits to get around SMEP.

SMEP support in Linux is still very new. The hardware won't be out for a long time. I suspect there's clever ways to work around this sort of problem. There's no doubt though that SMEP improves security though as generating uploading BPF routines is certainly a privileged (and restricted) capability.

I don't know what the JIT you refer to is, but if it's in userspace, it's totally unrelated to what's discussed here. If it's a kernelspace JIT, then it's likely the same trick could be done.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 2:51 UTC (Mon) by alison (subscriber, #63752) [Link] (1 responses)

What I'm wondering is not about kernel exploits per se, but whether a DOS could mounted against a platform device that has no kernel-user memory separation. In other words, might a graphics JIT like Renderscript bring down the GPU or other graphics IP cores?

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 3:03 UTC (Mon) by aliguori (subscriber, #30636) [Link]

Wouldn't that be a bug in the JIT?

There's no DOS happening here. The "spraying" part of this is just duplicating the BPF program as many times as possible in order to increase the likelihood of guessing a valid kernel address of the executable. Duplicating the JIT'd code is completely valid behavior.

Making sure JITs use bounded amounts of memory, don't generate infinite loops, etc. is pretty standard stuff.

From what I can tell, Renderscript is completely userspace. I would hope it doesn't allow for malicious generate GPU routines... That would be a pretty serious oversight.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 5:35 UTC (Mon) by Arach (guest, #58847) [Link]

> SMEP is a new x86 feature that improves security by preventing the kernel
> from writing to userspace

Actually, only from executing native code from userspace. What also prevents read/write access is SMAP: https://lwn.net/Articles/517475/

SMEP prevents execution.

Posted Nov 19, 2012 5:40 UTC (Mon) by gmatht (guest, #58961) [Link]

Unless I am missing something, Supervisor Mode Execution Protection (SMEP) only prevents execution from (not writing to) user space addresses.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 9:17 UTC (Mon) by deepfire (guest, #26138) [Link]

> SMEP support in Linux is still very new. The hardware won't be out for a long time.

Actually, Ivy Bridge CPUs have been widely deployed for quite some time already, if we're talking about SMEP -- execution prevention.

It is true, though, that /SMAP/ deployment will have to wait until Haswell ships.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 13:23 UTC (Mon) by nix (subscriber, #2304) [Link]

There's no doubt though that SMEP improves security though as generating uploading BPF routines is certainly a privileged (and restricted) capability.
In the presence of BPF seccomp filters, not as privileged and restricted as all that.

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 15:22 UTC (Mon) by arjan (subscriber, #36785) [Link]

SMEP is shipping in many many systems with Intel Ivybridge CPUs since earlier this year..... and it's on by default on Linux.

SMEP has one objective, and it meets that. It is not, and nobody every claims it is, the be all and end all of preventing kernel exploits.
Nothing is.

The road to a more secure system is one of many small steps... both on the "prevent bugs from happening via tools" side as well as the "make it harder to turn a bug into a successful exploit" side.
You need both sides to succeed...

Attacking hardened Linux systems with kernel JIT spraying

Posted Nov 19, 2012 13:03 UTC (Mon) by vonbrand (subscriber, #4458) [Link]

I believe the trick (mentioned in passing) allowing to have thousands of sockets (with associated BPF filters et al) "in flight" is a more serious issue than some trick allowing to write known executable code into the kernel space that has to be triggered somehow. The former is available everywhere, the later only with a very uncommon configuration and requires a kernel exploit to use anyway.


Copyright © 2012, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds