Proactive Security Roadmap

Status

Introduction

Establish a strategy for implementing proactive security features in Ubuntu.

Rationale

We want to reduce the risk of security holes in Ubuntu systems by reducing the number of potential attack vectors and find general solutions for preventing common classes of vulnerabilities. This confines the impact of actual vulnerabilities to a minimum.

Visuals: Normal vs. Protected (protections: address randomization; position independent executables; memory protection policy; stack smash protector)

Scope and Use Cases

Implementation Plan

See also ProactiveSecurityRoadmap1

Data Preservation and Migration

Does not apply here.

Packages Affected

Kernel:

prelink:

gcc:

dhcp3-client:

dhcp3-server:

inetd:

unix_chkpwd:

Outstanding Issues

Desirable for the future:

Comments

ColinWatson: I think other-user-process hiding should be off by default; when turned on, it requires the system administrator to use extra privilege to inspect the state of the system, which is not necessarily an obvious security win.

JohnMoser: Colin, consider that most users don't need to know all that. A 'sudo ps -e' is harmless unless the user running sudo has his account compromised, in which case running sudo could run a trojan that runs sudo to get itself root and (for stealth) execute your command. Consider what I just said: accounts with sudo access are basically root accounts with the stupidity guard on, they won't protect you from malware.

FurryBall: I would like to comment on the SSP and userland protection part. In overall there are more effective and secure ways to achieve this - doing it by the kernelland governing the userland. Mentioning PAX and Exec_shield. I'd say do the task once and focus on doing it better there..

JohnMoser: Furry, PaX and Exec Shield do not do direct stack smash protection, they do general memory corruption trap-and-kill. Also in some cases you still get an executable stack, or you have low entropy on address space randomization and a 216 second brute force on apache. In these cases, you have 32 orders of magnitude more work to do to break the SSP canary. Sorry, but a format string bug will quickly defeat this; we're still 4 billion times better protected against vanilla buffer overflows on the stack.

JohnMoser: Furry, you should also consider that ProPolice/SSP will re-order local variables and place passed arguments into local variables when entering a function. This protects local variables and passed arguments (i.e. pointers) from short buffer overflows in ways the kernel could never hope to. The only damage a buffer overflow can do before we detect it is to other stack frames from the calling function chain.

JohnMoser: An interesting hack would possibly be to combine ProPolice/SSP — protection of everything in the current stack frame — with a very ugly kernel and compiler hack that makes every stack frame start on a new page, and sticks an unmapped page between each stack frame. This would create a situation where buffer overflows on the stack can reliably be prevented from altering program flow before being detected; RETP and SFP damage would be detected before use, passed arguments and local variables are not harmed, and the previous stack frame has a guard page that can't be faulted into. This is of course a major ABI change and total breakage; it's a cute idea, but not viable. Maybe OpenBSD will do it.

JohnMoser: There probably needs to be a team set up to handle this. This stuff isn't a little toy you slap in just because "it helps" or something along those lines; these protections do absolutely nothing unless you can quantify their effects. Saying, "We don't know how often or if it really works, but this protection may or may not stop a buffer overflow," will not tell you anything; when it comes down to it, the only safe assumption is that it's still 100% exploitable. To that end, there needs to be a team to quantify exactly what the protections do and do not do; and to test and evaluate regressions to assure that the protections work as expected. See ProactiveSecurityEffects for some quantification examples.

Need for a Team

We need a team dedicated to managing proactive security in Ubuntu. Proactive security implementation affects packager policies, the kernel, and quality assurance testing. We need a group whose job is to make sure the policies on how to build packages are clear and enforced; make sure the kernel patches are properly applied; and form regression tests to make sure that the protections are actually being applied and doing their job.

Input from JohnMoser

Considering hardened kernels, PaX/GrSecurity vs. Exec Shield is a major argument. Exec Shield provides a very nice level of protection; while PaX provides a stricter but more secure environment. Exec Shield has wide testing in Fedora Core and RHEL, and thus is an easier near-target. See PaXvExecShield for a dedicated wiki entry comparing the two.

