Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Posted Oct 29, 2015 10:45 UTC (Thu) by paulj (subscriber, #341)Parent article: Kernel security: beyond bug fixing
I know that'd be difficult to do for user-space - it'd be a new ABI - but the kernel is much less constrained here. Is it viable? What am I missing?
Posted Oct 29, 2015 11:07 UTC (Thu)
by dunlapg (guest, #57764)
[Link] (29 responses)
But even if that could be arranged, it wouldn't actually help, because now the return address for the *parent* is in front of your buffer; you can overwrite that one instead.
Posted Oct 29, 2015 11:24 UTC (Thu)
by hummassa (subscriber, #307)
[Link] (3 responses)
Posted Oct 29, 2015 17:39 UTC (Thu)
by Nahor (subscriber, #51583)
[Link] (2 responses)
Posted Oct 30, 2015 4:14 UTC (Fri)
by pbonzini (subscriber, #60935)
[Link] (1 responses)
Posted Oct 31, 2015 16:33 UTC (Sat)
by mathstuf (subscriber, #69389)
[Link]
Posted Oct 29, 2015 11:27 UTC (Thu)
by paulj (subscriber, #341)
[Link] (20 responses)
Yeah, the stack frames grow down, and ebp is saved to the stack first, so overflows of the local vars can write to it. Why not have the compiler create frame-generation code that first allocates the local var space and /then/ pushes the base pointer to the stack? So ebp is at a lower address and can't be written to by local var overflows?
(No doubt there's a reason why this isn't possible, I'm just curious what it is).
Posted Oct 29, 2015 12:42 UTC (Thu)
by Paf (subscriber, #91811)
[Link]
Posted Oct 29, 2015 23:31 UTC (Thu)
by Gollum (guest, #25237)
[Link] (18 responses)
Defining a stack that grows upwards seems like one reasonable approach to deal with this problem. Perhaps swapping the heap (which currently grows upwards, I believe?) with the stack (which grows downwards) is the answer?
Or would that simply swap one set of problems (stack-based overflows) for a new version of heap-based overflows, where a heap overflow overwrites the heap metadata?
Posted Oct 30, 2015 14:01 UTC (Fri)
by dunlapg (guest, #57764)
[Link] (17 responses)
Right now, if you call foo() which calls bar() which calls zot(), you get:
So if you can overflow a zot local variable, you can overwrite the zot->bar return address.
Now suppose we switch it. What do we get?
So now if you can overflow a zot local variable, you can't overwrite the zot->bar return address, but you can still overwrite the bar->foo address.
Changing the direction of the stacks won't help.
Posted Oct 30, 2015 18:04 UTC (Fri)
by Gollum (guest, #25237)
[Link] (16 responses)
[zot local variables]
So, if you are in zot, and you overflow a zot local variable, you write into unused space, and don't "overwrite" anything at all. That is, because zot->bar return address is at a lower address than the zot local variables, and overflows write "upwards" using incrementing addresses, the opportunity to overwrite return addresses is eliminated.
It doesn't protect the rest of the zot local variables, of course, so overwriting something otherwise uncontrollable by yourself may result in a successful comparison when previously it would have been unsuccessful.
And as I say, changing the heap to grow downwards may simply be changing one set of problems for another.
Posted Nov 4, 2015 1:54 UTC (Wed)
by ploxiln (subscriber, #58395)
[Link] (15 responses)
foo() {
So if bar could overflow a buffer in its stack frame, or in foo's stack frame, no matter how you arrange them, one of them could hit the return address.
Posted Nov 4, 2015 12:30 UTC (Wed)
by renox (guest, #23785)
[Link] (14 responses)
"No matter how you arrange them" is not correct: if you have separated variable and address stack, you cannot use a buffer overflow to override a return address.
Posted Nov 4, 2015 23:47 UTC (Wed)
by PaXTeam (guest, #24616)
[Link] (12 responses)
Posted Nov 5, 2015 9:31 UTC (Thu)
by renox (guest, #23785)
[Link] (11 responses)
Can you explain it again or do you have a link with an article explaining how it could work?
Posted Nov 5, 2015 10:36 UTC (Thu)
by PaXTeam (guest, #24616)
[Link] (10 responses)
assume the attacker controls the data behind 'in' and that the memcpy overwrites both 'p' and 's' on the stack, the last line will then be able to write anything anywhere. in short, there are many ways a memory corruption bug can be exploited, overwriting the return address of the current frame is just one and perhaps the most popularized textbook example but by far not the only way. this is the reason why having a proper threat model helps avoiding mistakes in devising defenses.
Posted Nov 5, 2015 10:45 UTC (Thu)
by renox (guest, #23785)
[Link] (9 responses)
Posted Nov 5, 2015 11:25 UTC (Thu)
by PaXTeam (guest, #24616)
[Link] (8 responses)
Posted Nov 5, 2015 12:25 UTC (Thu)
by renox (guest, #23785)
[Link] (1 responses)
The 'arbitrary write' can overwrite the return address only if the address of the return address is known, which can be quite difficult if there is randomisation.
Also for the Mill CPU(unfortunately paperware only currently) I think that the separated address stack is managed directly by the CPU, so an 'arbitrary write' cannot overwrite a return address.
Posted Nov 5, 2015 12:46 UTC (Thu)
by PaXTeam (guest, #24616)
[Link]
Posted Nov 5, 2015 12:28 UTC (Thu)
by hummassa (subscriber, #307)
[Link] (5 responses)
Posted Nov 5, 2015 12:52 UTC (Thu)
by PaXTeam (guest, #24616)
[Link] (4 responses)
Posted Nov 10, 2015 16:39 UTC (Tue)
by hummassa (subscriber, #307)
[Link] (3 responses)
Posted Nov 10, 2015 17:29 UTC (Tue)
by PaXTeam (guest, #24616)
[Link] (2 responses)
Posted Nov 24, 2015 13:43 UTC (Tue)
by hummassa (subscriber, #307)
[Link] (1 responses)
Posted Nov 24, 2015 16:25 UTC (Tue)
by PaXTeam (guest, #24616)
[Link]
Posted Nov 6, 2015 3:38 UTC (Fri)
by ploxiln (subscriber, #58395)
[Link]
Posted Oct 30, 2015 13:28 UTC (Fri)
by mm7323 (subscriber, #87386)
[Link] (3 responses)
However, while such an ABI might make buffer overruns a little harder to exploit, because the overrun would generally be into the unused stack space, but I don't think it solves the problem; underuns or malicious code can still find return addresses in predictable read/write memory locations on the stack.
Posted Oct 30, 2015 13:54 UTC (Fri)
by cladisch (✭ supporter ✭, #50193)
[Link] (2 responses)
Posted Oct 30, 2015 14:32 UTC (Fri)
by mm7323 (subscriber, #87386)
[Link] (1 responses)
Is that an x86 thing?
On ARM, there are some shadow registers that backup the PC and the processor doesn't touch the stack itself - and rightly so! It's most efficient for the interrupt handler writer to decide what state needs to be saved and restored, particularly if the interrupt routine isn't going to do very much.
If a CPU did automatically push something on IRQ entry, you could still engineer an ABI that uses an 'empty ascending' stack where the stack pointer is maintained to point to the first unused word at the stack top.
I'm pretty sure you could run an ascending stack on ARM, probably other architectures too, but it would be for limited security benefits so moot.
Posted Oct 30, 2015 15:28 UTC (Fri)
by cladisch (✭ supporter ✭, #50193)
[Link]
ARM is pretty much the only architecture where software can choose the stack direction. There are many other architectures with optimized interrupt handling, but they do not have the same flexibility for normal function calls.
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
[foo local variables]
[bar -> foo return address]
[bar local variables]
[zot -> bar return address]
[zot local variables]
[foo local variables]
[bar local variables]
[bar -> foo return address]
[zot local variables]
[zot -> bar return address]
Kernel security: beyond bug fixing
[zot -> bar return address]
[bar local variables]
[bar -> foo return address]
[foo local variables]
Kernel security: beyond bug fixing
char buf[16];
bar(buf, 16);
...
}
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Thanks.
Kernel security: beyond bug fixing
{
long *p;
char out[8];
memcpy(out, in, 1024);
*p = s;
}
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing
Kernel security: beyond bug fixing