Buffer-Overflow-Attack
Contents
This is a blog recording what I learned when doing buffer-overflow attack
lab.
Stack layout
The figure below is from the lab instruction from my operating system course.
Shellcode
There are two programs. They are both written by c
language. However, one looks like a normal c program, while another one is executing data
.
1 |
|
execve
is asystem call
of Linux.The function of this
system call
is that it forks a child process and run another program.
1st arg
: the name of the file which executes the new program.
2nd arg
: the program to be run. It must end withNULL
3rd arg
: the new environment variable array.
It’s easy to understand the program above. Let’s see another one.
1 |
|
Let’s begin with main
. buf
stores a string which is stored in code[]
originally.
void(*)()
is a function pointer pointing to a function which returns void
. So, the 3rd line of main
means that we convert buf
to the function we mentioned.
Now, let’s figure out the meaning of the assembly program of code
. The source code is written in x86
style here.
1 | xorl eax, eax ; eax = 0 |
Stack of this program is like this. esp-->
means esp
is pointing here now. numbers correspond to labels in the program above.
Stack model |
---|
ebp |
1: esp --> eax |
2: esp --> 0x68732f2f |
3: esp --> 0x6e69622f |
4: esp--> eax |
5:esp --> ebx (the value of the 3rd step esp ) |
Now, we know the 2 programs have the same function.
Vulnerable Source Code
1 |
|
The problem is that strcpy
won’t check whether the length of str
is shorter than BUF_SIZE
.
str
will get its content from badfile
.
Before Attack
Before we manage to attack, we need to simplify our work.
- Shutdown
ASLR
(Address Space Layout Randomization) - Link
sh
fromdash
tozsh
- Open
execstack
(stack can be executed) - Shutdown stack guard
Shutdown ASLR
ASLR
is a countermeasure of operating system. It can randomize the starting address of heap and stack. We can disable this feature by the following command:
1 | $ sudo sysctl -w kernel.randomize_va_space=0 |
Change Link
In Ubuntu 16.04 VMs, bin/sh
symbolic link points to the /bin/dash
shell. Because in this lab, we want to get the root permission, the process will be executed in a Set-UID
process. It can change the effective UID but not real user ID. However, if dash
detects that it is executed in a Set-UID
process, it immediately drop the privilege.
Consequently, we link sh
to zsh
by the following command:
1 | $ sudo ln -sf /bin/zsh /bin/sh |
Shutdown other countermeasures of GCC
Stack guard
and noexecstack
are two countermeasures of GNU/GCC to prevent buffer overflows. In the presence of these protections, buffer-overflow attack won’t work. So, we disable these protections.
1 | gcc -o stack -z execstack -fno-stack-protector stack.c |
Exploit
Exploit.c
1 |
|
The shellcode has been explained before. What we need to write is only two lines of code.
- Put the address of shellcode on the return address of
bof
- Put shellcode on a valid position of
buffer
.
There are 3 numbers here. 0x24
, \x9c\xeb\ff\bf
, 420
. How can we get them?
GDB stack
In order to know the stack layout of stack
. (this is the process), we need gdb
to debug it.
1 | gdb stack #start debugging |
We can see such a surface like that. Now we are in bof()
. PC/EIP now points to 0x80484f1
.
We can know the starting address of buffer
by observing codes before strcpy
. Because buffer
is the first parameter of strcpy
. The offset between buffer
and $ebp
is -0x20
.
According to the stack layout, the address of buffer
+ 0x20
+ 4
= return address
of bof
.
We have got 0x24
.
Actually, 420
can be changed manually. You can define this number by yourself. However, it may be better if this number is big enough, because it can help us to find the shellcode more easily.
If we calculate the address of shellcode by the figure above, Let’s do the calculation.
- the address of
buffer
=$ebp
- 0x20 =0xbfffe9f8
- shellcode address =
0xbfffe9f8
+420
=0xbfffeb9c
Can we succeed? Of course.
1 | gcc -o exploit exploit.c |
We can see that we have root permission now. However, we need to pay attention to uid = 1000
. It means that the real-id is seed
but not root
.
Two stack layouts
We have successfully carried out the attack. It’s the truth. However, there is a little mistake here.
Actually, process running under gdb
and process running directly have different stack layouts. Why? Because the environment variables pushed to the stack are different under these two conditions.
We can succeed because there are lots of nop(0x90)
before the shellcode. We jump to nop
and run until encountering the shellcode.
How can we prove this? We need to add a line of code in stack.c
. It’s getchar()
. The process won’t terminate until it get a character from stdin
.
1 | int main(int argc, char **argv) |
That’s because we want to debug it with gdb
when it is executed directly.
- Compile and run the program and wait.
1 | gcc -ostack2 -fno-stack-protector -z execstack stack.c |
- Open another terminal and get the
pid
of stack2
1 | ps -elf | grep stack2 |
- Attach
gdb
to this process. This command needs root permission.
1 | sudo gdb attach 6903 |
-
Input
n/next
ingdb
, then input a character forstack2
-
Input
n
continually until we go back tomain()
-
When
$eip
points tocall bof
, inputs/step
to step intobof
.
- Get the value of
$ebp
aftermov ebp, esp
.
If you look back, you will find that $ebp
in gdb
mode is 0xbfffea18
. The distance is 64
between two values of $ebp
. That’s why I have mentioned that 420
can be changed manually, but it may be better if the number is big enough.
Countermeasures
In before attack part, I have shut down 4 countermeasures, 2 of which belongs to the operating system, while others belong to gcc
. I will talk about some details.
ASLR
Why we can succeed at once? Because the initial position of stack doesn’t change. If we turn on ASLR
, the initial position of stack alters every time this program is executed.
Input the following command to turn on ASLR
.
1 | $ sudo sysctl -w kernel.randomize_va_space=0 |
We need to write a shell script to run our program again and again.
1 |
|
After 9 minutes’ attack, we finally succeed.
Why can we succeed? Because the initial position floats in a small range(about plus-minus 200). Remember nop
I talk about before? It shows their function here.
Dash
dash
will check whether the uid
is the same with euid
. If not, it will deprive your root
permission.
If we can add some instructions like setuid(0)
before execve
, we can easily defeat dash.
1 | char shellcode[] = |
setuid
is a system call whose system call number is 0xd5
. If we shutdown ASLR, we can easily get the result.
Non-execstack and stack guard
In this experiment,I didn’t find any solution to these two countermeasures. The results are below.
Author: clickmouse
Link: https://clickmouse.github.io/Buffer_Overflow/
License: 知识共享署名-非商业性使用 4.0 国际许可协议