Firstly, be aware that Exec Shield does not provide a real NX-bit. A split is made where above is executable and below is non-executable; the highest PROT_EXEC page is the last executable page. Typically this is a strip of code from a library; the stack thus will be non-executable. The heap and library data and such are all still executable. To correct this, either the CPU needs a real NX bit; the supervisor bit overloading (SBO) technique used in PaX PAGEEXEC emulation need be used to supplement the current method; or a new address space layout is needed.

Most architectures besides IA-32 should have an NX-bit; some IA-32 architectures and IA-32 on x86-64 will have a hardware NX bit. When this is present, it is turned on automatically. This has been a feature in mainline for a short while now, since around 2.6.10.

SBO works rather well in most cases, although it can create a rather steep level of overhead in some rare usage patterns. The stack is spared this by the normal Exec Shield emulation model; PaX adopted this model for PAGEEXEC as an optimization a while back for exactly this reason. Take note that the design of SBO utilizes separate Instruction TLB and Data TLB banks; faults on the K6 architecture use the same TLB, thus SBO cannot be used here. By detecting stepping at boot time, we could disable this before it is used. Failing that, just generate a simple userland process before init starts and use it to test behavior directly.

A new address space layout is possible; however, this would reveal a lot about the layout of our address space. Heap, anonymous mmap(), stack, non-executable file-backed mmap(), and shared memory would all be mapped high; code segments would all be mapped low. The impact on address space layout randomization would have to be evaluated. Also, stray mappings and the vsyscall() pages (mapped higher than the stack) would defeat this; the vsyscall() page was taken care of with Exec Shield, however. OpenBSD uses a special lay-out to achieve these goals.

Exec Shield could be implemented in the default Ubuntu kernel. This works quite well in Fedora/RHEL, and thus has a wide test base. Exec Shield is also controlled by the exec-shield kernel command line; exec-shield=0 turns it off, mimicing IA-32 behavior; while exec-shield=2 defaults it on unless a binary opts out, mimicing behavior on a true NX bit platform. Rather than support kernels with and without Exec Shield, using the boot option would be possible.

If I recall correctly, Fedora Core and RHEL both use a higher order address space layout randomization than stock mainline; but the code is in mainline Linux. We should perhaps look into patching the kernel to use high order entropy by default, more similar to PaX; but also to accept a kernel command line parameter such as stack-aslr=12 and mmap-aslr=16 to adjust this at boot time. Low entropy randomization is easily brute forced in respawning daemons, especially ones which fork() to handle connections: apache is an easy target.

One problem with Exec Shield is it does not implement PaX mprotect() restrictions to enforce true data-code separation. PaX second-guesses the program and makes sure all memory is initialized with only one of PROT_WRITE and PROT_EXEC. This does not typically hinder program execution; however, there may be a few cases. We should look into implementing this as a managable policy to create passive data-code separation.

PaX also has the ability to enforce data-code separation. In this mode, non-executable memory never gains PROT_EXEC; and executable memory may never be granted PROT_WRITE without dropping PROT_EXEC. This gives active data-code separation. This can be disabled in PaX per executable binary or, with SELinux hooks, per process based on security context. This policy control is worth implementing, as it gives enterprise security administrators the ability to enforce fine-grained control over memory protection and keep track of what programs are most vulnerable in the event of a security alert. This leads to prioritizing security patch application.

GrSecurity has a good brute force deterrance scheme, which makes fork() enter a queue that gets one execution per 30 seconds when called if a PaX kill or segmentation fault is encountered in a child fork() process with the same executable image. This causes one fork() to occur every 30 seconds. As expected, this is a hugely visible denial-of-service attack; but hey, we're making broken code safe, it's not going to be pretty so don't try. Obviously this DoS can only occur if an already existing security hole is attacked, so clean programs are unaffected. It is possible to devise a less intrusive model that is almost as effective.

UbuntuDownUnder/BOFs/ProactiveSecurityRoadmap (last edited 2008-08-06 16:18:54 by localhost)