Académique Documents
Professionnel Documents
Culture Documents
(beside SELinux)
Ulrich Drepper
Red Hat, Inc.
drepper@redhat.com
December 9, 2005
Abstract
Bugs in programs are unavoidable. Programs developed using C and C++ are especially in
danger. If bugs cannot be avoided the next best thing is to limit the damage. This article will
show improvements in Red Hat Enterprise Linux which achieve just that.
Introduction
These problems are harder to protect against since normally2 all of the OSs functionality is available and the
attacker might even be able to use self-compiled code.
One way to avoid buffer overow problems is to use controlled runtimes where memory access is rst checked
for validity. This is not part of the standard C and C++
runtime, which means many applications are in danger
of these problems. Intruders can misuse these bugs in a
number of ways:
if the overwritten buffer on the stack is carefully
crafted, overow can cause a naturally occurring
jump to a place the intruder selected by overwriting the original return address. The target of the
return might also be in the data written onto the
stack by the overow;
a pointer variable might be overwritten. Such a
variable, if located in memory in front of the overowed buffer, could then be referenced and maybe
written to. This could allow the intruder to write
a value, which might also be controlled by the intruder, into a specic address;
2 SELinux
changes this.
ers.
int
match (const char *passwd)
{
char tmp[MAXPASSWD];
if (gets (tmp) == NULL)
return 1;
return strcmp (passwd, tmp);
}
The Plan
Exec-Shield
Version 1.6
low
tmp
high
return
address
Version 1.6
By doing all this, only one xed rock is left in the address
space: the executable itself. An executable, as opposed
to DSOs, is linked for a specic address which must be
adhered to, otherwise the code cannot run. Red Hat developed a solution for this problem as well, which will
be addressed in the next section. The executable itself
is a bit special though. for it not only consists of the
usual code and data parts, but it also has the brk area
attached to it. The brk area (aka heap) is a region of
the address space in which the process can allocate new
memory for interfaces like malloc. This area started
traditionally right after the BSS data of the executable
(BSS data is the part of the data segment which holds the
uninitialized data or the data explicitly initialized with
zero). But there never has been any formal specication
for this. And in fact, since almost no program (for good
reasons) uses the brk interfaces directly, user programs
are never exposed to the exact placement of the heap.
Which brings us back to randomization: the Exec-Shield
patch randomizes the heap address as well. A random
sized gap is left between the end of the BSS data and the
start of the heap. This means that objects allocated on
the heap do not have the same address in two runs of the
same program. The change has remarkably little negative
effects. The only known problem is that while building
emacs (not running the nal program) the dumping routines depend on the heap directly following the BSS data.
The workaround is to disable Exec-Shield temporarily.
Implementation Details
In the discussion of Exec-Shield so far, we glossed over
the implementation details. Exec-Shield is a kernel patch
and all the changes happen transparently for the user. But
one detail of the implementation is worth noticing.
The developers of the 80386, the rst implementation of
the IA-32 architecture with protected mode, saved some
precious chip real estate by not implementing the ag
governing execution permission for each memory page
in the virtual address space. Instead, the permission for
read and execution are collapsed into one bit. Without making data unreadable, it is not possible to control
execution this way.
There is a way to control execution, though, but it is convoluted. The segmentation mechanism of the processor
provides a coarse mechanism to control execution. Without the Exec-Shield patch, the segment used for program
execution (selected with the %cs register) stretches the
whole 4GiB and therefore contains the stack and all data.
The Exec-Shield patch changes this. The kernel now
keeps track of the highest address which has been requested to be executable. All addresses from zero to the
highest required address are kept executable. Requests
for executable memory are made exclusively by calls to
mmap or mprotect and the implicitly added mappings
of the program itself and the dynamic linker. This means
the stack is usually not executable. Since new mappings
Version 1.6
with the PROT EXEC bit set are mapped into the ASCIIarmor area, but pure data mapping are mapped high up,
this means the range of executable code is kept minimal
and data usually is not executable. If an intruder has control over the application this protection can easily be defeated by calling mprotect with a PROT EXEC parameter for an object high up in the address space. But the
Exec-Shield patch is about preventing the intruder to get
such control, not to contain him afterward.
gram then makes sure the correct crt les are linked in
etc. Needless to say that the -fpie option as well as
-pie are not supported if an old version of gcc or a nongcc compiler is used.
It is easy to miss a position-dependent le linked into
a PIE. Therefore, developers should always check afterward whether the PIE is free of text relocations. Text relocations are the result of such position-dependent code
being used. The linker will detect the problems, though,
and add a ag to the PIEs dynamic section. One can
check for the ag with this:
If this pipeline produces any output, the program contains text relocations and should be xed. It is not necessary for correct execution, but running a program with
text relocation means the memory pages affected by the
relocations are not sharable (increasing resource usage)
and that startup times can be signicantly higher. Text
relocations also can create security problems since otherwise write-protected memory briey becomes writable.
The script in appendix B will ag PIEs with text relocations, among other things.
Many applications which are directly exposed to the Internet and some other security relevant programs are converted to PIE in Red Hat Enterprise Linux and Fedora
Core. It does not, in general, make sense to convert all
binaries. Running PIEs is excluding them from taking
advantage of prelinking. The kernel and dynamic linker
will randomize load addresses of all the loaded objects
for PIEs with the consequence that the PIEs start up a bit
slower. If startup times are not an issue (and we are talking about differences usually in the sub-second range, often much lower) PIE can be used freely. All long-running
daemons are good candidates and certainly all daemons
accepting input from networks. But also applications like
Mozilla, which can be scripted from the outside, should
be converted.
[ 1] .interp
Version 1.6
PROGBITS
[ 2]
[ 3]
[ 4]
[ 5]
[ 6]
[ 7]
[ 8]
[ 9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]
[18]
[19]
[20]
[21]
[22]
[23]
.note.ABI-tag
.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.plt
.text
.fini
.rodata
.eh_frame
.data
.dynamic
.ctors
.dtors
.jcr
.got
.bss
.shstrtab
NOTE
HASH
DYNSYM
STRTAB
GNU_versym
GNU_verneed
REL
REL
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
DYNAMIC
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
STRTAB
};
The array msgs is declared const but in a position independent binary, the addresses of the strings that the elements of the array point to are not known at link-time.
Therefore, the dynamic linker has to complete the relocation by making adjustments which take the actual
load address into account. Making adjustments means
the content of the array msgs has to be writable. This is
why in the section layout above the array msgs would be
placed in the .data section.
Even though this is what the linker does, this is not the
optimal result. The compiler actually does better. It emits
the array in a separate section named .data.rel.ro
which contains data that needs to be modied by relocations, but is otherwise read-only. Unfortunately there
is no match for this in the current section layout.
The rst 15 sections do not have to be modied at runtime and can be mapped into memory to not allow write
access. The remaining section, except number 23 which
is not needed at runtime at all, are data sections and need
to be modied. This is the part of the program which
is putting the program in danger. Any place which is
writable is a possible target for an attacker.
readonly/exec
.bss overflow
.data overflow
.data
ELF data
.bss
[ 1]
[ 2]
[ 3]
[ 4]
[ 5]
[ 6]
[ 7]
[ 8]
[ 9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]
[18]
[19]
[20]
[21]
[22]
Version 1.6
.interp
.note.ABI-tag
.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.plt
.text
.fini
.rodata
.eh_frame
.ctors
.dtors
.jcr
.dynamic
.got
.got.plt
.data
PROGBITS
NOTE
HASH
DYNSYM
STRTAB
GNU_versym
GNU_verneed
REL
REL
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
PROGBITS
DYNAMIC
PROGBITS
PROGBITS
PROGBITS
[23] .bss
[24] .shstrtab
NOBITS
STRTAB
The rst 15 sections have not changed and we can ignore the last section since it is not used at runtime. The
data sections have changed drastically. Now all the sections with ELF internal data precede the programs data
sections .data and .bss. And what is more, there is
a new section .got.plt whose function is not immediately apparent. To take advantage of this additional section one has to pass -z relro to the linker (i.e., add
-Wl,-z,relro to the compiler command line). If this
is done the ELF program header gets a new entry:
7
This entry species what part of the data segment is only
written to during the relocation of the object. The intent
is that the dynamic linker marks the memory region as
read-only after it is done with the relocations. The dynamic linker in glibc 2.3.4 and later does just that. We
get the following changed picture:
readonly/exec
ELF data
.bss overflow
.data overflow
.data
.bss
Conclusion
Version 1.6
A Using Exec-Shield
The GNU C compiler and the linker usually determine whether the code needs an executable stack correctly. To see
what is recorded one can run commands like these:
0x4
The second to last column of the output shows that the stack for ls need not be executable, only read-writable (RW).
If the output is RWX the binary is marked to need an executable stack and the kernel or dynamic linker will make the
necessary adjustments. In any case, this is a sign that one should examine the binary since it might be unintentional.
Unintentional execution permission can be granted if any les linked into the binary were compiled with a compiler
which does not add the necessary attribution of the object les, or the le was written in assembler. In the former case
one must update to more recent versions (in case the compiler is a GNU compiler) or demand from the vendor that the
necessary instrumentation is done. The linker always defaults to the safe side: if any input le does not indicate that a
not-executable stack is OK, the resulting binary will be marked as requiring an executable stack.
The case of assembler les is more interesting since it happens even with an up-to-date GNU compiler set installed.
There is simply no way the assembler can determine the executability requirement by itself. The programmer must
help by adding a sequence like the following to every assembler le:
.section .note.GNU-stack,"",@progbits
Alternatively, the GNU assembler can be told to just add this section, regardless of the content of the le. This
is possible by adding -Wa,--execstack to the compiler command line. Note this will not work if an alternative
assembler like nasm is used. For nasm, add the following line to the input le (probably as the last line in the le):
Once the binary is created, the information needed to make a decision is usually lost. If a user knows for sure that no
executable stack is needed, it is often possible to mark the nished binary appropriately. This is especially useful for
binaries, executables and DSOs, which are not available in source form. The tool to use is called execstack and it is
part of the prelink package. Running
$ execstack -s /usr/java/*/bin/java
adds a PT GNU STACK entry in the programs program header. Adding this entry might sometimes fail for executables.
Adding something in the middle of the executable cannot work if not all the interdependencies between the different
pieces of the executable are known. Those interdependencies are lost at link-time unless the -q option for the linker is
used.4
There is one more way to inuence the stack handling. The kernel allows the system administrator to select a global
setting inuencing the use of Exec-Shield. One of three variants can be selected by writing one of the strings 0, 1, or 2
into the le /proc/sys/kernel/exec-shield:
0
Exec-Shield is completely disabled. None of the described protections is available in any process started afterward.
The kernel follows what the PT GNU STACK program header entry says when it comes to selecting the permissions
for the stack. For any binary which does not have a PT GNU STACK entry, the stack is created executable.
4 This
Version 1.6
This option is similar to 1, but all binaries which do not have a PT GNU STACK entry are executed without
executable stack. This option is useful to prevent introducing problems by importing binaries. Every unmarked
binary which does need an executable stack would have to be treated with execstack to add the program header
entry.
For debugging purposes it might be useful to not have the load address of binaries, DSOs, and the address of the stack
at a different place every time the process is restarted. It would be harder to track variables in the stack. To disable just
the stack randomization the system administrator can write 0 into /proc/sys/kernel/exec-shield-randomize.
Ulrich Drepper
Version 1.6
It is nice to have all these security improvements available. But how can one be sure they are used? Red Hat uses the
following script internally which checks currently running processes. Output can be selected in three different ways.
For each process, the script prints out whether the program is a PIE and whether the stack is writable or not. Especially
the later output is useful since no static test can be as thorough. At runtime, the permissions can change and this would
not be recorded in the static ags. Every process marked to have Exec-Shield disabled is a possible problem. If the
stack is executable just because the ag is missing, use the execstack tool (see previous section). If a program is
shown to not be a PIE this does not necessarily mean this is a problem. One has to judge the situation: if the process is
a high-risk case since it is accessible through the network or is a SUID/SGID application, it might be worth converting
the application into a PIE.
#!/bin/bash
# Copyright (C) 2003, 2004 Red Hat, Inc.
# Written by Ingo Molnar and Ulrich Drepper
if [ "$#" != "1" ]; then
echo "usage: lsexec [ <PID> | process name | --all ]"
exit 1
fi
if ! test -f /etc/redhat-release; then
echo "this script is written for RHEL or Fedora Core"
exit 1
fi
cd /proc
printit() {
if [ -r $1/maps ]; then
echo -n $(basename $(readlink $1/exe))
printf ", PID %6d: " $1
if [ -r $1/exe ]; then
if eu-readelf -h $1/exe|egrep -q Type:[[:space:]]*EXEC; then
echo -n -e \033[31mno PIE\033[m,
else
if eu-readelf -d $1/exe|egrep -q DEBUG[[:space:]]*$; then
echo -n -e \033[32mPIE\033[m,
if eu-readelf -d $1/exe|fgrep -q TEXTREL; then
echo -n -e \033[31mTEXTREL\033[m,
fi
else
echo -n -e \033[33mDSO\033[m,
fi
fi
if eu-readelf -l $1/exe|fgrep -q GNU_RELRO; then
if eu-readelf -d $1/exe|fgrep -q BIND_NOW; then
if eu-readelf -l $1/exe|fgrep -q .got] .data .bss; then
echo -n -e \033[32mfull RELRO\033[m,
else
echo -n -e \033[31mincorrect RELRO\033[m,
fi
else
echo -n -e \033[33mpartial RELRO\033[m,
fi
else
echo -n -e \033[31mno RELRO\033[m,
fi
fi
lastpg=$(sed -n /[[:xdigit:]]*-[[:xdigit:]]* rw.. \
\([[:xdigit:]]*\) 00:00 0$/p $1/maps |
tail -n 1)
if echo "$lastpg" | egrep -v -q rwx. ; then
lastpg=""
fi
if [ -z "$lastpg" ] || [ -z "$(echo $lastpg||cut -d -f3|tr -d 0)" ]; then
10
Version 1.6
References
Revision History
Ulrich Drepper
Version 1.6
11