Developing an intuition for binary exploitation - bin 0x20

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
We have already come a long way in trying to understand how we can exploit programs through memory corruption. We have covered a wide variety of examples, including basic buffer overflows, format string exploits and even heap exploits. And a lot of you might already find it quite complex. But if you put it into the context of history we are still like 16 years behind. The last examples we have explored from exploit-exercises.com introduced techniques that were state-of-the-art in like 2001. While it still directly applicable to shitty cheap embedded devices, like some IoT stuff, it’s very important to lay a foundation. But before we start talking about all these modern exploit mitigations and how we can bypass them, I think it would be a good idea to try putting what we have learned in more abstract terms. And try to get a feeling or an intuition about exploiting binaries. Let’s try to create a mental model of exploitation. But let’s first introduce some constraints, otherwise it will be too abstract. Let’s focus on programs like we have interacted before. this means intel architecture and on linux. Ok. Let’s start at the beginning. We want programs to execute on our CPU. That’s why a program contains assembler code - machine code. And machine code is just like any other data in our computers. It’s basically just bits. 0 and 1s. A lot of times we combine them into bytes. And a CPU can interpret a byte as an instruction. Or multiple bytes can be interpreted as an instruction. And some crazy digital hardware magic in the CPU knows that this particular value stands for “add the values stored in two registers and put the result in the first register. And registers are just like small memory cells in the CPU, which fullfil different purposes. And different architectures might have different registers for different purposes. But on intel we have for example the general purpose registers like eax, and ebx, that a programmer is free to use however he wants to. But there are also special registers, like EIP, the instruction pointer. That one simply contains the address, which points to memory, where the next instruction will be. And there is also this stack pointer ESP, which points to the top of the stack, and the base pointer EBP, which together with the stack pointer define a stack frame. And for beginners this might already feel complicated. There is code, there is data, there is a stack, there is a heap. There are functions you can call. Functions can return. Somehow there is like a return pointer on the stack you can overwrite with a buffer overflow. There are a lot of different terms that we use. But it’s a lot less complex as it may sound. Because essentially there are just two parts that are important we have memory, which is just a huuuuuuge space of bits that can be 1 or 0. Usually we group them in bytes or words. And there is a CPU, which has a well defined deterministic behaviour that operates on this memory. It’s really that simple. Well. The devil is in the practical details, but essentially, when the CPU is turned on, it will start at some defined address. This could be 0, but could also be defined to be something else. It request that memory content from some RAM, looks at the value stored at that address, and performs the action according to whatever that value represents. Now when we want to execute a program, you can’t just write your code directly into RAM, and restart the CPU at address 0. Because if your program would cause an endless loop, the whole system would stop working. But when you program an arduino, a little microcontroller, that’s basically what you do. But that’s why some people developed something like the Linux kernel. Which abstracts away the direct hardware for you and makes sure, that if your program sucks, you don’t kill the whole system. That’s why a program is not just plain assembler code, but it’s a fairly complex file format. An ELF file. Which does contain your raw code, but also a lot of other information. And when you execute this program, the linux system will actually open the ELF file, read and interpret all the necessary metadata and setup the execution environment for you, and then jumps to the start of your actual code. So how does the execution environment look like? That’s important to picture, because in that environment, you try to exploit a program, you need to understand it. And in some way it’s actyually very simple. Again, the devil is in the practical details, but it will make sense. Let’s say the CPU is just about to execute your first instruction. This means the kernel and the hardware magic has already set up everything. And this is how it looks like. You have a big blob of memory. It ranges from 0, to ffffff. In reality you don’t really have that much memory, that’s why we call it virtual memory. It looks like you own all this memory, but hardware magic only makes you think you have it. But in anyway, the CPU now executes your program which is somewhere in that huge memory. Let’s have a look at how this memory is divided up in a real program. So for example here, we see that from this starting address, to this end address, your program is mapped. We say mapped, because it’s not really physically at this address, but it’s there if you would read the value from that address, in your assembler code. So ignore the underlaying physical reality, and just acccept that there is this huge range of memory you can work with. And infact the stack is also just here. It’s also just defined as starting from this address and ends at this address. So the stack is not really growing or shrinking, that’s just the computer theoretical model of a stack. But how is the stack actually defined? Well the CPU has the stack register, ESP, and it would contain an address pointing into this area here. So could you just point the stack pointer somewhere else. Like into your code? Yeah! You absolutely could. The stack pointer is nothing really special, it’s just a register that contains an address, and it could contain any address. What makes ESP actually special is just that it does some fancy stuff based on instructions. So for example a pop eax instruction, would look up what value is stored at the location where ESP points to, usually that’s the stack, but doesn’t have to. And then writes the value from that location into the eax register. And you can absolutely abuse that in an exploit. For example if you find a bug that allows you to set the stack pointer to a different value, you could create a fake stack on the heap, and just point ESP there. Often times referred to as a stack pivot. So lose the mental image of a stack that grows and shrinks that you learned in computer science, and just think of it what it really is. It’s just some memory where the ESP register points to. And instructions cause interesting effects based on that register. And in the same sense the instruction pointer is not special. Usually it points into your code, but it doesn’t have to. If you manage to control EIP somehow, you can just point it to other memory. For example the stack, which we have used in previous exploits. Because we placed data that is actually valid assembler code onto the stack. You know it as shellcode. The CPU doesn’t care. The EIP register points into some memory, and the CPU just happily does what those values say. And well, that is just half true. Because as you may know, on modern systems the stack is not executeable anymore, so the CPU does kinda care, but also not really. It just means that certain areas in this memory can have different permissions. Certain areas have the executable flag which means the CPU allows EIP to point there and is happy to interpret the values as instructions, but other areas like the stack don’t have it. And then the CPU refuses to interpret it as instructions. Now when we look a bit closer to what kind of data is included in all those different memory areas, we can try to come up with creative ways of how to abuse the. One example is the typical stack structure. When the CPU executes a call instruction it places the current instruction pointer value at the address where the stack pointer points to. It places it ontop of the stack. And when the function returns it takes the value where the stack pointer points to and sets the instruction pointer to it. So if you somehow manage to modify this value on the stack, you can control to what EIP will be set to when the function returns, and thus you can decide what will be execute next. That’s a classical buffer overflow. Another interesting data structure is the global offset table, which is basically just an area in memory containing pointers to functions, if you overwrite an entry there, you can also control what will be executed if a function is called that references an address from this table. I mean oftentimes you cannot directly overwrite these values, but that just means you have to become creative. For example think of two objects on the heap. A user object and a name object, and the user object has a pointer to the name object. And when you want to change the name of this user, the code would follow the pointer and write the new name to that location, which means if you can somehow overwrite that pointer, you can control where it would write the name to. So we could overwrite the name pointer with an address on the stack, and when we write the new name for the user, we will actually overwrite the stored instruction pointer on the stack. See what I try to get at? There is memory that contains data. Some restrictions apply, like certain memory areas are not writeable, others are not executable. And there is a CPU that is very dumb and just executes whatever EIP points to. And there is a program in the memory, which the CPU executes. And this code uses the memory to do whatever it is supposed to do. And it trusts in a certain integrity of the data in memory. But if there is a bug, that allows you to change a value in memory, which the program did not intend to be modifiable, amazing things could happen. And what can happen, that’s just limited by your creativity and imagination. One changed byte here in memory, might cause a certain piece of code to write to an unintended location, which overwrites a function pointer of an object, which another part of the code wanted to use, and suddenly executes something very different. And all these techniques we assigned names to, like stack buffer overflow, heap fengshui, ROP, use-after-free, are all just creative ways to screw with data in memory and how the program executed by the CPU reacts to it. I understand that this episode might not have contained actual useful information to you. But I kinda wanted to get it out there, because maybe somebody didn’t quite picture programs and exploits in this way. But I hope you can see the value in this way of thinking about it. I think it takes some away some of the fear that it all looks so complicated. I’m really interested to hear your critical opinion about this. So comment here on youtube or on the reddit thread linked below. But nevermind if you liked this or not, we will continue soon our path to learn about more advanced memory corruption techniques. So, stay curious, and see you next time.
Info
Channel: LiveOverflow
Views: 43,826
Rating: undefined out of 5
Keywords: Live Overflow, liveoverflow, hacking tutorial, how to hack, exploit tutorial, binary exploitation, developing intuition, theoretical hacking, theory of exploitation
Id: akCce7vSSfw
Channel Id: undefined
Length: 11min 42sec (702 seconds)
Published: Sat Feb 25 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.