 All right, last lecture before the CTF. So we got a lot of stuff to cover today. So we're gonna kind of go through it really quickly. And yeah, so we'll see everyone on Tuesday will be on the Discord chat. So we'll be there, that's where we'll run the CTF from just like last time. If you have any questions, post them on the Piazza and we can take care of those. Cool, all right. So we've been talking about, in the last lecture, we looked at, and I could go back a little bit, we looked at specifically how function calls happen and how essentially the stack is used by our x86, kind of our assembly code, the compiled version of our application in order to call functions and to be able to return from functions. And so we're gonna look at now is basically the problem of what happens if specifically, when we have memory that's allocated on the stack and an attacker's able to arbitrarily overwrite data on the stack, what kind of things that they can do. And so normally, when this happens and when we write code like this, right, it causes a segmentation fault. So normally what happens is our program and a segmentation fault is due to us accessing memory we're not supposed to. But as we'll see, so a segmentation fault could fall into an availability attack, right? Because we're essentially crashing the application. And so what we wanna do here is we wanna show that actually we can turn this from just an availability attack, just a crash to actually being able to overwrite the return address with a user defined value. So we'll see how we can do that. And yeah, so, and what we'll see is that we can actually execute code of our choosing, which is really cool. And in this way, that code that we're executing runs with the privileges of the program that we're targeting. So what we're doing here is trying to get it so that our overflow and our data, we essentially trick the program into executing things that we have. So let's walk through this a little bit. So some of the implications as we saw of the Cdecl calling convention, right? So this was the calling convention where applications specified or a function before it called, the caller has to save the instruction pointer onto the stack. And then the callee, the function that gets called stores the saved base pointer onto the stacked. And so what we're gonna look at is an example of what happens and what prevents a program or function from changing those values. So we'll look at a, we'll go big here on the big screen. So we'll get a very simple C function. So we have a function called my copy that has a character buffer foo on the stack. And we're gonna copy whatever gets passed in as the string onto our character buffer foo. And that's just a very simple function. So, in a real example, obviously a lot of stuff would happen here, but for now we're just gonna focus on this. And then in our main function, we're gonna copy a large string, pass that into my copy, and then we're gonna print out after and then we're gonna return zero. So one of the things I should tell you is, I've been using this example for a long time for my 340 class in fall 2015. The other thing I learned from this is don't put a class name or a year in your slides, otherwise you feel the urge to update it every time you see it. But as you'll see, there's like a lot of slides that walk through all this. So I'm not going to update these. And so compiling this, we can look at the, and walk through the x86 assembly instructions that get created here. So we have a main function. The very first thing it does, and this is due to the Cdeco colony convention, main has to store whoever calls it base pointer. So we first push EVP, we then move the stack pointer into the base pointer and then subtract 16 or 10 hex from the stack pointer, allocating enough memory for our function. And so this is again the prologue of our function. Then we're moving 804, 8504 into where the stack pointer points to, and then we're going to call my copy. So what is 804, 8504 in this example? Yeah, so it's, so the way to think about it is essentially this is the pointer, right? So it's the address of 804, 8504, and when we go and examine the memory location that's exactly at 804, 8504, there will be the bytes ASU space, CSE space, 340 space in memory with zero. Yeah, so that's, and again, this is something the compiler does, right? So the compiler sees this string constant. Yes, I have all of my 340 lectures on YouTube as well. I think everything, the only class that's not on YouTube is what's it called? It was the very first class I taught here. It was a 591 security and vulnerability analysis at grad class, but everything else should be on there. So, okay, so, yeah, so what the compiler does is it compiles this, it sees this constant string and it says, okay, I need to put these bytes somewhere in memory, and then I'm going to know exactly where I put it in memory and I'm going to put that there. So then we have that, and now we have the next part, we move 8048517 into EAX, move that onto the stack pointer, and then call printf. So then what's at this memory location, 8048517? Yeah, the string after, exactly, right? So that's the memory location where the string after is done. Then we move zero into EAX, and then we move, so now we're going to do the return zero, move zero into EAX, leave, and then return. So that's everything in the main function. Then on the my copy function, we first push EBP, so we're going to store whoever called this base pointer again, we're then going to move stack pointer to the base pointer, subtract now hex 28 from ESP, and then we're going to move EBP plus eight into EAX. So what's EBP plus eight in the function my copy? No, it's not character foo, right? And we know because local variables are negative offsets of the base pointer. Yeah, it's the string, it's the argument, right? This is the way you can tell the difference between them. So even if we didn't know exactly which argument it was, we could know that because it's the base pointer plus some value, that it's going to be one of the arguments. If it's the base pointer minus a value, then we know that it's a local variable. So that's how we can tell, and we'll see that in this function. So we move EBP plus eight into EAX, we then move EAX onto the stack, so we move it into ESP plus four, and we can see now that this is definitely string because this is ESP plus four, and we're pushing arguments onto the stack from right to left order. So first string will be on the stack, and then foo will be on the stack. Then in the next instruction, we load the effective address, so we copy what is EBP minus seven into EAX? Yeah, so again, the compiler just decides 28 hex. So I don't know exactly why it decided this, but it just, yeah, it decided, so it needs to keep room for this four character, so it needs four bytes for this, and then it needs another four bytes for string and four bytes for foo that it's using the stack and pushing it on there, so at least what's at 12, and then it decides to round it to 28 for probably legacy reasons. So now what we're doing here, and remember really important is the difference between a load effective address and a move. So a move says copy whatever is at EBP plus eight, and copy that into EAX. A load effective address doesn't say copy what's at EBP minus C, because what's at EBP minus C, it's a local variable, nothing has been stored there. It says calculate what is EBP minus C, and then copy that into EAX, so we'll see the difference there. Then move that onto the stack, and then call string copy, leave, and then return. So this is the compiled version of the C code on the left, and now let's go forward and see how this execution plays out. So we have our code here, and we have all of the memory addresses from all the code. We have our stack on the left. And again, one time when I ran this, the stack pointer when main was called happened to be at FD2D4. Does the, yeah, so I think Dylan's asking if the address values, decided values change when you run a program and reload a program. So the answer is it depends. So some programs are, like this one, are at fixed memory location. So essentially the address of where this string is is hard coded. It never changes every time you run this. As we'll see, and actually part of the reason why what became important was randomizing where code is in memory, became important actually to combat buffer overflows and the things that we're talking about here. And what happened there is then they needed, it's called the PI, so PIE, position independent executables. So this means that your whole program could be moved at any place in memory and the loader, the program that loads it into memory figures out where the new pointer values and everything go. So in this case, I believe it would be a local offset from here rather than a hard coded fixed value. So yeah, it just depends on the specific version of the actual application. Cool. Okay. So let's keep going. So now our base pointer happened to be at FD2E0, so someplace farther higher above us on the stack. And the instruction pointer is gonna be at 804-840E. And so now we'll walk through this execution. So the very first thing that happens is we push and store the person who called this base pointer. We then move the stack pointer into the base pointer, setting up the base pointer for our function frame. We now subtract 16 or hex 10 from the stack pointer. And now we're ready to actually execute the main function. So what we're gonna do is move the literal value 804-8504 and we can tell the difference here because there's a dollar sign in front of it and there's no parentheses around it. If there are parentheses, the parentheses in a move means dereference. Okay. So we copy that onto the stack 804-8504 and then we call my copy. So what is call my copy gonna do? Or think of it this way. What is the value that it's gonna push onto the stack? Yeah, the next address. What is the next address? Specifically what value? Yeah, this one 804-8483, right? So call does two things. It sets the instruction pointer to whatever my copy is. So 804-8383-F4. And it also pushes onto the stack right here the exact where we should execute when we're done executing this function. So it's going to push 804-8483 onto the stack. This is the next instruction after the call instruction and it sets the instruction pointer to be 804-8383-F4 which is the address of my copy, right? So the call instruction does those two things. And now we can see on the stack we now, so the first thing that my copy does is push main's EBP onto the stack. It moves the stack pointer to the base pointer, setting up its base pointer. And then it subtracts a lot of value, much larger value, hex 28 from the stack, right? So at this point we can see that directly where EBP is pointing to is the saved base pointer of main and then above that is the saved instruction pointer of main and then above that is our argument, right? Which makes sense. So at EBP plus eight is our address for our string variable. What's the difference between the my copy address? I'm not sure the question, Ethan, are you talking about this line? Oh, oh, this is, so yeah, this is the instruction after the call instruction, right? So this is the when main, when any function calls another function, right? What call does is it has to push and store after this function's done executing where do I come back to, right? So the place that needs to happen where we come back after we're done executing my copy is the instruction right after call, this 804, 84, 23, and which gets saved on the stack here. And kind of looking forward basically what we're gonna look at is what happens if we mess up that value? How does, what happens? Yeah, so this is the return address exactly because this is, and it gets to, and the way to think about it is this is the nature of functions, right? We want anyone to be able to call a function. And so in order to support that we need to be able to go back to whoever called us without knowing in advance who is the one that called us. Okay, so we walk through this, we make room on our stack and now we go through this. So we've subtracted space, we now have our base pointer for my copy and we have our current stack pointer. So now we're gonna move whatever's at EBP plus eight into EAX. So that's 804, 8504. We're gonna move that into EAX. We can see that change the EAX register. And next we're going to move EAX onto ESP plus four. So that's gonna change it here, 804, 8404. Now, or 8504. Now this next instruction, a load effective address. So I've already shown here, what's at EBP minus C is the memory address FD2AC. But there's nothing in here. And now is it actually true that there's nothing in here? Can we be certain that there's nothing in here? Yeah, no, right? This is, that's exactly right. There's, I mean, okay, there could be something. It could be zero, it could be any value. We don't know, because remember, all we did in this line is we moved it down, right? So there could be garbage there from another function call. There could be anything there. And so, but we, our program doesn't really know exactly what's there. So this is important. This load effective address is not a dereference. We're not saying copy whatever's at FD2AC and move it there. What we're saying is calculate what is EBP minus C, which is FD2AC and move that into EAX. So that's gonna be put into EAX, FD2AC. We're gonna then gonna move that onto the stack. And then we're gonna call my copy. So now we can walk and understand, okay, or sorry, not my copy. We're gonna call string copy, right? So string copy is the first argument in his destination, which is FD2AC. And the next argument is the source of 804-8504. So now let's look what is the semantics of string copy. So let's figure out how to make this bigger. It's always some like insane combination. There we go. Let me change. Okay, so what is string copy gonna do? So we always wanna look at this and say, okay, what exactly is it gonna do? So here's, that's a P copy, string copy destination to source. The string copy function copy the string to destination, including the null terminating character. So what it's doing is it's iterating over the string copying a byte from source to destination and then incrementing it until we get to the null byte from source and then we're done, right? So very important is string copy checking the length of any of these strings? Yeah, no. And fundamentally, if we even go back to the source code, we're on 155 here. If we go back to the source code, which was days ago apparently, right? And if we look here in this function, is there any way that, well, okay, I guess this is a bad example, but fundamentally string copy gets passed in character it doesn't know the size of the destination. It doesn't know that there's only four bytes that the programmer has allocated for foo, right? All it knows is I'm gonna keep copying from string to foo. And so let's see what happens there. So let's go to 155, there we go. Cool, so we're right on the call now to string copy. So what's gonna happen? Well, essentially what's gonna happen is just like we talked about, string copy is gonna look at what does that memory address 804-8504? Oh, it's the string ASU space CSE space 340 space fall space 2015 space rocks estimation point null byte. And what it does is copies that first byte A to at FD2AC and then the byte S at FD2AC to a D and then the byte U at FD2AF. And so what direction in terms of the stack is this copy going to happen? You wanna try again? Not down, that's good, yes. Yeah, so why is that, right? So it's incrementing byte by byte, right? So it's FD2ACDEFB0B1B2 which actually goes up the stack, right? So remember, up is higher numbers and lower down is smaller numbers, right? So this is kind of a, one of those things that's a little bit ironic that our stack grows down, but strings are copied in the opposite way that the stack grows. So what's gonna happen is our stacks are gonna grow up and or sorry, not our stack is gonna grow up, but our string is gonna be copied up the stack. And so what's above FD2AC on the stack? Yeah, so we have the saved base pointer on the stack, this FD2D0 and we have the saved instruction pointer, we have argument values, all kinds of stuff are actually saved on the stack. And so let's walk through what happens. Well, the first thing that happens is ASU space, well, it happens byte by byte, but essentially this happens and why, so what's the hex for lowercase a? Yeah, it's 61 and then S, 73, U, 75, and what about space? There's a space here that we don't see, it's 20, right? So why is this hex value here, start with a space and end with a, why is it backwards? Yeah, it's because of little Indian values, right? Because that the writing of ASU, so literally at memory address FD2AC will be the character A, which is hex 61, and then at FD2AD will be 73, E will be 75 and F will be 20, but if you were to read that as a 32 bit number, it would actually appears the opposite. So 20 appears as the highest bit of byte and the A61 is the least significant byte. So this actually becomes really important if we want to control the memory address that ends up here or the value that ends up here, we have to remember this little Indian fact that the highest byte ends up here and the lowest byte ends up here. So we'll keep going, we see that next we have CSE space, then we have a 340 space, and then now, as we can see, we're getting into memory. We've actually already, at this point, we've already overwritten memory that we shouldn't have, right? Because our original buffer was only four characters long and now we've written eight characters. So we've already bypassed what we should have, but that in this case doesn't actually, wouldn't actually cause a crash. But let's see what's happened. So we overwrite this. So what happens now when we overwrite this value? Does the program segfault, do we get a crash? Yeah, not yet, why? Yeah, right now, string copy is just copying into memory. And a segfault happens when you copy into memory that you don't have permissions to read or write to. Right, but here, this is the stack. We have permissions to read and write to all of these values. And so we're just, yeah, so we're copying values onto the stack and nothing prevents us from doing that. If you think about it, maybe in terms of access control, right? The stack we can read and write to and here we're just writing to the stack and nothing stops us from doing this. So we can keep writing. We'll write space 201 and then 15 space rock exclamation point. And then there's something missing from this picture. There'll be a null byte up here. So that null byte is copied there, right? And again, now string copy leaves and does it crash? Yeah, remember, so where's, in this diagram that we have here, where's string copies function frame? What direction? Yeah, it'll be FD290 down, right? It's somewhere down here, but did we touch any of that memory down there? Exactly, no, we didn't. All that we touched is memory above FD2AC. So string copy just executes. It may call other functions. The stack could grow three or four levels deep. We have no idea, but it comes back to us. And when it comes back to us, then we're good, right? So it comes back and now, okay, yeah, we're here. I think that's just a, I was trying to think about that. Yeah, it'd be interesting. You could corrupt your own stack if you wrote down, you would still, so I think the answer is no, it doesn't really matter. The core problem is writing to memory that you don't have permission to, right? So if we were to write down, we would then overwrite our own stack pointer and things would break and blow up. The real way to think about solving at least this problem is have a, don't store the return addresses on the same stack that you use to store data. So if that's stored somewhere else that an adversary can't overwrite, that's called a shadow stack, then that is really good, but it has a lot of performance problems because so the funny thing is we've been using this x86 style architecture for a long time that CPUs have optimized for this, like storing return values on the stack, it's insanely fast and using other types of techniques are very slow. And people don't really want to make their programs 20, 25% slower just to prevent security vulnerabilities unfortunately. So that's part of what security researchers do is say, how can we do this more effectively, more efficient so that people actually adopt it? Cool, okay. So now we've returned from string copy, right? We can see, we can visually see the stack has been completely messed up, but still nothing crashes because we haven't tried to dereference any of these values, right? If we tried to dereference and said, okay, give me what's that memory address 20, 75, 73, 61, the operating system says, whoa, I haven't given you that memory. You hit a segmentation fault. You tried to access a memory segment that is not meant for you. So then we keep going. So now we have a leave. So remember, what does leave do? Leave copies the current base pointer into the stack pointer moving the stack pointer up, right? So we have our memory address is here. We copy the base pointer into the stack pointer. So now we set the, there we go, the stack pointer to be fd2b8, right? And that didn't cause a crash because we're just copying a value from one register to another. And then our next instruction is a popEBP. That's the second part of leave. So leave does a popEBP. So it's then gonna just take whatever is currently in where the stack points to copy that value and put it in EVP, which as it turns out is gonna be 6C, 6C, 6166, right? Does this cause a crash? Why? But why is it not allowed? Think about this. Is it anything prevent us from putting this value in this register, right? Registers just hold values. So like right now, EAX has this value fd2ac, which is a memory address. But could I put the value 6C, 6C, 6166 into EAX? Yeah, of course I can, right? So this is kind of the fundamental problem that we have essentially bits in our registers. And we don't really know, does this point to a memory location or not, right? The only, when that check happens is when we actually dereference that to use something. And so for instance, on our program here, what are some dereferences? EBP plus eight into EAX, right? That value. EBP plus eight must be a valid memory location. But we haven't done any of those things yet, right? So the fact that EBP has this insane value in it doesn't actually do anything until we try to dereference EBP. Does that make sense? Cool. And we can see at least here, there's no more EBP references in my copy. So leave, execute just fine. Now comes the return. So effectively, where are we going to try executing from when we execute this return statement, right? What did the return, what does the return statement do? So it actually doesn't touch EBP at all. Yeah, it changes EIP, right? It sets EIP to 31, 33, 220, right? Because remember, what was here was our saved instruction pointer and it should have been 804 or 8430. That's the next instruction we were gonna go to. But what actually happens is we pop this value into EIP and then the CPU says, now it does a dereference and it says, okay, let's start executing at 31, 30, 32, 20. And what's at that memory location? Yeah, who knows? Garbage, not even garbage, it's even worse than that. Nothing, our program is not allowed to touch that memory. The CPU has not given us permission to touch that memory. And so we should get a segmentation fault. So if we run this, we can compile it, we can run it, we'll get a segmentation fault. And now if I run it in GDB, it'll tell me exactly where it crashes. So I can run it in GDB, it starts the program and look, it says segmentation fault 31, 30, 32, 20. And I can look at the registers and I can see that EBP is 6C, 6C, 6166, just like we predicted. And that EIP is 31, 30, 32, 20, which was 340 space in our input here. So now I've gotten this to crash. What else could I do with this? Yeah, I can make it jump wherever I wanted, right? Let's actually go through that real quick. Here we have this. Do you wanna be, we can update this really quick, 365. What's it, 2020? But it's spring, it's still gonna say 2020. GCC M32 sample, GDB is probably what I want. Okay, okay, I see. Because that file didn't exist. Oh, great. Yeah, M32 says compile it for 32 bit and I have to disable some protections that the compiler adds that you can learn about later and stack protection. So where's my Jeff? Didn't I install Jeff on this machine? I want the nice output so that we can see exactly what's going on. I thought I did it on this machine, but apparently not. There we go. Okay, so now we can see this. And so without M32, it's 64 bit. So x86, 64, just like, if you remember back to the reason why this is called EAX is extended AX register. So on 16 bit machines, it's just called AX. So the x86 architecture has expanded to, I think probably from eight to 16 to 32 and now 64 bit registers. So everything is very different. I mean, not insanely different, but there's a lot of difference between 32 bit and 64 bit for the things we're talking about. Registers are twice the size. Everything is RAX, RBX, RCX. The calling convention is a little bit different. So that's why we haven't focused on that. But now we can see here that it, so at 32, 30, 32, 20, at this point, we now, so this is where we're able to control it. So this was our, we changed it to 365 space. And essentially, if you think about it and remember this original program, whatever we put there at 365 space will essentially be where we go and where we start executing after this. So a question would be, what if I wanted to put this program in a basically like an infinite loop? What if I wanted to copy and jump? Let's say I wanted to jump back to main. How do I figure out the address of main? Yeah, so let's look at object dump. And okay, this is a, I see. So this program, okay, is position independent. So check sec is a program that I installed for everyone. I believe it's, this one is part of Pone Tools. So it shows you different security architectures, things that are enabled. So this is position independent, the PIE. The NX means the stack is not executable and it's telling me that there's no stack canary. You can look up exactly what all these things are. So yeah, let's, good call. Let's GDP, A dot out, B main, run. Okay, so at this time when I ran it, it's at F6, FF, sorry, five, six, five, five, five, five, eight, seven. So this is the address of main. And so what it's doing, it's moving everything around in memory because it's position independent. When you run it with GDP, it turns that off. So it's always at fixed memory location. So we'll at least see this work in GDP. So we look at sample and so how, so this is where I wanna go, right? So we know it's this 365 space. So what if I did something like change these values to be like 56, how should X 55? So here I'm put, I'm using C syntax to do exact string values or exact byte values. So the slash X 56 says put the thing with hex code 56 right at this value. 55, what was it? 5555 again, slash X 55 and slash X 87. Get rid of that space, compile it, hex escape sequence out of range. What does that mean? So let's, now let's run it in GDP. Okay, so we're at main. We go to string copy, we'll go step by step. So, what was that? Okay, there we go. We're gonna push EBP. We're gonna move EAX into EBP. Now we're gonna call string copy. So now we're gonna copy, oh, interesting. Okay, well, let's see if this will mess up what we're gonna do here, but that's okay. Is Jeff installed in the assignment server? No, but you can install it very easily. It's just a one line install from the webpage, like I just did here. You don't need any permissions because it installs it locally just to you. Okay, so now we can see that what we're doing here, so this string is gonna be copied onto the stack here at FF FF D5 to C. So that's the string copy. And now let's look at, so where's it shows us the stack? Well, let's just step through and see what happens. Bop, bop. Okay, so now we're about to return and where are we about to return to? So the stack pointer points here at FF FF D5 3C. And yeah, okay, so that did mess it up. So now let's do VIM 870 slash X. Okay, so then I need to do, I'm just gonna put a space here. That should work. So stuff happens, string copy, add, add, leave, move, leave, return, and it's still not working. Why is that? This is really ruining my example. Oh, there's obviously nothing there. Oh, I did not, let's see. Okay, 6555578. Okay, so no, I think I did have the wrong value, but it's still getting at a 32, 30, 3220. Why does it do that? 3230, that's a space 202 swap. Oh, oh, wait, wait, no, no, no, it's, wait, wait, bam. So, okay, ASU, space, CSE. Oh, I bet I know, I bet when I'm compiling it here, the stack layout is different. Okay, yeah, that's the problem. So let's get rid of this, ASU space, 365, and GCC, just run it. Okay, yeah, so the problem was, we were using the stack offsets of our previous run, and, but we're crashing at a different place. So we were crashing, right? So this is, no, you can't actually change it, but you can give different inputs. So think of this like the input, this is why we're doing this, instead of something that looks suspiciously similar to a homework assignment. Okay, space three, so what's, okay. So what is 30 in hexes, zero, okay, so it's all, so it's zero, zero, two, zero, two, is that right? That's what, oh, 2020, okay, that's where we are. Got it, okay, that makes more sense, oops. Okay, so we need to change this 2020. So let's change this to slash x 56 slash x 55 slash x 55 slash x 87, and GCC it, and GDB. Because we're weak, it's because it's compiled with a different compiler. So this was done, as you can tell, in 2015 with a different compiler. So the fact that, like this part, the fact that it adds 28 to the stack pointer basically defines like how, where the thing that we have to overwrite is at EBP minus C, but this stack pointer, or this compilation, this compiler basically put that somewhere else. And that's why it's different. Yeah, the end in this was gonna be the point. That's what I'm gonna hopefully show. That was what I meant to show, but let's see how this works. Okay, good, step through this, call string copy, leave, and return, and now we can see, okay, remember we put in here 56555578 because that's where we wanted it to go, but we can now see the end in this problem. So this is why I originally put that in incorrectly because I wanted you to see that when it's interpreted as a number specifically here for an address, we're gonna get a crash because this number doesn't exist. But what we want to get is, and it needs to be 78, not 87. So that's where we wanna be. And so if I continue this execution, we'll crash because our bytes are exactly in the wrong opposite order. So I want, where was my main? I want 56555578, and then, so now we will change this. So we want 78555556, okay. So now compile it again, GDP, break on my copy, run it, and now we'll step through it. So stuff happens, call string copy, and now we have that move, the leave, and then the return. And now we can see what's on the top of the stack here. Exactly, 56555758, which when we step through to that is exactly the address of main. So we're now back at main. So what's gonna happen when I keep going? Yeah, so if I disable my break point, it's gonna keep going forever until it gets a seg fault because it's using too much memory, because it's just continually changing the stack pointer. So yeah, now if I do this, I get a completely different seg fault. So I've made it go in an infinite loop. So we can see that the stack pointer, that everything is changing here. So sigsev is sig fault, yeah. But we can see that now we can make this go literally wherever we want it to go by changing this value in the input here. We can make it jump wherever we want to in the program, which is super powerful. And so now that we did that, let's go through and talk about, so we can see that, no, the operating system ensures that two different processes can't access each other's memory. So each program thinks that it has a exclusive view of memory, unless you can do things like shared memory in order to explicitly share memory between a process. But you can't essentially do that adversariately. Heaps a 466 topic, so you should go take that class. Yes, it's actually called virtual memory. So essentially the operating system virtualizes the memory and it lies to each program and it thinks it's accessing a certain memory, a location in memory, but that actually maps to physical memory in a different place. So get into that more in operating systems, but it's super cool. So one, so then, oops. So basically one of the things we can see about this is certain functions like string copy. If an attacker is able to control the source of a string copy and the destination is a fixed buffer like we saw, it's fundamentally impossible for somebody to prevent this because an attacker could always supply a large enough value in order to take advantage of this. And so there's actually a whole host of functions that are innately vulnerable, things like gets, string copy, S print F, scan F. So you need to use the secure version of these. So there's a string N copy with the letter N in it for the number of strings to copy. So now the question becomes, well, how do we exploit this, right? Because we're able to control the instruction pointer. How do we turn that into arbitrary code execution? So there's many different ways to do this. One of the first ways of doing this was a paper published called Smashing the Stack for Fun and Profit. It's still a really good, and this is like a hacker paper, not like an academic paper, so it's very accessible. And so what we wanna do is execute essentially arbitrary code of R choosing into the vulnerable applications process space. So we essentially saw that what we're doing is, right, the save DIP, we're overwriting that to get the program to go where we want it to go. So the traditional way of doing this was actually as part of your buffer overflow on the stack, you include x86 code that would traditionally pop a shell. So that's basically call slash bin sh to get you a shell and so that you could execute arbitrary programs. Now this was possible because the stack was executable. Now as we saw on that example, right, if we go back to this example and we do check sec on a.out, we'll see that it has nx enabled. So what that means is the stack is not executable. So this means that this approach fundamentally doesn't work on modern executables. And we thought that that would be a, people thought that this would completely solve basically a buffer overflow problems because sure you can control the instruction pointer, but where do you actually go from there? And so a new technique was developed in, it was first introduced around 2005 that built off of an earlier technique that basically said, there's actually a function, a very nice function in libc called system, right? So rather than jump jumping to shell code that's on the stack, what if I change EIP to jump to the, jump to system, which is a function call, which we'll call binsh for me, which is great. Then they took this idea. So this is a return into libc idea and generalized it. It's that rather than invoking just a whole function like system, you can actually invoke just a snippet of code like a snippet of assembly followed by a return instruction. And in this way you can actually, and this is kind of a, what I love about this is it's a combination of a super cool exploitation technique along with kind of interesting theoretical computer science concepts. So if you've ever thought of or thought about taking 355 and you talk about computation and what types, can a Turing machine do this type of computation and what types of operations do you need for Turing completeness, that actually comes up here in that what little gadgets or little snippets of code can you get to achieve full Turing complete execution? So this was actually created, basically they say, and the title of this is very cool, the geometry of innocent flesh on the bone, return into libc without function calls. So the idea is in any sufficiently large body of x86 executable code, there will exist sufficiently many useful code sequences that an attacker who controls the stack will be able to use by means of these techniques that we introduced to cause the exploited program to undertake arbitrary computation. So super cool. So we're gonna actually walk through an example of this. So here's a incredibly simple program and I'm gonna have to go a little bit fast because I wanna cover this in 15 minutes. I know it'll seem like I'm racing through this but that's okay, I want you to be exposed to this before the CTF so yeah, so here we have a simple function. We have a buffer of size 50 on the stack and we have a string copy which we know as we just saw is automatically vulnerable for argv one and we're gonna copy that value into foo and then just return 10. So we can see the assembly is roughly how we would expect this. We have the base pointer, we're setting up our frame. We then call string copy, move and return. So a very simple one. Now, a big difference that I'm doing on this one as opposed to other ones is I'm using the slash static flag in this one. So what this means is that it's compiled with libc because it's using this libc function in a string copy. Rather than what normally happens is this binary gets dynamic, the libc gets dynamically loaded at runtime. It's actually statically compiled in our program. So let's look at this. Do I not copy this? There we go, gcc, let's see, gcc, wall. Oh no, oh there we go. Wall static, no optimizations, no protector, protector, m32, m, okay. So if we look at this, we can see that it is a.out. We can see that it's very large and if we do object dump, we'll see that we have our main function here. And then we have a lot of other stuff. I'm just gonna, we have string copy, we have, I think we should have system somewhere in there. Yeah, we have basically a lot of libc is actually included in our binary. I don't know if it's all of it, I think it is, but. And so what we wanna do, our goal is, we need to find little snippets, little gadgets in the binary that will perform different actions and encode what we wanna have happen with our shell code, which is to call execve with bin sh into our shell code. So what we're gonna do is, and we need some place to store the string. So what we're gonna do is we can look and we can find a gadget, like a little snippet of code. So you can see this, it's three bytes long. And what this does, it says, move whatever's inside the EAX register to wherever EDX points do. So essentially what we can think of this as is, this is a little function that does, if I'm writing this in like star EDX is equal to EAX, right, if that was like C code. So we're saying, okay, take whatever's inside the EAX register, copy it where EDX points to, and then return. So using this, we can essentially be able to arbitrarily write to memory because we can control as we saw. We can control, in this case, if we set EIP to this value, whatever's inside the EAX register will be copied to where EDX points to. So now assuming we can control those registers, now we can write arbitrary values into memory. And then this return will allow us to, this will then jump to whatever the next thing is on the stack. And so as part of what we're trying to build up, we want to, our goal here is to call exact VE, bin SH, this is our goal, this is what we're trying to build up. So we saw that we can write to the dot, we can write to address 080EA060 in our example. So if we have EAX be the string slash BIN, and we have EBX be this value, then we'll be able to write. So we can say, write at 080EA060, 080EA060, right at that, the string slash BIN. And then we'll want to write to this plus four, which is what, A. We'll see we can get away with slash slash SH. So now we're copying this string into memory so that we can eventually call exact VE with these values. But we need to control EDX and EAX, we have no way of doing that. So we can search more in the binary and we can find a gadget and maybe we, that and we can actually, so we can use a tool, there's great tools for this. So let's just say a RopGadget unrecognized argument because I need the dash dash binary. So it's gonna analyze this binary, it's gonna look, and there's a ton in here, that's why I usually don't show this, but yeah, cool. So we can see it there. We have here a pop EDX in return. Yeah, so usually with a sufficiently large program, there'll be no problem about finding gadgets. And because you can be very clever about the use of gadgets. So that's why for this program, because it's such a small program, I compiled it with all of libc, so we have all of that code to work with. And so we find this gadget that does a pop EDX and return. So why is that useful? Well, we know that when we overwrite values on the stack, when we can have a buffer overflow on the stack, we control the saved instruction pointer and we also control all of the stack. So if we set this to not system, but this gadget that does pop EDX return, whatever this next value we put here, like if we put foo here, whatever's right after this value that we copy onto the stack, that will get put into the EDX register. And then this is where we go next. Oops, that looks ugly. That's where this instruction will go to next when we return from it. So we can keep building these sequence of little gadgets that we wanna go to, including some data that will be used by gadgets here. So we can, yeah, so actually we can show this happening. So this is an example. So here's our gadget. This is the example program. We needed 50 As to get up to there. The back ticks mean execute Python. This is a nice way to get kind of unprintable characters into your program. So I'm gonna print first 50 As, then the value BCDE, and then our gadget 806E91A, and then finally this value EDCB. So breaking right after this call instruction on our program, we can see that we've overwritten the stack. The red pointer here is the base pointer. So at the base pointer is the saved base pointer, which we copied BCDE. And then we copied 0806E91A, which was our little gadget. And then above that we have BCDE, so it in reverse. And this is the value that we're gonna go to. So we leave, which changes the stack pointer here. Now we're gonna return. And again, just like before, I remember when we jumped to main, the CPU has no idea that this value has changed. All it knows is I need to go start executing from here. So it starts executing from here in memory at that gadget that we just looked at. And so it's gonna say, okay, 0806E91A, what do I do? I pop EDP. So I pop that value off the stack here, copy that into the EDX register. And now, see, I've controlled this value, the save DIP, and then I've controlled the value above it. So I'm able to control what value goes into the EDX register. And then this return is gonna go to wherever the address has after it, but I didn't copy anything in there, so I haven't controlled that yet. But using this, I've been able to control the EDX register by whatever value I copy onto the stack after this gadget. So with this gadget, then we can put the next value on the stack into the EDX register, which is amazing. And now we need to find a gadget to put our data into EAX, because that's what we're gonna copy in. It turns out at this location in this program, there's a pop EAX return, we can control EBX with a pop EBX at that memory location. Same with ECX, we have a nice gadget here. So what's the purpose of this gadget? What happens if you XOR a value with itself? Yeah, it sets it to zero. So this sets EAX to zero, and there's a instruction to increment EAX, which is nice. And then finally, so remember, our final goal was to call exec VE, which is an in 80. So this is what we ultimately wanna do is call in 80. And now we can actually manually build our shellcode chain for here. So we can keep doing it like this by building up this Python string, but that gets kinda crazy. So let's actually write it into a script, and this script will actually help us deal with this endianness problem. So I'm gonna keep going just as a side note. I'm gonna keep going till the end of at least this Rop stuff. So if you have to drop off because of class or whatever, please go do that, I'm gonna keep going so that we finish this before the CTF on Tuesday. And I'll of course post the recording online. So we have our payload first starts with our 50 As and then our BCDE. So the first thing I need to do is copy slash BIN to dot data. And so I'm using this. So the struct module in Python has this nice pack. So all this means is take this integer, which is 0806E91A and pack it. The arrow I means pack it as a 32 bit little Indian number. So it's really nice because I can look at this and I can say, yes, this is a Python script. So this is all Python. So I'm appending to P my payload and I'm adding this address. The next thing I do is I'm gonna put the address of dot data because that's what's gonna be put into EDX. And then so this gadget is gonna copy 080A060 into EDX. And then when it returns, I'm gonna go here. So I'm gonna go pop EAX and then return. So what do I want to put in there? The string slash BIN. Then after that, now this is my gadget to move EDX into where EAX is and then return. So that happens and so this is one gadget. So after these execute, and we'll see exactly how that happens, the value at memory address 080A060 will be, whatever's inside EAX, the slash BIN value. Then we're gonna now copy the other part of the string slash BIN slash slash SH. So similar thing, but now with slash slash SH. Now we need to zero out. So we need to put a null byte at dot data plus eight because we need to have our string end in a null byte. So then we call, yeah. So we XOR EAX with itself to set it to zero and then we call that gadget to move it in there. So now we've been able to change memory so to add slash BIN slash slash SH and then a null byte which is now we have our first argument to exec VE this 080A060 and then we, now we need to have the argv vector. So if we look back at this here, so we've been able to do this part. We got this address in memory. Now we need this vector. So we need to copy in the addresses here. So this is, we already know the addresses in advance, remember, because our hard coding things. So we can just use any, we'll use address of dot data plus 12. So we will copy those into there using the exact same gadgets and a null byte. And that's great. Now we need to call exact VE with the address of dot data, the address of dot data plus 12 and the null is at the address of dot data plus eight. And we need to put, so when we do CIS calls, remember, this is when we looked at CIS calls. The way we do that is EAX will have the CIS call number. So for exact VE, I know it's 11 and then EBX will have the address of dot data, ECX will have the address of dot data plus 12 and EDX will have this address of dot data plus eight. So this is just all we're setting up here is we use these gadgets, this pop EVX return to put that in the address of dot data. We then use, set up ECX, EDX and then finally we set EIX to 11. So we XOR it with itself. We then increment it 11 times. So this is just a very simple way to make sure that it's 11. We just keep doing that and then call int 80 and we should be good, we print out our payload. So, and this is again doing this all by hand. So now we can run this in GDB using that exploit script as our input and set a break point right before the end of this function. So this is essentially exactly what the memory will look like here. And you can see the stack is just overwritten with this insane, like all of these different memory addresses with some data mixed in. And again, the really important thing here is that the CPU just has no idea that we've done all this and that we've clobbered this stack. It just does exactly what it's supposed to do for every single instruction. So this return just says, okay, start executing at 0806E91A. And what's there? Well, pop EDX return. And these are just all the things we've looked at, right? We've been looking at how x86 works. So this does a pop EDX, sets this into EDX 080EA060 and then returns. So then it goes to 080BB6D6, which is here, this little gadget. And I've laid them out here just for visibility, but clearly you can see based on their addresses, they're all at different places in memory. So then we do a pop EAX return. So take this value on the stack, move it into EAX. Now EAX has this value returned to that value. Now is our move gadget. So now we're moving EAX into wherever EDX points to. So we do that return. We do this again to get now. So we adjusted slash BIN, this is slash slash SH. And then now we're popping EDX. Now we XOR EAX. So now EAX is gonna become zero and we're gonna copy zero at the end there of our string. And we keep going. So we pop EDX, pop EAX. And now we're gonna set up that vector of addresses to pass into exact VE. Now we clear EAX, copying that in. And now we set up EBX. So now we're setting up our SIS call. So the SIS call is at 080EA060 is our string, the program we wanna execute slash BIN slash SH. We then pop ECX, which is the argv vector. And then we pop EDX so we control EDX. And now we have to set up EAX. So we XOR it so that it's zero. And now we do a bunch of increments until we get up to 11, which is B. And at this point now we call the N80 and now we've set up everything only reusing this tiny gadgets. I mean, this is what's so cool to me is that we used, what is this? One, two, three, four, five, six, seven, eight, eight little gadgets, each two instructions long. And we're able to use them in order to manipulate the memory of this program and the registers of this program so that it will call a system call of our choosing with our values and our data. And when we call this system call, so we can look at it here. So this is inspecting that memory at that memory address. And it'll say I have the string slash BIN slash slash SH. This is a little old trick that two slashes in a path don't mean anything. There's no difference. And it's again, somebody mentioned it's to avoid a null byte in our input. So if we have a null byte, it won't be copied correctly. Then if I look here, so here my second argument, so this was the, this is EBX, the first argument to exact VE. This is ECX, the argv vector. So here I have memory, it's a character pointer pointer. So here I have a pointer to the string BIN SH which is gonna be argv zero and then a null byte which says it's the end. And then I look at 68, my argument pointer and that's null. So now if I continue, it'll say, oh, my program is executing a new program BIN dash. And so I've successfully called exact VE with the string BIN SH and the argument vector BIN SH and null. And so this is fully, so the cool thing is the code in this example, it's not a PIE but it doesn't matter where the stack is, where anything is, as long as the code remains in those fixed locations, it's a completely, this Rop payload will work on this program no matter where the stack is in memory. So that trick that I mentioned earlier that used to happen of jumping to the stack isn't necessary anymore. So cool. Okay, so yeah, we're at the end here, this is great. So, oh yeah, what is dash? So dash, on Ubuntu, BIN SH is actually a sim link to the program dash. Yeah, pi is position independent executable. Is string in C++ safe from this? To be perfectly honest, I don't know enough about C++ to comment on that. I definitely wouldn't wanna make a claim. So ASLR means the stack and the heap are randomized in memory. PIE means that the code can be moved around in memory. So some will be done with, you can think of like PIE is actually used to get around this type of thing. So you can't build a fixed Rop payload because you don't know the exact location of the memory addresses. So actually what has to happen is what attackers do is they chain vulnerabilities. So you use one vulnerability that leaks out a code pointer and now you know you've broken the randomization of the layout and you use that to build up your Rop payload. Oh yeah, so at this point, if we had this payload, as soon as we execute this, now we'd have to figure out, we figure out how do we have it do whatever we wanna do. But yeah, at this point, we've called bin sh, we can call any program we want, we can do anything that this program has permissions to do. And so cool thing, so I can show this real quick since I already have this example. So I showed Rop gadget on this. So Rop gadget has a ton of different options of looking at different kinds of things. The really cool thing that it has is the chain option, it should, where is that? Yeah, okay, Rop chain. So it will actually, so it'll print that out and it will try to automatically build it. So it will look for those gadgets that we talked about and automatically build up a payload that you might be able to use. So you can see the reason why, actually why I use Python 2 is I believe when I used this, it was, I started with this payload. So yeah, you have a nice Rop payload that you can use against this binary in order to make it do what you want it to do. There's other tools for this that have automated tools to do this. The other thing that I will highly recommend and the undergrad TAs are gonna go over tomorrow is using Pwn tools. Let me see if I can pull that up. So Pwn tools is an entire library dedicated to basically writing exploits mainly for CTFs and capture the flags. So a CTF framework and exploit development library, it actually has a ton of insane features. I constantly am learning new things about Python, sorry about Pwn tools and let's see if they have an example. Let's see, getting started. So yeah, it has nice ways to connect to remote places to exploit them, ways to send and interact with remote binaries. Once you pop a shell and get shell, you actually has this nice interactive where it pretends like it's a shell so you can interact with the remote system, helps with packing. It has kind of nicer things like it has this P32 instead of doing the struct packed whatever. Yeah, actually, I mean, tons of cool stuff. So yeah, as you get more and more into this, this tools like this become more important. Let's see, what? Yeah, why is that? Okay, yeah, so then kind of, we've looked at a lot of different, this is kind of a first introduction, crass course to binary and application security. So as we mentioned, there's a lot more in depth that we can go into here, but kind of the really, you know, like some people mentioned, there's really a lot of these, especially low level vulnerabilities come down to being able to modify memory, right? So what happens if an attacker can control alter modify memory? So whether that control happens on the stack, like we looked at future, you know, more modern exploitation happens with heap, if you can modify memory on the heap to cause a program to do something that you control. There's actually a lot of research that we're doing here at ASU. So part of what we do is basically create frameworks and programs to automatically like take a binary like this and automatically identify vulnerabilities and then even automatically create exploits of those vulnerabilities. So Jan and Fish, so Dr. Shoshish Tashvili and Dr. Wang, when they were PhD students at UCSB, they led the UCSB's team in the DARPA cyber grand challenge. So this was a competition that happened in 2016, basically to try to say, can we create an automated system that can compete in a capture the flag competition like a human? And so they actually won third place. So where is there? Oh, there we go. I saw it at the bottom with their system called the mechanical fish, which is actually fully open source. You can go check it out. Yeah, here, maybe I can, oh no. That's funny. Oh, it's an image, ICIC. Yeah, so this is the DARPA cyber grand challenge. Yeah, so their system, the mechanical fish is fully open source and from this competition, so they, this was initially a competition that was open to everyone. So they had a qualification event. The top 10 teams there were invited to compete. I believe they won either 500 or 750K from winning the qualification event and then the third place prize in the cyber grand challenge was $750,000. Yeah, so yeah, ooh, this is a good diagram. So this shows the score, so this is the mechanical fish. And so what we're essentially doing now at ASU is trying to turn this into, and because what they had to do here was build, so it wasn't like Linux binaries. It was an operating system that had a very limited set of capabilities. And so just to, because even doing this in the first place is very difficult. So what we're trying to do now is how do we turn these mechanical fish like things onto real applications? So can we use this to find vulnerabilities in real applications? And then the interesting thing that we're finding is that computers are really good at certain things like generating a bunch of input, fuzzing applications, but they're really bad at understanding the functionality of an application and actually interacting with it. So we're also doing research into how do we combine humans with these machines in order to find vulnerabilities better? So that's just kind of a sampling of the research that kind of we're doing here at ASU and more generally into application security. So yeah, that's thanks for staying with me. Sorry for going late, but I definitely wanted to cover this. I guess I'll throw this out there now. I know everyone's gonna be focusing on assignment six and the homework, but I'll probably create a poll on Piazza. If there's something specific you want to learn about next Thursday, let me know. We can cover, we can talk about web stuff, we can talk about other stuff, we can talk about, I don't know, more of this. So kind of whatever you're interested in, let me know. Thanks, everyone. Good luck, prepare for the CTF. We'll see you on Discord on Tuesday.