- 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 parse these messages,
you have to have these temporary storage places
to keep information as you're parsing 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're doing it,
is that you're using an operation or a function
in C, for instance, like a strcpy or sprintf,
and you're 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 to the 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 bit 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 bits, so when they went to 32 bits,
they gave everything with an E prefix,
and then when you go into 64 bits, 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 if 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.
What 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 frame's end and the beginning
is the stack pointer and the base pointer,
and you can see that we have a saved base pointer
that allows you to do, like, a linked list of 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,
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 let me show you an example of this.
So we're calling a function,
and we're decorating the stack pointer,
and that's our local frame,
so we have saved the base pointer that we had from before
and the instruction pointers so that we can resume
from where we were when we left off,
and then that base pointer is then incremented back up,
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'm 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 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 my buffer,
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.