Content
This commit is contained in:
parent
541a979646
commit
020a4542fb
BIN
references/2014_heartbleed.pdf
Normal file
BIN
references/2014_heartbleed.pdf
Normal file
Binary file not shown.
BIN
references/2019_checksec.pdf
Normal file
BIN
references/2019_checksec.pdf
Normal file
Binary file not shown.
BIN
work/01paper.pdf
BIN
work/01paper.pdf
Binary file not shown.
524
work/01paper.tex
524
work/01paper.tex
@ -27,6 +27,7 @@
|
||||
%\usepackage[ngerman]{babel}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{cleveref}
|
||||
\usepackage{url}
|
||||
%%fuer abkuerzungen begin
|
||||
\usepackage[acronym,hyperfirst = false]{glossaries}
|
||||
@ -60,7 +61,12 @@ MatrNr. 3220018}
|
||||
\maketitle
|
||||
|
||||
\begin{abstract}
|
||||
TODO
|
||||
|
||||
This paper tries to explain the details behind buffer overflows, explore the
|
||||
problems stemming from those kinds of software vulnerabilities and discus
|
||||
possible countermeasures with focus on their effectiveness, performance impact
|
||||
and ease of use.
|
||||
|
||||
\end{abstract}
|
||||
|
||||
\begin{IEEEkeywords}
|
||||
@ -74,20 +80,24 @@ Buffer Overflow, Software Security
|
||||
When the first programming languages were designed, memory had to be managed
|
||||
manually to make the best use of slow hardware. This opened the door for many
|
||||
kinds of programming errors. Memory can be deallocated more than once
|
||||
(double-free), the programm could read or write out of bounds of a buffer
|
||||
(information leaks, \acp{bof}). Languages that are affected by this are e.g. C,
|
||||
C++ and Fortran. These languages are still used in critical parts of the worlds
|
||||
(double-free), invalid pointers can be dereferenced (\mintinline{C}{NULL}
|
||||
pointer dereference; this is still a problem in many modern languages), the
|
||||
program could read or write out of bounds of a buffer (information leaks,
|
||||
\acp{bof}). Languages that are affected by this are e.g.\ C, C++ and Fortran.
|
||||
While most if not all of these problems are solved in modern programming
|
||||
languages, these languages are still used in critical parts of the worlds
|
||||
infrastructure, either because they allow to implement really performant
|
||||
programms, because they power legacy systems or for portability reasons.
|
||||
programs, offer deterministic runtime behaviour (e.g.\ no pauses due to garbage
|
||||
collection), because they power legacy systems or for portability reasons.
|
||||
Scientists and software engineers have proposed lots of solutions to this
|
||||
problem over the years and this paper aims to compare and give an overview about
|
||||
those.
|
||||
|
||||
Reading out of bounds can result in an information leak and is less critical
|
||||
than \acp{bof} in most cases, but there are exceptions, e.g.\ the Heartbleed bug
|
||||
in OpenSSL which allowed dumping secret keys from memory. Out of bounds writes
|
||||
are almost always critical and result in code execution vulnerabilities or at
|
||||
least application crashes.
|
||||
than \acp{bof} in most cases, but there are exceptions, e.g.\ the Heartbleed
|
||||
bug~\cite{Heardbleed2014} in OpenSSL which allowed dumping secret keys from
|
||||
memory. Out of bounds writes are almost always critical and result in code
|
||||
execution vulnerabilities or at least application crashes.
|
||||
|
||||
In 2018, 14\% (2368 out of 16556)~\cite{Cve2018} of all software vulnerabilities
|
||||
that have a CVE assigned, were overflow related. This shows that, even if this
|
||||
@ -95,241 +105,439 @@ type of bug is very old and well known, it's still relevant today.
|
||||
|
||||
\section{Background}\label{ref:background}
|
||||
|
||||
% TODO: many references
|
||||
|
||||
\subsection{Technical Details}
|
||||
|
||||
Exploitation of \ac{bof} vulnerabilities almost always works by overriding the
|
||||
return address in the current stack frame, so when the \mintinline{ASM}{RET}
|
||||
instruction is executed, an attacker controlled address is moved into the
|
||||
instruction pointer register and the code pointed to by this address is
|
||||
executed. Other ways include overriding addresses in the \ac{plt} of a binary so
|
||||
that, if a linked function is called, an attacker controlled function is called
|
||||
instead, or (in C++) overriding the vtable where the pointers to an object's
|
||||
methods are stored.
|
||||
Code execution via \ac{bof} vulnerabilities almost always works by overwriting
|
||||
the return address in the current stack frame (known as \enquote{stack
|
||||
smashing})~\cite{Smashing2004}, so when the \mintinline{ASM}{RET} instruction is
|
||||
executed, an attacker controlled address is moved into the instruction pointer
|
||||
register and the code pointed to by this address is
|
||||
executed~\cite{Detection2018}. Other ways include overwriting addresses in the
|
||||
\ac{plt} (the \ac{plt} contains addresses of dynamically linked library
|
||||
functions) of a binary so that, if a linked function is called, an attacker
|
||||
controlled function is called instead, or (in C++) overwriting the vtable where
|
||||
the pointers to an object's methods are stored.
|
||||
|
||||
A simple vulnerable programm might look like this:
|
||||
A simple vulnerable C program might look like this:
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{minted}{c}
|
||||
int main(int argc, char **argv) {
|
||||
void vuln(char *input) {
|
||||
char buf[50];
|
||||
for (size_t i = 0; i < strlen(argv[1]); i++) {
|
||||
buf[i] = argv[1][i];
|
||||
size_t len = strlen(input);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buf[i] = input[i];
|
||||
}
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
vuln(argv[1]);
|
||||
return 0;
|
||||
}
|
||||
\end{minted}
|
||||
|
||||
A successful exploit would place the payload in the memory by supplying it as an
|
||||
argument to the programm and eventually overwrite the return address by
|
||||
providing an input $> 50$ and therefore writing out of bounds. When the
|
||||
\mintinline{C}{return} instruction is executed, and jumps into the payload, the
|
||||
attacker's code is executed. This works due to the way, how function calls on
|
||||
CPUs work. The stack frame of the current function lies between the two pointers
|
||||
\ac{bp} and \ac{sp} as shown in~\ref{fig:before}. When a function is called, the
|
||||
value of the \ac{bp}, \ac{sp} and \ac{ip} is pushed to the stack
|
||||
(Fig.~\ref{fig:call}) and the \ac{ip} is set to the address of the called
|
||||
function. When the function returns, the old \ac{ip} is restored from the stack
|
||||
and the execution continues from where the function was called. If an overflow
|
||||
overwrites the old \ac{ip} (Fig.~\ref{fig:exploit}), the execution continues in
|
||||
attacker controlled code.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\includegraphics[width=.3\textwidth]{./dot/before.pdf}
|
||||
\caption{Stack layout before function call}\label{fig:before}
|
||||
\end{figure}%
|
||||
|
||||
\begin{figure}[h!]
|
||||
\includegraphics[width=.3\textwidth]{./dot/call.pdf}
|
||||
\caption{Stack layout after function call}\label{fig:call}
|
||||
\end{figure}%
|
||||
|
||||
\begin{figure}[h!]
|
||||
\includegraphics[width=.3\textwidth]{./dot/exploit.pdf}
|
||||
\caption{Stack layout after overflow}\label{fig:exploit}
|
||||
\caption{Vulnerable C program}\label{lst:vuln}
|
||||
\end{figure}
|
||||
|
||||
This is only one of several types and exploitation techniques but the general
|
||||
idea stays the same: ovewrite the return address or some kind of function
|
||||
pointer (e.g.\ in vtables or the \ac{plt}) and once that function is called, the
|
||||
execution flow is hijacked and the attacker can execute arbitiary code.
|
||||
A successful stack \ac{bof} exploit would place the payload in the memory by
|
||||
supplying it as an argument to the program (or by placing it in an environment
|
||||
variable, writing it to a file that the program reads, via network packet, ...)
|
||||
and eventually overwrite the return address by providing an input with $> 50$
|
||||
bytes and therefore writing out of bounds. When executing the
|
||||
\mintinline{C}{return} instruction, and the jumps into the payload, the
|
||||
attacker's code is executed. This works due to the way, how function calls on
|
||||
CPUs work: The stack frame of the current function lies between the \ac{bp} and
|
||||
\ac{sp} as shown in~\cref{fig:before}. When a function is called, the value
|
||||
of the \ac{bp} and \ac{ip} is pushed to the stack (\cref{fig:call}) and the
|
||||
\ac{ip} is set to the address of the called function. When the function returns,
|
||||
the old \ac{ip} is restored from the stack and the execution continues from
|
||||
where the function was called. If an overflow overwrites the old \ac{ip}
|
||||
(\cref{fig:exploit}), the attacker controls where execution continues.
|
||||
|
||||
The most trivial kinds of exploits is known as a \mintinline{ASM}{NOP} sled.
|
||||
\begin{figure}[h!]
|
||||
\begin{subfigure}[b]{.3\textwidth}
|
||||
\includegraphics[width=\textwidth]{./dot/before.pdf}
|
||||
\caption{Stack layout before function call}\label{fig:before}
|
||||
\end{subfigure}\\
|
||||
|
||||
\begin{subfigure}[b]{.3\textwidth}
|
||||
\includegraphics[width=\textwidth]{./dot/call.pdf}
|
||||
\caption{Stack layout after function call}\label{fig:call}
|
||||
\end{subfigure}\\
|
||||
|
||||
\begin{subfigure}[b]{.3\textwidth}
|
||||
\includegraphics[width=\textwidth]{./dot/exploit.pdf}
|
||||
\caption{Stack layout after overflow}\label{fig:exploit}
|
||||
\end{subfigure}
|
||||
\caption{Stack layouts during an \ac{bof} exploit}
|
||||
\end{figure}%
|
||||
|
||||
This is only one of several types and exploitation techniques. Others include
|
||||
|
||||
\begin{itemize}
|
||||
|
||||
\item Heap-based \ac{bof}: In this case there is no way of overwriting the
|
||||
return address but objects on the heap might contain function pointers
|
||||
(e.g.\ for dynamic dispatch) which can be overwritten to execute the
|
||||
attackers code, when executed~\cite{Detection2018}.
|
||||
|
||||
\item Integer overflow: Some calculation on fixed sized integers is used to
|
||||
allocate memory. The calculation leads to an integer overflow and only a
|
||||
small buffer is allocated~\cite{Detection2018}. Later a big integer into the
|
||||
buffer is used and reads or writes outside the buffer. This kind of
|
||||
vulnerability can also lead to other problems because at least in C, signed
|
||||
integer overflow is undefined behaviour.
|
||||
|
||||
\end{itemize}
|
||||
|
||||
This paper won't explore other kinds of \ac{bof} in detail because the concept
|
||||
is always the same: Unchecked indexing into memory allows the attacker to
|
||||
overwrite some kind of return or call address, which allows hijacking of the
|
||||
execution flow.
|
||||
|
||||
The most trivial kinds of payloads is known as a \mintinline{ASM}{NOP} sled.
|
||||
Here the attacker appends as many \mintinline{ASM}{NOP} instructions before any
|
||||
shellcode (e.g.\ to invoke \mintinline{shell}{/bin/sh}) and points the
|
||||
overwritten \ac{ip} somewhere inside the \mintinline{ASM}{NOP}s. The execution
|
||||
\enquote{slides} through the \mintinline{ASM}{NOP}s until it reaches the
|
||||
shellcode. Most of the migration techniques described in this paper protect
|
||||
against this kind of exploit but there are different and more complex ways of
|
||||
exploiting \acp{bof} that are not that easily migrated.
|
||||
|
||||
\subsection{Implications}
|
||||
shell-code (e.g.\ to invoke \mintinline{shell}{/bin/sh}) and points the
|
||||
overwritten \ac{ip} or function pointer somewhere inside the
|
||||
\mintinline{ASM}{NOP}s. The execution \enquote{slides} (hence the name) through
|
||||
the \mintinline{ASM}{NOP}s until it reaches the shell-code. Most of the
|
||||
mitigation techniques described in this paper protect against this kind of
|
||||
exploit but there are different and more complex ways of exploiting \acp{bof}
|
||||
that are not that easily mitigated.
|
||||
|
||||
\section{Concept and Methods}\label{ref:concept}
|
||||
|
||||
\subsection{Methods}
|
||||
\subsection{Research Methods}
|
||||
|
||||
This paper describes several techniques that have been proposed to fix the
|
||||
problems introduced by \acp{bof}. The performance impact, effectiveness (e.g.\
|
||||
did the technique actually prevent exploitation of \acp{bof}?) and how realistic
|
||||
it is for developers to use the technique in real-world code (e.g.\ is
|
||||
incremental introduction into an existing codebase possible). In the end, there
|
||||
is a discussion about the current state.
|
||||
This paper describes several techniques that have been proposed to mitigate the
|
||||
problems introduced by \acp{bof} and tries to answer the following questions:
|
||||
|
||||
\subsection{Runtime Bounds Checks}
|
||||
\begin{itemize}
|
||||
|
||||
\item What is the performance impact?
|
||||
|
||||
\item How effective is the technique? Did it actually prevent exploitation of
|
||||
\acp{bof}?
|
||||
|
||||
\item How realistic is it for developers to use the technique in real-world
|
||||
code? Can it be introduced incrementally?
|
||||
|
||||
\end{itemize}
|
||||
|
||||
The paper focuses on solutions for the C language, since it is still the second
|
||||
most used language as of December 2019~\cite{Tiobe2019}. Some of the described
|
||||
techniques are language agnostic but this is not a focus of this paper. In the
|
||||
end, there is a discussion about the current state.
|
||||
|
||||
For the literature research, the paper~\citetitle{Detection2018} served as a
|
||||
base. From there a snowball system search with combinations of the keywords
|
||||
\enquote{buffer}, \enquote{overflow}, \enquote{detection}, \enquote{prevention}
|
||||
and \enquote{dependent typing} was performed using
|
||||
\url{https://scholar.google.com/}.
|
||||
|
||||
Results are evaluated and prioritized using the following criteria:
|
||||
|
||||
\begin{itemize}
|
||||
|
||||
\item Type of publication in the following order:
|
||||
|
||||
\begin{enumerate}
|
||||
\item conference paper
|
||||
\item unreleased paper
|
||||
\item books
|
||||
\item online sources
|
||||
\end{enumerate}
|
||||
|
||||
\item Number of citations
|
||||
|
||||
\item Publisher
|
||||
|
||||
\item Author's reputation and institute
|
||||
|
||||
\item Overall quality (first by checking structure and abstract, then by
|
||||
the actual content)
|
||||
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\subsection{\Ac{rbc}}
|
||||
|
||||
The easiest and maybe single most effective method to prevent \acp{bof} is to
|
||||
check, if a write or read operation is out of bounds. This requires storing the
|
||||
size of a buffer together with the pointer to the buffer and check for each read
|
||||
or write in the buffer, if it is in bounds at runtime. Still almost any language
|
||||
that comes with a runtime, uses runtime checking. For this technique to be
|
||||
effective effective in general, writes to a raw pointer must be disallowed.
|
||||
Otherwise the security checks can be circumvented.
|
||||
size of a buffer together with the pointer to the buffer (so called fat
|
||||
pointers) and check for each read or write in the buffer, if it is in bounds at
|
||||
runtime. Still almost any language that comes with a runtime, uses runtime
|
||||
checking. For this technique to be effective effective in general, writes to a
|
||||
raw pointer must be disallowed. Otherwise the security checks can be
|
||||
circumvented. \Ac{rbc} introduces a runtime overhead for every indexed read or
|
||||
write operation. This is a problem if a program runs on limited hardware or
|
||||
might impact real-time properties.
|
||||
|
||||
\subsection{Prevent/Detect Overriding Return Address}
|
||||
Introducing \ac{rbc} into an existing codebase is not easy. Using fat pointers
|
||||
in a few functions does not prevent other parts of the code to use raw pointers
|
||||
into the same buffer. So for this to be effective, the whole codebase needs to
|
||||
be changed to disallow raw pointers, which, depending on the size, might not be
|
||||
feasible. Still, if done correctly and consequently, it is simply impossible to
|
||||
exploit \acp{bof} for code execution. \Ac{dos} is still possible because the
|
||||
program terminates gracefully when a out of bounds index is used.
|
||||
|
||||
Since most traditional \ac{bof} exploits work by overriding the return address
|
||||
\subsection{Prevent/Detect Overwriting Return Address}
|
||||
|
||||
Since most traditional \ac{bof} exploits work by overwriting the return address
|
||||
in the current stack frame, preventing or at least detecting this, can be quite
|
||||
effective without much overhead at runtime. \citeauthor{Rad2001} describe a
|
||||
technique that stores a redudnant copy of the return address in a secure memory
|
||||
technique that stores a redundant copy of the return address in a secure memory
|
||||
area that is guarded by read-only memory, so it cannot be overwritten by
|
||||
overflows. When returning, the copy of the return address is compared to the one
|
||||
in the current stack frame and only, if it matches, the \mintinline{ASM}{RET}
|
||||
instruction is actually executed~\cite{Rad2001}. While this is effective against
|
||||
\ac{rop} based exploits, it does not protect against vtable overrides.
|
||||
stack based \acp{bof}, in the described form, it does not protect against vtable
|
||||
overwrites. An extension could be made to also protect the \ac{plt} and vtables
|
||||
but custom constructs using function pointers would still be vulnerable. Since
|
||||
this technique is a compiler extension, no modification of the codebase is
|
||||
required to enable it, and while it does not prevent all kinds of \ac{bof},
|
||||
mitigates all stack based \acp{bof} with only minimal overhead when calling and
|
||||
returning from a function.
|
||||
|
||||
An older technique from 1998 proposes to put a canary word between the data of a
|
||||
stack frame and the return address~\cite{Stackguard1998}. When returning, the
|
||||
canary is checked, if it is still intact and if not, a \ac{bof} occurred. This
|
||||
technique is used in major operating systems %TODO
|
||||
but can be defeted, if there
|
||||
is an information leak that leaks the cannary to the attacker. The attacker is
|
||||
then able to construct a payload, that keeps the canary intact.
|
||||
An older technique from 1998 proposes to put a canary word (named after the
|
||||
canaries that were used in mines to detect low oxygen levels) between the data
|
||||
of a stack frame and the return address~\cite{Stackguard1998}\cite{AtkDef2016}.
|
||||
When returning, the canary is checked, if it is still intact and if not, a
|
||||
\ac{bof} occurred. This technique is implemented by major
|
||||
compilers~\cite{Gcc2003} but can be defeated, if there is an information leak
|
||||
that leaks the canary to the attacker. The attacker is then able to construct a
|
||||
payload, that keeps the canary intact. This mitigation has a minimal
|
||||
performance impact~\cite{Gcc2003} and offers a good level of protection. It is a
|
||||
compiler extension so no modification of the code base is needed.
|
||||
|
||||
\subsection{Restricting Language Features to a Secure Subset}
|
||||
\subsection{Static Analysis}
|
||||
% \subsection{Restricting Language Features to a Secure Subset}
|
||||
% \subsection{Static Analysis}
|
||||
\subsection{Type System Solutions}
|
||||
|
||||
\citeauthor{Dep2007} propose an extension to the C type system that extends it
|
||||
with dependent types. These types have an associated value, e.g.\ a pointer type
|
||||
can have the buffer size associated to it. This prevents indexing into a buffer
|
||||
with out-of-bounds values. This extension is a superset of C so any valid C code
|
||||
can be compiled using the extension and the codebase is improved incrementally.
|
||||
If the type extension is advanced enough, the additional information might form
|
||||
the base for a formal verification.
|
||||
can have the buffer size associated to it~\cite{Dep2007}. This prevents indexing
|
||||
into a buffer with out-of-bounds values. This extension is a superset of C so
|
||||
any valid C code can be compiled using the extension and the codebase is
|
||||
improved incrementally. If the type extension is advanced enough, the
|
||||
additional information might form the base for a formal verification. In some
|
||||
cases, the type extensions can even be inferred~\cite{Dep2007}.
|
||||
|
||||
This technique prevents all kinds of overflows, if used, but requires changes to
|
||||
the codebase and is only effective where these changes are applied. Since it is
|
||||
a compile-time solution, it does affect the compile-time but has no negative
|
||||
effect on the runtime.
|
||||
|
||||
\subsection{Address Space Layout Randomization}
|
||||
|
||||
\Ac{aslr} aims to prevent exploitatoin of \acp{bof} by placing code at random
|
||||
locations in memory. That way, it is not trivial to set the return address to
|
||||
point to the payload in memory. This is effective against generic exploits but
|
||||
it is still posible to exploit \ac{bof} vulnerabilities in combination with
|
||||
information leaks or other techniques like heap spraying. Also on 32 bit
|
||||
systems, the address space is small enough to try a brute-force attempt until
|
||||
the payload in memory is hit.
|
||||
\Ac{aslr} aims to prevent exploitation of \acp{bof} by placing code at random
|
||||
locations in memory~\cite{AtkDef2016}. That way, it is not trivial to set the
|
||||
return address to point to the payload in memory. This is effective against
|
||||
every kind of \ac{bof} vulnerability but it is still possible to exploit
|
||||
\ac{bof} vulnerabilities in combination with information leaks or other
|
||||
techniques like heap spraying. Also on 32 bit systems, the address space is
|
||||
small enough to try a brute-force attempt until the payload in memory is
|
||||
hit~\cite{Effectiveness2014}.
|
||||
|
||||
This is another technique that works without modification of the code base. Also
|
||||
there is no runtime overhead because nothing changed except the location of the
|
||||
program.
|
||||
|
||||
\subsection{w\^{}x Memory}
|
||||
|
||||
w\^{}x (also known as \ac{nx}) makes memory either writable or executable. That
|
||||
way, an attacker cannot place arbitiary payloads in memory. There are still
|
||||
techniques to exploit this by reusing existing executable code. The ret-to-libc
|
||||
exploiting technique uses existing calls to the libc with attacker controlled
|
||||
parameters, e.g.\ if the programm uses the \mintinline{shell}{system} command,
|
||||
the attacker can plant \mintinline{shell}{/bin/sh} as parameter on the stack,
|
||||
followed by the address of \mintinline{shell}{system} and get a shell on the
|
||||
system. \ac{rop} (a superset of ret-to-libc exploits) uses so called \ac{rop}
|
||||
gadgets, combinations of memory modifying instructions followed by the
|
||||
\mintinline{ASM}{RET} instruction to build instruction chains, that execute the
|
||||
desired shellcode. This is done by placing the desired return addresses in the
|
||||
right order on the stack and reuses the existing code to circumvent the w\^{}x
|
||||
protection. These combinations of memory modification followed by
|
||||
\mintinline{ASM}{RET} instructions are called \ac{rop} chains and are turing
|
||||
complete~\cite{Rop2007}, so in theory it is possible to implement any imaginable
|
||||
payload, as long as the exploited program contains enough gadgets and the
|
||||
overflowing buffer has enough space.
|
||||
w\^{}x (also known as \ac{nx} or \ac{dep}) makes memory either writable or
|
||||
executable~\cite{AtkDef2016}. That way, an attacker cannot place arbitrary
|
||||
payloads in memory. There are still techniques to exploit this by reusing
|
||||
existing executable code. The ret-to-libc exploiting technique uses existing
|
||||
calls to the libc with attacker controlled parameters, e.g.\ if the program uses
|
||||
the \mintinline{shell}{system} command, the attacker can plant
|
||||
\mintinline{shell}{/bin/sh} as parameter on the stack, followed by the address
|
||||
of \mintinline{shell}{system} and get a shell on the system. \ac{rop} (a
|
||||
superset of ret-to-libc exploits) uses so called \ac{rop} gadgets, combinations
|
||||
of memory modifying instructions followed by the \mintinline{ASM}{RET}
|
||||
instruction to build instruction chains, that execute the desired shell-code.
|
||||
This is done by placing the desired return addresses in the right order on the
|
||||
stack and reuses the existing code to circumvent the w\^{}x protection. These
|
||||
combinations of memory modification followed by \mintinline{ASM}{RET}
|
||||
instructions are called \ac{rop} chains and are Turing complete~\cite{Rop2007},
|
||||
so in theory it is possible to implement any imaginable payload, as long as the
|
||||
exploited program contains enough gadgets and the overflowing buffer has enough
|
||||
space.
|
||||
|
||||
|
||||
\section{Discussion}\label{ref:discussion}
|
||||
|
||||
\subsection{Ineffective or Inefficient}
|
||||
\subsection{Effectiveness}
|
||||
|
||||
\subsubsection{\ac{aslr}}
|
||||
|
||||
\Ac{aslr} has been really effective and wildly used in production. It is
|
||||
\Ac{aslr} has been proven effective and is wildly used in production. It is
|
||||
included in most major operating systems~\cite{FBSDaslr}. Some even use kernel
|
||||
\ac{aslr}~\cite{Linuxaslr}. Since this mechanism is active at runtime, it does
|
||||
not require any changes in the code itself, the programm only has to be compiled
|
||||
not require any changes in the code itself, the program only has to be compiled
|
||||
as a \ac{pie}. On 32-bit CPUs, only 16-bit of the address are randomized. These
|
||||
16-bit can be brute forced in a few minutes or seconds~\cite{AslrEffective2004}.
|
||||
|
||||
There is no runtime overhead since the only change is the position of the
|
||||
program in memory. Since there is no additional work except maybe recompilation,
|
||||
this technique can and should be used on modern systems.
|
||||
|
||||
\subsubsection{w\^{}x}
|
||||
|
||||
With the rise of \ac{rop} techniques, w\^{}x protection has been shown to be
|
||||
ineffective. It makes vulnerabilities harder to exploit but does not prevent
|
||||
anything.
|
||||
ineffective. It makes vulnerabilities harder to exploit by preventing the most
|
||||
naive types of payloads but it doesn't actually prevent exploits from happening.
|
||||
|
||||
\Ac{nx} does not prevent any exploits but makes it harder for an attacker that
|
||||
does not know the system, the program is running on (e.g.\ a network service).
|
||||
It has no runtime overhead and is a compile-time option so it does not hurt to
|
||||
enable \ac{nx}.
|
||||
|
||||
\subsubsection{Runtime Bounds Checks}
|
||||
|
||||
Checking for overflows at runtime is very effective but can have a huge
|
||||
performance impact so it is not feasible in every case. It also comes with other
|
||||
footguns. There might be integer overflows when calculating the bounts which
|
||||
footguns. There might be integer overflows when calculating the bounds which
|
||||
might introduce other problems.
|
||||
|
||||
Methods that have been shown to be ineffective (e.g.\ can be circumvented
|
||||
easily) or inefficient (to much runtime overhead)\ldots
|
||||
|
||||
\subsection{State of the Art}
|
||||
|
||||
What techniques are currently used?
|
||||
Operating systems started to compile C code to \ac{pie} by
|
||||
default~\cite{ArchPie2017} and \ac{aslr} is enabled, too. Same goes for \ac{nx}
|
||||
and stack canaries~\cite{ArchPie2017}. The combination of these mitigations
|
||||
makes it hard to write general exploits for modern operating systems.
|
||||
|
||||
\subsection{Outlook}
|
||||
To check the current state, the latest release of the \ac{gcc} (9.2) and the
|
||||
latest commit of the LLVM-project (\mintinline[breaklines]{shell}{181ab91efc9})
|
||||
are compiled using the default configuration. The experiments are performed on a
|
||||
64-bit Debian 9.11 system running on version 4.19.0 of the Linux kernel. The
|
||||
following commands are used for compilation:
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{subfigure}[b]{.3\textwidth}
|
||||
\begin{minted}{shell}
|
||||
mkdir objdir \
|
||||
&& cd objdir \
|
||||
&& ../configure \
|
||||
--build=x86_64-linux-gnu \
|
||||
--host=x86_64-linux-gnu \
|
||||
--target=x86_64-linux-gnu \
|
||||
--disable-multilib \
|
||||
&& make -j8
|
||||
\end{minted}
|
||||
\caption{\ac{gcc} compilation script}\label{lst:gcc}
|
||||
\end{subfigure}
|
||||
\\
|
||||
\begin{subfigure}[b]{.3\textwidth}
|
||||
\begin{minted}{shell}
|
||||
mkdir build \
|
||||
&& cd build \
|
||||
&& cmake -DLLVM_ENABLE_PROJECTS=clang \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-G "Unix Makefiles" ../llvm
|
||||
&& make -j8
|
||||
\end{minted}
|
||||
\caption{clang compilation script}
|
||||
\end{subfigure}
|
||||
\end{figure}
|
||||
|
||||
The \mintinline{shell}{build}, \mintinline{shell}{host} and
|
||||
\mintinline{shell}{target} parameters in~\ref{lst:gcc} only describe the target
|
||||
platform for the compiler and \mintinline{shell}{disable-multilib} disables
|
||||
32-bit support. The \mintinline{sh}{-j8} flag only tells make to use all 8
|
||||
available cores for compilation.
|
||||
|
||||
The fresh builds of \ac{gcc} and clang compile the code from~\cref{lst:vuln} to
|
||||
check which mitigations are enabled by default. Using
|
||||
\mintinline[breaklines]{shell}{gcc -o vuln.gcc vuln.c} and
|
||||
\mintinline[breaklines]{shell}{clang -o vuln.clang vuln.c} to compile the source
|
||||
code, the \mintinline{shell}{checksec.sh} tool~\cite{Checksec2019} shows which
|
||||
mitigations are active in the new binary:
|
||||
|
||||
\begin{table}[h!]
|
||||
\begin{center}
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
Mitigation & Active in \ac{gcc}? & Active in clang? \\
|
||||
\toprule
|
||||
Stack Canary & No & No \\
|
||||
\midrule
|
||||
\ac{nx} & Yes & Yes \\
|
||||
\midrule
|
||||
\ac{pie} & No & No \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Enabled mitigations in a default \ac{gcc} and clang
|
||||
build}\label{tab:mitigations}
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
Surprisingly enough, two of the most popular C compilers enable only one of the
|
||||
described compile-time mitigations by default. Maintainer of operating system
|
||||
packages of the compiler might choose a more secure configuration for the
|
||||
compiler as shown in~\cite{ArchPie2017} but still, compiler vendors might want
|
||||
to choose better defaults, too.
|
||||
|
||||
So far, all described mitigations don't change anything about the existence of
|
||||
\acp{bof} but just try to prevent the exploitation for code execution. The
|
||||
vulnerable programs will still terminate if the stack canary is overwritten, a
|
||||
call into \ac{nx} memory occurs or execution continues inside garbage data due
|
||||
to \ac{aslr}. The underlying problem persists, only the worst results are
|
||||
mitigated. \Ac{dos} is still a problem in safety critical systems (e.g.\ cars,
|
||||
planes, medical devices) or in any area with real-time requirements.
|
||||
|
||||
Language extensions to fix the problem of \acp{bof} as described
|
||||
in~\cite{Dep2007} require lots of discipline to use them everywhere. They are
|
||||
only useful if the whole codebase uses the new features. Introducing them in an
|
||||
existing codebase is quite unrealistic since it requires lots of modifications.
|
||||
On the other hand, this actually prevents \acp{bof} from happening and not just
|
||||
from being exploited, so it looks like an interesting concept for safety
|
||||
critical software.
|
||||
|
||||
\section{Conclusion}\label{ref:conclusion}
|
||||
|
||||
While there are many techniques, that protect against different types of
|
||||
\acp{bof}, none of them is effctive in every situation. Maybe we've come to a
|
||||
\acp{bof}, none of them is effective in every situation but in combination they
|
||||
offer good protection against code execution attacks. Maybe we've come to a
|
||||
point where we have to stop using memory unsafe languages where it is not
|
||||
inevitable. There are many modern programming languages, that aim for the same
|
||||
problem space as C, C++ or Fortran but without the issues comming/stemming %TODO
|
||||
from these languages. If it is feasible to use a garbage collector, Go might
|
||||
work just fine. If real-time properties are required, Rust could be the way to
|
||||
go, without any language runtime and with deterministic memory management. For
|
||||
any other problem, almost any other memory safe language is better than using
|
||||
unsafe C.
|
||||
problem space as C, C++ or Fortran but without the issues coming from these
|
||||
languages. If it is feasible to use a garbage collector, languages like Go, Java
|
||||
or even scripting languages like Python might work just fine. If real-time
|
||||
properties are required, Rust could be the way to go, without any language
|
||||
runtime and with deterministic memory management. For any other problem, almost
|
||||
any other memory safe language is better than using unsafe C.
|
||||
|
||||
\section{Sources (Dummy Section for Deadline)}
|
||||
% \section{Sources (Dummy Section for Deadline)}
|
||||
|
||||
\begin{itemize}
|
||||
% \begin{itemize}
|
||||
|
||||
\item RAD:\ A Compile-Time Solution to Buffer Overflow Attacks~\cite{Rad2001}
|
||||
(might not protect against e.g.\ vtable overrides, \ac{plt} address changes,
|
||||
\dots)
|
||||
% \item RAD:\ A Compile-Time Solution to Buffer Overflow Attacks~\cite{Rad2001}
|
||||
% (might not protect against e.g.\ vtable overwrites, \ac{plt} address
|
||||
% changes, \dots)
|
||||
|
||||
\item Dependent types for low-level programming~\cite{Dep2007}
|
||||
% \item Dependent types for low-level programming~\cite{Dep2007}
|
||||
|
||||
\item StackGuard: Automatic Adaptive Detection and Prevention of
|
||||
Buffer-Overflow Attachs~\cite{Stackguard1998} (ineffective in combination
|
||||
with information leaks)
|
||||
% \item StackGuard: Automatic Adaptive Detection and Prevention of
|
||||
% Buffer-Overflow Attachs~\cite{Stackguard1998} (ineffective in combination
|
||||
% with information leaks)
|
||||
|
||||
\item Type-Assisted Dynamic Buffer Overflow Detection~\cite{TypeAssisted2002}
|
||||
% \item Type-Assisted Dynamic Buffer Overflow Detection~\cite{TypeAssisted2002}
|
||||
|
||||
\item On the Effectiveness of NX, SSP, RenewSSP, and \ac{aslr} against Stack
|
||||
Buffer Overflows~\cite{Effectiveness2014}
|
||||
% \item On the Effectiveness of NX, SSP, RenewSSP, and \ac{aslr} against Stack
|
||||
% Buffer Overflows~\cite{Effectiveness2014}
|
||||
|
||||
\item What Do We Know About Buffer Overflow Detection?: A Survey on Techniques
|
||||
to Detect A Persistent Vulnerability~\cite{Detection2018}
|
||||
% \item What Do We Know About Buffer Overflow Detection?: A Survey on Techniques
|
||||
% to Detect A Persistent Vulnerability~\cite{Detection2018}
|
||||
|
||||
\item Survey of Attacks and Defenses on Stack-based Buffer Overflow
|
||||
Vulnerability~\cite{AtkDef2016}
|
||||
% \item Survey of Attacks and Defenses on Stack-based Buffer Overflow
|
||||
% Vulnerability~\cite{AtkDef2016}
|
||||
|
||||
\item Beyond stack smashing: recent advances in exploiting buffer
|
||||
overruns~\cite{Smashing2004}
|
||||
% \item Beyond stack smashing: recent advances in exploiting buffer
|
||||
% overruns~\cite{Smashing2004}
|
||||
|
||||
\item Runtime countermeasures for code injection attacks against C and C++
|
||||
programs~\cite{Counter2012}
|
||||
% \item Runtime countermeasures for code injection attacks against C and C++
|
||||
% programs~\cite{Counter2012}
|
||||
|
||||
\end{itemize}
|
||||
% \end{itemize}
|
||||
|
||||
|
||||
\printbibliography{}
|
||||
|
@ -52,3 +52,13 @@
|
||||
short = DOS,
|
||||
long = denial of service
|
||||
}
|
||||
|
||||
\DeclareAcronym{gcc}{
|
||||
short = GCC,
|
||||
long = GNU compiler collection
|
||||
}
|
||||
|
||||
\DeclareAcronym{dep}{
|
||||
short = DEP,
|
||||
long = data execution prevention
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ year = {2001}
|
||||
|
||||
@ARTICLE{Smashing2004,
|
||||
author={{Pincus}, J. and {Baker}, B.},
|
||||
journal={{IEEE Security \& Privacy}},
|
||||
journal={{IEEE Security and Privacy (SP)}},
|
||||
title={{Beyond stack smashing: recent advances in exploiting buffer
|
||||
overruns}},
|
||||
year={2004},
|
||||
@ -126,6 +126,22 @@ year={2007}
|
||||
title = {{7.1.1-4: enable SSP and PIE by default}}
|
||||
}
|
||||
|
||||
@online{Heardbleed2014,
|
||||
url = {http://heartbleed.com/},
|
||||
urlDate = {2019-12-15},
|
||||
year = {2014},
|
||||
title = {{The Heartbleed Bug}},
|
||||
author = {{Codenomicon}},
|
||||
}
|
||||
|
||||
@online{Checksec2019,
|
||||
url = {https://github.com/slimm609/checksec.sh},
|
||||
urlDate = {2019-12-16},
|
||||
year = {2019},
|
||||
title = {{Checksec.sh}},
|
||||
author = {{Klein}, Tobias}
|
||||
}
|
||||
|
||||
@online{Tiobe2019,
|
||||
author = {{TIOBE}},
|
||||
title = {{TIOBE Index for December 2019}},
|
||||
|
Loading…
Reference in New Issue
Block a user