 Alright, it is Monday. Welcome back from framerate. Hope you all will study these brand new houses and homework abilities and expectations. Because it is going to be kind of handy. So homework 3, assignment 3 is release. It's got a week and a day. Or two weeks and a day. Ah, so you can try it out for weeks if you're lucky. So the idea is you will all have access to a shared Linux server. I've already created all your accounts, so you're going to be emailing each of you with the account information and how to access it. That will happen after class, so I know you're not actually doing this during class. The idea is, and I can show you here, so when you have access you will be on this system. It is living in my lab, so don't do anything bad on the network. I've actually blocked a lot of that stuff, but still don't do anything. Your goal is if you run the score command. So there's a program and user local bin score. You can see that everyone here starts out at level 1. So your goal is to each levels 1 through 10 or 10 points each out of 100. So you have to level 10 if you want the points. Right now there's 15 levels. There will eventually be a total of 20, and the next one will be your tool. Those are, you know, going to be worth some points, but it's really more about getting on scoreboard. So how does this work? So if I run the ID command, you can see that I am user ID, add-and-be, group ID, add-and-be. I'm also in group level 1. So if we actually look at the last, you can see that we are all in group level 1. So this is how your score is. So how do you get from level 1 to level 2? You have to hack something. So inside var challenge, you can actually, so you can read this directory. You can see all of the challenges here. So what are the permissions on the level 2 directory? Sorry, the level 1 directory. So it's readable, arrival, executable by the challenge user. It's readable and executable by level 1 group. Who has level 1 group permissions? Everyone. Everyone right now, but me right now, because nobody has access to this. So inside here, you can see there's one file called level 1. So when we look at the permissions here, we see it's readable, and executable by the challenge user. So the challenge owns this. It is readable and executable with what bit set? The set group ID. So it's the sticky bit set on the group. So it's executable, and it will execute as whose group permissions? Level 2. And there's some file that I've already written that needs. So, can I access the level 2 directory? If I'm in level 1, we can see the permissions there, right? The permissions of level 2 is it's only readable or executable to people in the level 2 group. So your goal is, by using some kind of vulnerability in this one, this program, you need to get access to the level 2 group and add yourself to it's group. Luckily for you, we have created a hand made program called Leap. When you run this program, it will automatically add you to the next group level if you're one of the next group levels. So your goal for each of these challenges is to execute this leak program as the next level of group user. So if I just did Leap, it will tell me, what are you doing? You should be level 1 through level 16 right now to use this command. So once, so if I'm able to execute this leak program using the permissions of the 1 program, right, which is set you ID on that group bit, it will add me to group 2 automatically. Question about this? So sometimes in this directory, let's see. So here, do you see any source code? No. So some levels have source code and some levels don't. And it depends. So you have to deal with that. Let's see. It wasn't a lot of work. Thanks to you. Oh, there is something weird about... Let's see. Actually, I don't think this will work because I don't know my password. It's not password, apparently. But I can go to my actual user? Yes. Okay. So ASLR, I have set up some tricks so that ASLR is enabled for the entire system. I'm going to talk about exactly what that is yet. The important thing to note here for level 8, there's 8 and then there's 8 underscore real. There's several levels like this. The 8 real is the program you should analyze. This is something that has the vulnerability. 8 is just a wrapper that calls 8 underscore real. But it disables ASLR when it calls that. So if you're free to analyze it, it should be a super tiny program here. But this one is set to ID as level 8. So if you just try to execute 8 real, will that do anything? So you break this 8 real program, what missions do you have? Level 8. And you already are level 8 to even access this directory. So you want to run that 8 program. Any questions? This is really fun. Because you look more excited. You're the best mom I've ever had in my life. So what do you turn in? So what you're going to do is turn in all of the source code that you bring for this assignment. So if you wrote ANC code, or helper functions, or Python code, whatever you write to solve this, submit that. Also to read me that test by exactly how you solve these levels. So all the commands that you're running on there. I don't really want to tell this story, but please know funny business, right? You're all... So there's a couple things. A, remember you're on a shared system. Everyone's going to be using this. I've put in some things so you can't fork bomb somebody else, but try not to let it happen, because it sucks for everyone and the system's down. And then one of us has to go in and try to fix it. So don't do that. Also be careful of what files you're leaving where, right? If you leave your exploit in some 10th directory that's world readable, try to submit that. We see, hmm, we have two people who submitted the same exploit at the same level. This is going to be like a potential academic therapy violation problem. So don't put us in that situation. Make sure you're responsible for all of your code in your account, right? So somebody submitting your stuff is the same as if you give it to them, whether they stole it from you or not. Let's see. If you get... I'm happy about it. If you are able to get root on the system by exploiting some vulnerability software, let me know. I will give you extra credit points, but don't use that to ruin the fun for everyone else. Just know what you're doing and one person will say, hey, I can do it on a slash and that's like a root directory, so I can become a root. No, I want to see that your user ID is root on the system, right? Let's see. You didn't do it yet, but say a high level, you know, just like someone to check some stuff for the other side of it, right? These are basically like puzzles, right? Each level is like a puzzle and it's very easy to share a solution with somebody as the solution is to a puzzle that they can just run and do. Don't do that. It sucks for everyone. If you're struggling, come to Office Hours. You should start this now so you can get help. We'll gladly help you as much as we can in our Office Hours. We've got more Office Hours. We want to help you. We don't want you to be stuck. I mean, you need to be stuck so much, right? You need to be able to think your way out of problems. But getting stuff from somebody else just sucks because you're ruining all of your learning, right? You're not actually learning anything. So that's very cool. Don't do that. Also, don't try to scan answers from anyone else. I can't believe I have to say this, but it's happened in the past and that student guide has found. So don't do it. Good? All right. Happy stuff. Okay. Final look. So start early. So this is definitely one that if you try to do this that they've already do, you're going to run into some problems because you'll be analyzing the code, you'll be trying to understand how the miners work, and you really need time in order to process these things. You need time to get stuck and to think about what it is that you're stuck on so you can think of ways around it. That's where you get a lot of good security knowledge so you can really find ways to break this stuff. How do you think it's different in what it is and how long it takes to show? Try refreshing it. I updated it at some point. Actually, I did that, but it still shows me 28 of March. It should be the 28. Is that what I said? It says 27. Okay. Wow. Wow. I already updated it. I guess I just have to have a home bird, okay? Yeah. So a weekend day, it should be like Tuesday. Yeah. And how do we get to this one? We're still grading a sign of two so once we're done grading a sign of two, we'll email you with all the credit. And individually, we'll email you with your credit raise in the box. Two mother-of-lives. We're back to two mother-of-lives. So, what is the core problem? Yes? Everything up to the date before the midterm. Everything up to some month and Wednesday. So, we looked to refresh your memory. We looked at a tiny program like this to see what happens when we try to copy a string onto a buffer that's allocated on the stack, where that string is larger than the buffer space that is allocated. So we're not going to go all the way through this. So what's the core problem there? Why? What is the fundamental problem of these buffer overslows? I think the stacks are limiting your stack or the movement of the pointer on the stack. Whose pointer? What pointer? The register that points to the where you're writing on the stack. Yes. Okay. So arrays, fundamentally in C, are just pointers to memory. And access operations are just offsets of that pointer. So there's nothing in there that is checking that, hey, am I writing to allocated memory? So fundamentally, all buffer overflows and they really fall under this general class of memory corruption that you're able to write to memory that is outside of where you should be able to write. And so there's a class of functions that are all inherently unsafe. So whenever you see the use of one of these functions, you should immediately start thinking buffer overflows or some kind of memory corruption. These are the GETS program. Sorry, the GETS function. So what does this do? Anybody use this? What's that? Existing input from the user. More specific than from the user. More specific. Standard in. Yes. It doesn't have to come from the user. It doesn't have to come from the console, right? Standard in. So we know it's reading from standard in. This is incredibly unsafe because if you look at the mandate it says you pass in a buffer to GETS and it says, well, it reads in from standard in until it gets to either a new line or an end of file and it adds that to, it appends that to the buffer. So there is fundamentally no way for the programmer to specify the maximum size that should be written to the buffer using GETS. The GETS is inherently unsafe. Other functions, string copy and string hat. These are other functions where, as we saw the string copy before, right? There's nothing that says how many characters or tracks to the limit. How many characters should be written into the other buffer? S, print dash. So what's the difference between S print dash and print dash? S and four. Secure? Secure. Succeed. What was that? Succeed. Not safe? String. So instead of printf, automatically prints out to standard out. S printf prints out to a file. S printf prints out to a string. S is four. So to print out to a string, you need to pass in what is a parameter. You need a format string. We also need a buffer. You need somewhere to write this result of this format string into. So actually, this is good. Let's, look at this. Actually, let's do it on the white box. Always good to go to the documentation. So you get S, is passed in a character string. And so you can see, reads the line from standard in into the buffer going to my S until either determining new line or end of file, which replaces with a null line. Says no check for buffer overrun is performed. And C bothers for more information instead of never ever use this function and I don't know why it's so much standard library. This is where you get all the information about functions. This is the gold standard for information. And trust me, nine guys out of ten when you put in fgets or string copy into Google, what you're going to find is a web page that is the name page of this. But you just pop yourself 30 seconds in the round trip to Google when you could just go up your terminal and have it right there. So I'll try and save you time. So string copy, we look at the, the signature has a destination, a source. And so if you're reading this, it says the string copy function copies the string pointed to my source, including the terminated null byte to the buffer pointed to my desk. The string may not overlap. That would be a weird problem. And the destination string must, and the destination string must be large enough to receive a copy. Beware of buffer overruns. At least this is in all underscored and like exclamation point, right? Please say that you'd be aware of this. Other, so we have S printf. So you can see, look at the S printf. So here we have a character pointer to a buffer. Then we have the format string. And then we have one of the dot dot dot here. Is what? Variable number of arguments. Yeah, variable number of arguments depending on the format string itself, right? So format, all the printf family of functions are all of this style you can pass and an arbitrary essentially number of arguments to it. Let's see. S printf right to the character string. So we actually see there's four here. And we have the scanf family of functions. So scanf reads in a format and then, let's see, I think it's 12%. S is how you read in. Yeah, so this is given an array of p. There's scanf in. Oh, okay. So scanf, sscanf and fscanf are all other types of ways that you can overrun. These are just kind of a classic. These are the library functions that are inherently marrable. So if an attacker can control the source buffer, right, because we know that if the program has allocated a static buffer for the destination, we know we can always provide input that is larger than that fixed buffer, right? So we can always try to get that to overflow. But we have to be aware, right? You can still have a buffer overflow and a custom input string, right? Somebody can call getc, which getc returns what? A character, one character. So it's getc returns one character, right? They can write a custom read routine that reads until the end of the line and doesn't do any bounce checking. Or they can do bounce checking, but maybe they're off by one, right? And so you have to analyze these custom input routines, too. Your first check should be for, like, these four. If you see any of these, you should immediately start investigating further. If you don't, then you need to maybe look for other ways to just get out. We've looked at, at a high level, what a buffer overflow is. We can overwrite a buffer. We've looked at, if we can overflow a buffer on the stack, what are the important things on the stack that we want to try to control? You can go to the arbitrary. Yeah, what does it call? Save instruction pointer. Save instruction pointer? Yes. So save instruction pointer is maybe our main target. So that is the instruction pointer that is saved on the stack that when the function returns, it will jump and start executing code there. So as how to actually exploit this, we've only been talking about this at a very high level, right? We've been talking about it as, hey, here's what a buffer overflow is. And I'm trying to convince you that, hey, if we can control this save instruction pointer, we can start executing code. But how to actually put all those pieces together. And how to actually exploit this varies based on the exact assembly, the operating system calls, alignment. There's a great paper that I highly suggest you read called Smash in the Sack for fun and profit. When I say paper, I mean it was published in like a Hacker magazine. So it's not really like a paper paper. It's more of like, hey, here's this cool thing I found and here's how you can use it to exploit a buffer overflow. It's always helpful to actually go back to this original source to try to understand what's going on. But fundamentally, so let's say we can use a buffer overflow to control this save instruction pointer. And then what do we want to do? Now we're going to do that. Okay, so we can overwrite the save instruction pointer. Fundamentally, what does that mean? So we can control that instruction pointer. What does that actually mean? Yes. We want to, so with exactly, so with that save instruction pointer, if we can overwrite and put arbitrary values there, yes, but now we need to actually run code of our choosing, right? We want to execute some code that we chose, right? That performs some computation that we want to. And so to do this, the kind of general name for this is called shellcode. So with shellcode, the goal is we want to execute arbitrary code in the vulnerable applications process space. Remember, right? Because we're trying to exploit some other application that's running. It's running its own process, its own memory space. We can't access that memory otherwise we would just own it completely, right? And so we want to be able to execute some code in that vulnerable apps process space. The key is that this code has the same privileges as the application. So if it's a setUID root application, now that code that we're executing is executing with root permissions. Similarly, for the homework assignment, when you execute code and that calls the lead program, it looks up and it says, hey, it looks like you're in the next group. Great, I'm going to add you to that group. So shellcode is the standard term. Shellcode is the standard term for this type of code. And it's called shellcode because the classic example of what you want this to do is to execute bin slash sh, which executes a new shell, which now when you type in commands to that shell, you're running those commands as the owner of that application. So from there it allows you to execute everything. But fundamentally, there's nothing that says shellcode only has to call bin sh, right? You can write custom shellcode to let's say called, I can't remember where it's at anyway, user local bin lead, you can do that. Or you could write shellcode to, I don't know, delete all their files or something without ever calling bin sh. You can do some really cool stuff. It's really just assembly code to perform a specific purpose. That's really the way to think about it. Rather than like, oh, it has to spawn a shell to be like shellcode. No, it's just a chunk of assembly code. So if we want to do just a bin batch shellcode, we can first, and a way to do this when you develop shellcode, you want to first write what you want to do in C because who wants to write x86 assembly raw? I mean, it is kind of fun. So you have to write some of it by hand. But to really understand what's going on, we'll look at a C version and we'll see how that compiles down so that we can understand how to write the assembly code by hand. So we have an array, a character pointer pointer, right? So we have an array of character pointers. Name zero is going to be the string slash bin slash sh. Name one is going to be null. And we're going to call it exec ve, passing in name zero, name, and null. And then we're going to call it exit. So what's the exec ve? What is that? What's the function signature for that? Execute whatever string you give it. Nope. There was going to be a process before. The binary name and then the variable number of arguments. Yes. So important that we go look at this up. Very good. We're pouring out. So, important thing. First, let's see how it works out here. Oops, sorry. Okay. First thing is, exec ve is a system call. So it's a call from the user space to the kernel. The exact arguments are, can you all read this in the back? So, exec ve takes in a filename, an rv array, and an environment pointer array. And it says it executes the program point to do by filename. Filename must be either a binary executable or a script starting with the lines of the form, the hash bang and the interpreter, which you probably dealt with in mark two. Rv is an array of argument strings passed to the new program. By convention, the first of these strings shouldn't contain the filename associated with the file being executed. Environment pointer is an array of strings conventionally that form key values that are passed to the environment of the new program. Both argument and environment p must be terminated by a null pointer. And so, the argument vector and environment can be accessed by the called program's name function. When it is defined like this. So, this is exactly how, when you write a C program, how your arguments are getting passed into this program. So, we looked at bash and we saw that bash calls fork. We know it calls fork because it's creating a separate challenge process. And then it's calling exec ve with the name of the program you want to execute and your rv vector and the environment. So, when we want to execute a new shell, oh, unimportant things, exec ve does not return on success because it completely overwrites the current process and the current process is memory. So, it completely changes it. There's no returning from this. It literally becomes a different program. Let's see, the text, data, bss and stack are overwritten by the program being loaded. So, what is this call going to do? Call business age with no environment. Call business age with no environment. What argument is it passing to business age? What's the rvc going to be when business age is called, is executed? One, and then what's rv0? Spraying business age and rv1 should be what? No. And no environment. So, we're executing business age with no environment. Why do we have this exit at zero? We don't have things to dislike. There's no problem. We exit cleanly with a zero. So, if in exec ve was a library function with cdecal calling parameters, then how would we call exec ve? What would we first do to call that? What would the assembly instructions be? It's the first thing we have to do when we call a function. Push the arguments, and what order? Right to the left. Right to the left. So, we first push it all, then we would push what? Address of the name, and then we would push the address of the name zero, and then we would call exec ve to execute, and then we would never return. Unfortunately, it's not a library call, it is a system call. So, the calling convention is different. So, we have to keep that in mind when we hand-break this assembly. So, we are calling into... So, the parameters are actually usually in registers. There's a fixed number of registers, and if you are passing more parameters than registers, then you push them onto the stack. And then you create a software in-register, and you pass the x value of 80. So, to call exec ve, first you need to tell the operating system which system call do you want to call. Because you're sending a signal to the operating system, you're passing 80, x 80 of that signal, which means that the operating system goes, okay, you're calling a system call, but which system call? There's actually a huge number of them. So, to do this, you put the value x ve into eax. So, the eax register has the index of which system call you actually want to call. And these are all defined... I think it's called syscall.h. Let me look at it in a second. These are all defined somewhere. Then, we put in ebx, we're going to put the final name. So, the address of... So, a pointer to the string, slash din, slash ssh will be in ebx. Then, the address of what we saw is the name variable, right? So, an address to a two-array address, where the first one points to the string, din, slash ssh, goes in ecx. And then, null... Well, we need... We can put the null in edx, and that will be fine there. And then, we want to call it 80. And that will trigger an interrupt. We'll go to the operating system, we'll see, oh, you can call the system call, you can call exactly e, you can call all these parameters, great, I'll execute this program for you. Similarly, so we have an exit. So, exit is actually also a system call. So, exit, the index is one, so we put one in the ex register, we put the exit code that we want in ebx register, and then we call it 80. So, that's what that will look like. So, it's important to look at this when we look at the actual assembly code and see what it does. I lost a USB connector. So, let's think about what we need. So, for exact ve, right, we need xb needs to be in register ex. We need ssh here, and then we basically need an array within ssh, and then null, so this is what our goal is trying to execute here. So, we want to craft some code to do this. So, to do this, we need to somehow move b into ex, and then what needs to be in ebx? The address of what? bnsh. What do we also then need? We need bnsh somewhere, right, we need literally the bytes, slash vin, slash sh somewhere in the program. So, let's say, so let's draw kind of over here, we need the string, slash vin, slash sh somewhere in memory, let's call it memory location alpha. Right, so at memory location alpha, the first byte is slash alpha plus one is b alpha plus three, like two is i plus three is n and slash sh and we can't forget the null terminating zero again here, right? If there's no zero there, then we don't have the right thing. So, we essentially need alpha to go into ebx. Then, what do we want to go? Can we put alpha into ecx? They're right next to each other? So, what's our next goal to do this? So, how do I change this in memory layout to make this work? So, what I need, exactly. So, what I need is a memory location, right? This is alpha and this is null, right? It needs to be a null terminated array. But can this live at alpha? At null, when you're trying to deposit the alpha. Yeah, alpha can't contain the first four bytes of alpha or slash vin, right? So, these are the first four bytes. So, unless you got weirdly lucky and put this string at the memory location slash vin, it's not going to work, right? So, we need somewhere else. So, we need some kind of a beta, right? We need beta to have when you dereference beta, it has alpha. And when you dereference that, it points to the string bin slash the page. And then beta plus four better have the value null there. Because remember, when we look at the function signatures, right? So, we go back here. The rv is a pointer pointer, right? So, beta here is a pointer. When you dereference that, you get another pointer alpha. When you dereference that, you get a character. You get that string. So, we can't put alpha in the ECX. We need to put some other address beta where the first entry there has alpha, and four bytes above that has zero null. And then, what do we want to put in EDX? Zero null. I'm writing null here just to be very clear, but it's zero, right? We want some zeroes there. So, it's kind of tricky, right? What's tricky about this? Or does it look super easy? You're ready to do this on your own? It's a difficult part. And determining what alpha and beta are. So, is it, we're already assuming what about the program, if there's a buffer overflow? Well, we're already assuming that we can write into its memory. That's the nature of the buffer overflow, right? We're going to use the buffer overflow to overflow a saved and trapped pointer, right? So, can we put into the buffer overflowing the string slash bin slash sh maybe it depends? So, we may get that string in there, but yeah, then the tricky things are how do we write alpha and how do we define beta? So, keep that in mind as we continue. So, we need to we're going to talk about this. We need to null turn into string slash bin slash sh somewhere in memory. We need to be address of the string bin slash sh somewhere in memory followed by the null pointer. So, this was beta in our example, right? We need an alpha followed by null. And we need the, we need null somewhere for environment pointer. So, our goal is going to be copy b into ex register copy the address of the string slash bin slash sh into the edx register copy the address of the address of bin sh into ex copy the address of the null word into edx execute in 80 and then copy 1 into the ex register copy 0 into edx and then execute into edx What are these last three doing? Exit. Just call it exit. This should be nice. So, if we were to write this in assembly we would have a data segment we give it a label, we call it sh we have the string slash bin slash sh we have an integer 0 and then our program is pretty straightforward, right? We want to just translate exactly what we want to do into assembly. So, we can set b to be hex b which is the same as 11 right? 10 is a, b is 11 we can then move sh into edx so this we'll use this and we'll see how this compiles so this will move the address of the string slash bin slash sh into edx we then need to do what? So, this is alpha so we set up alpha but what do we now need? We need beta, right? We need something that has 0 and then alpha and we need to get that address so what can we use that has space for temporary variables? The stack so we can create this chunk, this beta that we need on the stack so we can first push null which will push four bytes of nothing then we can push this actually edx onto the stack because that will be the value there and then we can know we can copy the we can query the stack pointer to figure out where we are in memory and that will be our beta so we've solved the beta problem we don't need to worry about beta because we can just use the stack we'll push 0, we'll push sh then we'll move the stack pointer into edx so we think about it exactly at this moment that was good but I can try and draw it out so we think about what the stack looks like so we have bin somewhere at let's call it address of sh right so the stack do we know anything about what was above the stack? nope we don't know but we know that it's used by the program although we don't want to care because we've just taken it over so the first thing we'll do is push 0 onto the stack so this whole thing is going to be null then what's the next thing we'll push onto the stack to the sh and then we have a stack pointer I don't know how to do this ESP is pointing here so now wherever ESP is is our beta so now we have beta which has alpha followed by null and alpha has the string slash vi and slash sh so we move that into edx finally we move 0 into edx so we set up the environment pointer just to be null we call it 80 and we're good then we can clean up move 1 into edx, move 0 into edx and then call it 80 so we can type and I just try ending it and doing it again so we can compile this so we can write it in a .s program we can compile it for 32 bit and then we can run it because gcc is going to turn it into an ai out file the next variable binary and what are we expecting it to do if it works what do we want to see a shell, we want to see a new shell and just as if we called slash bin slash sh so bin sh is like a super primitive version of bash so it shouldn't have any of this fancy host name stuff here so we do that, it looks just like this on loan system that I ran this on sh-dash-4.1 so that's how we know it works execute arbitrary commands in that command line at that point we can type in anything lsid here, we ran our own program this program is not running as any different user it's running just as us but fundamentally we could write it, change it to do whatever we want it could run any program that we want and we can even change the shell code to do anything we want so I'm going to call it exactly e and in sh you could call rm slash rf slash rbin slash rmspace bash rf, you can set that up to just wipe all their files and then you can just do it, right? you won't see a shell prompt but it's still doing what you want it to do then it's just a nice way because it's a human in a nice way to kind of use the tools you're familiar with to interrogate the system so we can take this file we can compile it into an object file and then we can use object dump on this to look at what exact assembly code did the compiler generate for this what did it look like so let's look at it so the main function we can see move b into dps move what's this value into dps sh so that is the address of sh so the compiler created probably a .data segment vin slash sh in the executable and told the compiler to load it at this memory location when it gets executed then push 0, then push 804961c move the stack pointer dcx, move 0 to edx and 80 move 1 to edx 0 to edx, then all in pdf so now we need to test our shell code, right? so we tested it a little bit by running it so that shows us that yes it does it should do what it's supposed to do we're just running this as a program when really when we execute this we're going to inject this code somewhere in their application's process space and it needs to be executed so we can actually simulate that with the c file because c is a terribly unsafe horrible language so you can do this you can create a variable memorize the null shell code You can copy these bytes where do I get these bytes with the object node with these bytes right here the third column here or the second column shows all the bytes of these instructions so v80b00000000 vb1c96 V8 We're creating, what's this doing? Thought you knew C, huh? Yes, it's a function pointer. It's a pointer to a function that takes in zero arguments and returns an integer. So we can set shell, the function pointer, equal to shell code, and then we can call shell which will start executing this program. Now we have to actually add some compiler flags because as I told you there's been a lot of defenses built up over the years against buffer overflows and these type of vulnerabilities. We'll see when we get to advanced techniques of how to bypass those, but in order to do that yourself, you need to compile it with special flags. So this, because right now, this character shell code this is going to be on the stack, right? So we need to tell GCC to make the stack executable, so that's what this flag does. Now if we do this, yeah. Does that still require that bin s h is in on the stack somewhere and it never exists somewhere in the application? Yes. So what? So when I execute this what was the success on the last one? We got a shell. Are we going to get a shell this time? Yeah, it's going to get, it's going to say we're doing nothing, right? And if we look at this, what's the problem with this assembly code? Yeah, there's no slash bin slash s h at that address, right? At 80496 one C, who knows what's there? And why is that? Why don't we know what's there? It's a shell code, right? As far as right now we just took this shell code and just like in a buffer overflow we're going to inject it into somebody else's process and get them to start executing it. That is exactly what this code does right here. This code, all this C code does is hey, here's a string I'm going to execute it. That is exactly what our buffer overflow is going to do. But our shell code that we wrote assumed that there would be the bytes slash bin slash s h zero located at memory location at 496 one C, which does not hold for this other memory location. But what if it doesn't exist? Who says why does this string slash bin slash s h have to be anywhere in their memory space? Yes, so always go back to your tricks. That's like one of the key things you should learn. If you learn the little tricks, like we learn getting around to have to know the address of the data, we'll use the stack. The stack will tell you the address. Here the problem is essentially alpha. There's two problems. A, we know the address of alpha, but the problem has been hard coded it and we don't know what. We can't guarantee that the string slash bin slash s h is going to be at that address. So let's get around that and let's just use the stack. And the key problem is we need position independent shell code. We need shell code that can execute in any process and call exactly e without worrying oh is there this string slash bin slash s h at this fixed memory location. So you think about it, the problem was the code that we wrote was fixed to this one location when we need to write new shell code that can be executed in any processes space regardless of what's there. So we can do that. It's really cool. So we'll again move 11 into EX now we'll exactly use the stack. So we'll push zero so slash s h zero and then we'll push bin on the stack of slash bin slash s h zero. We have to be careful about ending this here when we push this onto the stack. So we think about again there's one thing about what's the stack going to look like. In what sense? Yeah, draw it with your hands. I'm not joking. I'm not making any decisions. I do this all the time now. It helps me so much on these things. So I will literally draw the stack I'll draw what the variables are, I'll draw like where the offsets are and then everything becomes so simple. It's like oh yeah, this is 90 bytes down from the 90 bytes to this and then I'm going to say I'll do that and then this and then you can write shell code in like one go. Also I'm doing this like I will draw just like I do here in class. It doesn't stop at the dooper line. I do it to help me think about these things. Can you print the addresses to see if we put the right stuff at the right? Yeah. So you just have to debug this. You have to debug this code and you can step through and start to my instruction in GED and look at what the stack looks like on these locations. So when we get right here, we have ESP. Again just like before there is something above us on the stack that we don't necessarily know what it is. Then we're going to push essentially it's in reverse but it's going to be slash SH and then zero the character is zero. So this is a no terminated string here. And then we're going to push slash BIN. So it'll be slash BIN slash it'll be slash SH zero. That's how this all works. So this is now how the stack looks here. Now we need to get alpha because we put, so now we've done our first step. We put the string slash BIN slash SH zero somewhere into the processes memory. And it's somewhere right here that we put that B pointer to it. We know that ESP points to what we want as an alpha right now. And so we can just copy ESP and put it into EPP. That's where we want alpha to go. So this here now is, this is alpha we will then push zero. We'll then push EVX. I'll give you a minute. What's in EVX right now? So now ESP points here and then a lot. Yeah. Now this becomes beta. And now we set it up. So we have in EAX is the value XB 11. In EAX is a pointer is the memory address of the string slash BIN slash SH zero. And in ECX is the pointer to an array of the first one pointing to a string that is BIN slash SH and the second one being null. And then we have zero in the environment pointer, so let me call it E. Then we have our pointer. So now we can ask you this and this will work. So we wanted our first thing, right? We wanted to compile it just to make sure this works. It doesn't work in its own process space. It's never going to work in somebody else's. So we can compile it like this. And look at the object up. This is an important thing to do, right? So we can see, look at this. We can take all these bytes copy it into our shell code tester. We can execute or compile it with an x people stack. Run it and we'll see that we actually get a shell from here. So now we can take this code as long as we can get somebody to execute these bytes in their process space that will spot a shell. Questions on this? It's just a weird convention. But no. So what's the benefit of smaller shell code? Yeah, so you may, it all depends on the program, right? But let's say you can only get, like let's say it's Twitter. You can only get 140 bytes into the program. And maybe that's enough for an overflow, but it's maybe not enough for an overflow plus shell code if your shell code is really long. So there's a couple ways around that. So yeah, there's all kinds of tricks of how to make your shell code as small as possible. There's also, you can also do crazy things and have your shell code in stages. So you first have a really tiny shell code that then connects back to you and reads more bytes. So then you send more bytes that it puts somewhere else and then it executes. So you have a stage one and a stage two shell code. All kinds of crazy stuff that you can do. You can even, so we'll talk about that. So now this leads us to another important thing. So it wasn't a stupid question. It's a very good question. So, when the program's, what do you, so let's say you have a string copy function. So string copy, copies from one buffer into another buffer. We control the source buffer, right? When does it stop copying? When it reaches a null byte. So when would it stop copying if we wanted to use that to include our shell code into their process? Yeah, literally we only copy the first two bytes. So we copy B8, 0B, and then the null byte. So technically three bytes. Right? So that's one problem. The gets routine, we said stops on what? It stops at end of file or what? A new line. Yeah. So actually I don't know is there any new lines in here. But this is actually part of the challenge, right? This is probably something you've never thought about before. You've only written code and then ran it, right? Or compiled it, interpreted it, whatever. Here we need to write, not only write assembly code by hand, but now we care about what bytes does that assembly code correspond to, right? Our goal now is can we write the assembly code that does exactly the same thing but has no null bytes in this program and no new lines. So those are the two things we want to avoid. So the way we do this is we look at this appending code and we see, okay, which instructions in here are problematic? To push because of why. Because we put a null point, we put a zero in there, right? We wanted to include the null pointer and slash bin, slash s h null, right? We have to have a null byte after the string, slash bin, slash s h. But what else can we do? We can append it later. We can, if we're pushing this onto the stack, we can push for null bytes, just like we did on that 6a 0 0, the push 0, right? That pushes for null bytes onto the stack. And then if we push, well, bin, slash s h, then at least at that point it will be null terminated. So that's why we do that. What about the first one? What's the problem there? What are we trying to do with that very first line? Yeah, what is this doing? Why are we doing this? Why? Yeah, it's a system call. So we have to, so you always want to separate and think about what are we doing that we have to do and do we have to do it this way? So that actually goes back to the question about the exit, like how do we even worry about this at this point? I'm just going to get rid of it, right? Screw it, we don't need it, right? So a bunch of zeroes, I don't want to think about how to get rid of this. So by the time, the thing that we must have happen is that by the time we get here to this m80, there better be a value of 11 inside the ea-x register. Do we have to get it there by moving 11 into ea-x? What else can we do? Yeah, so we can move all f's, so we can move maybe 1, 2, 3, 4. So 4 f's here into the ea-x register or maybe 3, I don't know, 11, yeah. So it'd be 4, wait a minute, 4 f's. So we can move that into the ea-x register and then so that would be negative 1 and then we could either, well you could add from there, you could do, I think there's an increment operator, so you can increment from there up until 11, so you can have 12 increments. What else can we do? So we could speed up, because having 12 increments in a row is kind of silly, right? So we could do maybe a shift left, so each shift left is basically times 2, right? So we could have a shift left by however many we needed and then an increment to get the values in there. Why is this instruction 5 bytes? It's moving a 32 bit integer into ea-x. What do we really need to be in there? We just need 11, which is only really one byte that we care about. So we move 11 to ea-l. So if we moved 11 into ea-l, it will then, so you have to experiment with this, right? You have to see will the instruction actually be smaller like I think. The answer is yes, it will only be 2 bytes, but will that actually work in any single process? Yeah. Exactly, what if we have extra stuff? So the problem with shellcode is we have absolutely no idea about any of the registers or any of the memory. Fundamentally we can't make any assumptions that, oh yeah, everything else is just 0. So how do we 0 out of a register? We move 0 into it, right? That doesn't work very well, because then we get no bytes. So we can XOR that register with itself and then no matter what bytes are in there, or bits are in there, we know that the register will be empty by the time we're done. So that gets us out of here. So this will do with the combination of XOR and basically a move B into a-l. This will get rid of by pushing and then you can actually start being very clever about reusing things, right? And you can kind of get into too much of a trap if you, this is pretty straightforward code that you can easily kind of change around. But once you let's say you can XOR eax, so now you know eax is 0, then you can push eax onto the stack to get an old byte and then you can push, and then you can move B into ea-l. Maybe I want to use eax later, so maybe you can use that later. It's all kinds of ways you can go about doing this. And the more convoluted, and you guys have gotten to this, we're trying to write assembly and minimize the number of instructions and the number of registers. You can kind of go crazy and at the end you have something where you have no idea what anything does, and everything's all interconnected in this big ball. So, we have XOR eax with eax, it turns out this has no little bytes in it, so that's good. We can push eax then onto the stack, but now when we want to push the string, what string do we want to push, slash bin slash s h, right? How many bytes is that? Seven characters that we want to push onto the stack because we no longer have to push the remaining null byte. But how many bytes does the push instruction push onto the stack? Four, but we have seven bytes. But how do we do that? Add to space? So, yeah, we kind of need the file system equivalent of a space, if you will, right? We need an additional character that doesn't actually change which file we open, right? But that gets us to that nice offset because otherwise I guess we can do it, we can do a space first. So, the first character could be a space and then slash bin slash s h, but then to get that address of alpha we'd have to subtract one from it, so it would be an offset of where it actually was. It would be a huge pain to actually do that. Does anybody know a character that doesn't actually affect what file you open? Yeah. So, file systems are super weird. I don't know if you know where exactly this behavior comes from, but if we do slash slash bin slash s h, that works. Or slash bin slash slash s h. You can actually have as many slashes as you want. It doesn't change which file you open. So, we're going to do slash slash bi and then n slash sh and then zero comes from the previous push of dax. So, there's four bytes of zeros then there's slash slash bin, bi and then n slash sh and then zero. Then now at this point we have our alpha. So, those didn't have any problems. We'll look alpha into edx. Yes. If you put a space after s h, will that be a problem? No, yes, that would be a problem. So, we'll try to exact a file. You can have spaces in file names, right? You just have to use quotes when you access them. So, yeah, that would not access the right file. Push edx, push edx. So, are we pushing edx here? No, nobody, right? We want to push another null, right? We need to have null and then alpha and then have that vr beta. And so, we're reusing this edx register for the fact that we know that edx has zero hit. Then we push edx. So, we push alpha on the stack and now we move that stack pointer to ecx and then we move ex into edx and now what is edx's value? Zero. So, I don't know if I just did this because I like this. You can also try, is it less bytes doing this versus xor edx or edx because that would have the same effect, right? So, it's kind of a fun music experiment. And then we move 11 into al and then we call it 80 and we can also do the same thing here for 1 and 2, but we don't have to really do that. And if we compile it, we'll see that it actually works. We can test this. We can see that it works. Except that this actually doesn't work. So, you'll have to actually do this on your own. Because these bytes aren't work correct. So, you'll have to actually compile this and create your own shout code for, or maybe not, I don't know, depending on what the shout was about. Cool. Alright, I think that's good. We got here and then we'll put it all together on Wednesday. So, I'll show you how that goes.