- We're gonna get lower level in this lesson.
We're gonna talk about what buffers are
and what a buffer overflow is
and how you can use a buffer overflow to exploit
and gain control of a process or a device.
And first, let's talk a little bit
about what buffers actually are.
Buffers are containers for data.
And really when we're talking about buffers
we're talking about the C programming language.
This is a programming language
that has very manual memory management.
As you bring in messages and you pass these messages
you have to have these temporary storage places
to keep information as you're passing the data.
And so your buffers are your containers for this data.
And in this case we're doing something called a linked list
where we have a buffer, and then we have a pointer
that points to the next structure
that has the same buffer in it and so on.
And when you do a buffer overflow
you're stuffing too much information into that container.
So we've only set aside 14 characters
or 14 bytes to hold that information.
And when you write over this buffer,
you are writing into the next field of that structure
in this case.
So you will have lost that information that you had
to be able to link those buffers together.
And this is a buffer overflow.
In this case, we've lost track
of our linked list data structure
but you can do things that are much more damaging
which you'll soon see.
And this is just showing how we are doing it
is that you're using an operation or a function in C
for instance, like a strcpy or sprintf
and you are writing to that buffer
and if you're not careful with your boundaries
you can actually overwrite that data
and write over to the end of the buffer
and into the thing that's next in memory.
When we're talking about processors and processes
you have very low level architectural things
where you have to worry about like registers.
In this case, we're talking about Intel's X86 processors
and we're running in 32 byte mode.
Whenever you see something with a prefix of E
that's an extended instruction pointer.
This is for compatibility reasons
they had an instruction pointer that was just an IP
that was only 16 bytes so when they went to 32 bytes,
they gave everything with an E prefix.
And then when you go into 64 bytes, you have an R prefix.
So we're talking about the instruction pointer
and the instruction pointer is a register
that points to the address in memory
where the next instruction is.
The next instruction that we're going to execute.
And you manipulate this register directly using calls
and branches.
And when you're returning from a call
you're putting information into the instruction pointer
to resume where you left off.
And it's valid only you're pointing to an executable region
in memory.
If you start executing parts of memory that are maybe data
then the processor is actually interpreting that data
as code and it will go off into the weeds
and it will likely trigger an exception.
And an exception in this case is a segmentation fault.
There are other types of exceptions like dividing by zero,
for example.
And if you execute invalid memory,
if you execute from a location that is maybe say,
the zero page then that may not be mapped
into a process at all.
It may not be data, it's just not there.
And if you start trying to run things that are not there
then the processor gets upset
and it'll tell the OS to stop the program.
So talking about the registers,
we have a couple of special purpose registers that are used
for keeping track of the stack.
And the stack is, it's a temporary location to put values
in memory and stacks grow from higher addresses
to lower addresses.
So this is kind of maybe backwards,
which you may be expecting is that
you start from the higher addresses you have saved,
values that are on the stack
and then you have local values that are stored
inside of the stack frame.
And between the local frames end and the beginning
is the stack pointer and the base pointer.
And you could see that we have a saved base pointer
that allows you to to do like a linked list
of a stack frames.
And the stack pointers, then hold those local variables
and those parameters that are used by a function
and it's used to save registers.
So what does that mean?
So some of the registers that we talked about earlier
like EAX, EAX is a general purpose register
that could be used as a result for mathematical functions
or mathematical operations.
Let's say you're adding two numbers together
and you have a result and you would save those
in that register.
Well, let's say that a function called another function
and it expects EAX to have another value,
you have to store those somewhere
and you would store them on the stack.
The stack forms a chain in which functions are called
and functions return
and those values that are stored on the stack
like the EIP is the saved instruction pointer,
that's so that we can resume back from where we left off
after we return from a function.
So when a function is called those local values
and function arguments are pushed onto the stack
and then when the function returns
those parameters are popped, so to speak, off the stack.
So lemme show you an example of this.
So we're calling a function
and we're decrementing the stack pointer
and that's our local frame.
So we have saved the base pointer that we had from before
and the instruction pointer, so that we can resume
from where we were when we left off
and then that base pointer is then incremented backup
and that establishes our new stack frame
and it keeps going and so on.
And so I think you could see where I'm going here with this
is if you overflow a buffer that is on the stack,
you can actually overflow these values
that are saved registers.
And one of the ones that's important is the EIP.
So let me give you an example of this.
And this is written again in C
and it's an intentionally vulnerable example.
You probably will not see code that looks like this
in the real world
but this is just for demonstration purposes
that'll give you an idea about what's going on.
And so what we have here is we have a main function
and this main function allocates a buffer called mybuffer.
And you can see where it's called char mybuffer[128]
So that means that I am allocating 128 bytes
for that buffer and I'm using this function later on
called strcpy.
And what strcpy does is it takes two buffers
and it copies between them.
So in this case, we have a buffer that is called argv
and we're indexing it by one.
That is the first command argument value that we're copying
to the buffer.
And we've only allocated 128 bytes for this
and my arguments that I could pass to the program
can be much longer.
In my case, in my system, I had two megabytes was the limit.
And so you could see that I could actually overflow
that buffer far, you know
far over what it actually is supposed to fit in there.
And so what happens when we put more than 128 bytes
in this buffer is that we overwrite not only mybuffer,
but we start writing into the base pointer
and the instruction pointer.
So let's say that I'm putting just all As
and I'm passing those into the command.
You could see that I'm writing over 128 bytes of As
into the process, and then it goes in
and eventually it overwrites the base pointer
and the instruction pointer
and possibly even data that's after that.
And we're taking the control.
So what happens is when the process returns
it's going to load the value that is the saved EIP,
the saved instruction pointer
and then it is gonna put that into the instruction pointer
and it's gonna start executing.
So that's really basically the gist
of how a buffer overflow works is you're taking control
of the instruction pointer, and you're running whatever
you know, you can actually point to code to run at
and what you could do if you know where that buffer is,
you could point the instruction pointer to that buffer
and actually execute it directly
so what was data becomes code.