 All right, so here we are in the final stretch. Let me make sure everything's working. Cool. So what we're gonna do today is, you can think of it in a couple of different ways, live hack through it, or you could think about it as a group hack through, because you can definitely follow along from home and I encourage you to do so. So what we're gonna be doing, I'm basically, so what I did was I took some challenges and the idea from, so if you can see this, this is, I may have brought this up before, this is the last year's Defcon Qols event. So one of the, just like we have on our CTF, with different categories and all that stuff, here we had, new for the first time, we had a category called speedrun, which was basically trying to see how fast people could exploit things. So in this case, let me see the leaderboard. Yeah, so this is actually crazy. So this, look at this one next. I made an easier first one that's more similar to the homework, so we can see that there. But yeah, you can see that somebody exploited this literally from the time that this service, so there were 12 services, we released them in a 24 hour period. So every two hours a new speedrun challenges was released and I honestly don't remember the exact scoring, but basically the top three got points, oh yeah, you can see it here, 25, 20, 15, 10, and five. And then everyone else got five just for solving it and then your overall score in terms of seconds of how many seconds it took your team to complete it, that got overall points for doing this. So the interesting thing that I'll share with you is I asked around after this and this team, Soul Plus Badass, so what is that, 266 divided by 60, so that's in seconds, so less than five minutes. So that's four minutes between four and five minutes, roughly around four minutes, 40 seconds, something like that. And so this, I found out that the person who did this was a 14 year old Korean hacker who got really good at quickly exploiting binaries. So I thought that was super cool, but you can see kind of the differences in the timing there. Like this was actually a pretty quick race and you can see on the different services how quickly people can, yeah, 14 years old, no joke. Yeah, I guess now the person's 15 years old. Maybe I'll put in the Discord a link to the tweet where I found that person. So yeah, super impressive. And this is the, no, now he's 15, so he was 14 then. So yeah, that was really crazy. And some of these teams, like you can see on here, teams like Shellfish, PPP is the team that ended up winning the, I think, did they win? Let's look at the scoreboard. Yeah, PPP ended up winning Qualls and then won Finals this year, but you can see on a pure kind of speed basis, it's just a stark difference between those two. And these are for a lot of reasons, right? But this just kind of shows one different type of aspect when it comes to trying to solve these challenges quickly. Yeah, it's just, I mean, I was totally blown away. You can look at some of these and see the speed here. It's just, it's honestly incredible how fast these can happen. The crazy thing was I also took a screenshot. I was, because I created all these, so I was waking up every two hours to release them. And so that was a major pain for me. But what we will do is, so I created two services here. So actually the funny thing about automation, that's a great comment, is when we were doing this, so part of the work we're doing with some of our government partners is how to do this type of automated exploitation. So we had done that, but even tools like anger, I think being able to analyze a binary and generate an exploit in four minutes is probably not possible. So that was pretty much it. So anyways, yeah, automation is super cool. So let's look at this challenge. So I made this a zero point challenge that you can't actually submit so that you can actually just play along from home if you'd like. So if we look at the challenges here, there should be a new speed run challenge. Yeah, so it's zero points and it's not worth anything to you, but you can feel free to play along at home. So I didn't come up with any clever saying or anything here. So we have the binary speedrun 000 speedrun.c and we can see that the binary is at this location. So basically what I'm gonna do now is I'm gonna walk through exploiting this binary and oh, you're saying automate the release of the challenges. Yeah, no, our systems don't really work like that. You want, especially for something big like that, you want somebody to actually have to push a button to release things. Otherwise, if you mess up the automation, you could get all the challenges spilled out early and it can be a major problem. So yeah, there are cases like, you know, nuclear missiles and releasing challenges that you want it to be a little bit manual. Okay, cool. So, okay, yeah, so what we're gonna do here and all I'm gonna use is, I'm gonna use, so I downloaded a Ghidra last night. So I'm gonna use Ghidra and I'm gonna use the assignment six server. So I'm not gonna use anything I have locally, except for those things. Where's my new project? All right, I'm not really super used to Ghidra. Okay, so we have here, we don't have any clever text or anything on here and we just know that it's at this IP address in this port. So the first thing we should look at is, I will first SSH, so just as we're a user on the system, we're not gonna use anything special, it's gonna be exactly what you've been seeing. So again, and this kind of came up a bunch at the start of the CTF, right? So the idea is there is this service, this binary running at that IP address in that port, right? So we can use the netcat command. So netcat is just a simple tool to, and if we do man netcat, we can see that it does arbitrary TCP and UDP connections and listen. So it's actually a very handy utility to be able to connect to remote services, but also just stand up something that can listen. So here's some kind of fun things we can do with this. Let's see, I'll make two windows and I can do SSH there. And I can do netcat nl. So listen on port, let's see, 8080. And then I'm gonna netcat to localhost 8080. And anything I type in here will be sent to the next. So this is all happening over TCP. It's made a TCP connection over here and we can see that this packet gets sent to each other. So this means we can actually do things like netcat, things that we know that are open, like Google.com, port 80, right? So port 80 is HTTP, so we can type in kind of anything here, but we'll get a, the server is expecting an HTTP request, right? So what we get back is an HTTP response that says, hey, I don't understand what you're talking about. Okay, what we want to connect to is this client. So we connect there, port 9000, and we can see it says anything, we type something in and it said, you said this, goodbye. And then it goes away. So okay, not much information from talking to it, but at least this gives us a way to talk to this remote service. Okay, then we gotta inspect what we have here. I guess I will fire up Emacs so we can look at that C file. Let's see, is there a way, I know there's a way for you to, speed run, oh downloads, right? Speed run 000, that is a binary, that's not gonna help me much, dash dot C. Okay, so let's look at what we have here and let's kind of try to game this out a little bit. So I'm gonna, all right, how do I increase this font size? I thought I used to be able to do this, but can you all see the, can you read the text on the screen pretty well for the Emacs terminal or window? Okay, yeah, I'm trying to do command plus. It says it's undefined. So I thought I could like command scroll, maybe I'm using a different version or something. Anyways, okay, I think I have something like this. There we go, I do have something that can increase it a little bit. Okay, so yeah, you can definitely download this to follow along from home. So let's walk through this program. We can see it's not very much and I'll kind of go over it, talking about what kind of things I'm thinking about, right? So we have a, so we have the main function here, right? So we know that's where the program will start executing. We have this set VBUFF, which if you're really curious, so there's a little bit of magic that goes on in a lot of CTF challenges. If you're interested, the basic idea, if we look at this challenge and we look at other challenges, you'll see that they get input from standard input. Yet, as we just saw, we can talk to this service over TCP. So here we're making TCP connections to that service. So XINETD is a way for a system to say, hey, whenever you get a TCP connection on this specific port, send it to this program that gets executed here. So that's all that's going on here. That's why when you look at the binary, it's all looks like standard input, standard output, because that's a lot easier to write and deal with. The tricky thing there is you need it so that the output's not buffered. So it actually makes it all the way back to the client, but it's safe to just say that you can ignore any kind of set VBUFF lines. Then what we're gonna see here is we call three different functions. So we have a function called say hello and say hello, prints out a hello new line. We have a function called what do they say? What happens here? There is a character buffer of length 100. There's a printf that says any last words, new line. There's an fgets, which reads into buffer 700 characters from standard input. And if we didn't know that, we can look at the man page of fgets here. So we can see that fgets is a function that takes in a character pointer, an integer for a size, and a file pointer to the stream. So an important, it is always important. I highly recommend, especially on assignment six and any type of challenge, really just reread the manual for exactly what this function does, because definitely I can't keep in my mind exactly what the different semantics between fgets, scanf, gets, all these different types of things by all actually do different things and have different limitations. So for instance, if we read the man page for fgets, we say it's a function that reads at most one less than the number of characters specified by size. So it's gonna read in at least here 699 characters from the given stream and stores them into the string string, good. Reading stops when a new line character is found at end of file or error. So this means it's gonna keep reading until it gets a new line. So this is important and this is something that we really have to keep in mind because if we send a new line as part of our input and part of our payload, so if we send, if we try to send, here's a bunch of A's and then a new line and then a bunch of B's and then a new line and then a bunch of C's, if we send that the fgets will only read this part. So it'll only read up until that new line and everything else we sent won't be put into that buffer. So this is why it's really important to read these types of things. Some things like scan f on a percent S, I believe will not accept spaces. So any spaces won't get counted and won't get read into the program. So okay, so we know it's gonna stop reading when it gets to a new line. The new line is returned. If any characters are read and there is not an arrow and there is no error, sorry, a null byte is appended to the end of the string. Yeah, so it appends a null byte, but it reads in less than the size. So if the size here, let's say was a hundred, we'd say that it's gonna read in at most 99 characters and then put in the null byte at the last one. So that should be fine. The tricky thing is, yes, the flag is slash flag. I didn't put that in the description. And so here we have, so yeah, so a null byte character is appended to the end of the string, cool. So then we have a print f, you said this, so we're printing out what we said, then say goodbye, just print out goodbye, and this makes sense. And the important thing with making sure you actually understand what's going on is looking through, when I connect to this, is this what I see in this binary? Or this, in this case, C code, right? So I have the hello, I have the any last words, I have it's clearly waiting on input for me, I type in input, it said you said this, and then it output goodbye, and then we're done. So we also have a suspicious function here that is never called. So why is this function here? Yeah, actually, and this is part of a it's part of meta gaming in some sense what these challenges are, right? Because there's some organizer that's created this challenge, whether it's a professor in your assignment, or it's a CTF organizer, and here we can see that they're giving us a function. So if we're able to execute success, then we will have a shell on this computer. And so what we need is and the reason why this is important, if we think about the standard operating procedure of this program, right? What does this program do? It says hello, it says any last words, it gets some input from us, it prints out what we said, and then it says goodbye, right? In any of this, does the intent of this program let us execute any arbitrary code, arbitrary functions? Does the intended functionality of this let us read the contents of a file like slash flag? Right, no, exactly. So, but this is here in order to give us a way to easily do that, so that if we call success, then it will give us a shell that we can type in arbitrary commands to. Okay, so we have that, we looked at the C program. And then, so as we're looking through the C program, we wanna go through our list of the types of vulnerabilities and the things we talked about in class, right? So we think, okay, does this program reader write any files? No, there's no open, there's no read, right? There's nothing like that. What we're reading and writing from is standard input. So we can think, okay, maybe all of those classes of vulnerabilities that deal with opening files like path, modifications, home, environment variables, dot, dot attacks, right? Those all kinda go out the window because it's not opening any files. Like how could we trick it to use one of those to open a file? So we think, okay, that goes out the window. We'd think, what about command injection? Are there any, are we using P open or exec VE or anything like that or not exec VE, but exec L or any of those other functions that could allow us to trick it to execute programs that it didn't intend to? Yeah, I know it won't accept the flag. I deliberately did that so it's not gonna affect the scoreboard. You'll know if you get the flag right. Right, okay, cool. So yeah, somebody asked about the flag. So I deliberately, I added these speedrun challenges so you could see them, but you won't actually be able to submit them because even at zero points, I think it'll still mess with the scoreboard. So I want the scoreboard to remain the same on what we talked about. So, okay. So doesn't seem like any command injection, right? There's nothing, we're not calling system with any input from us or anything like that, right? So then that leads us to, okay, well, what about a buffer overflow, right? So the first thing we'd have to ask, are there any buffers in this program? Yeah, clearly, right? So there's clearly a buffer in this program right here. The size of this buffer is statically allocated at 100 bytes on the stack. And then we'd have to ask, is it ever possible for a attacker controlled input to overwrite and write past the bounds of those 100 bytes on the buffer? Yeah, exactly, this F gets, right? We just saw it's gonna read up to 699 bytes and then a null byte. And it's gonna copy that into a buffer that's size 100. So we have a clear buffer overflow here. And at least in this example, we also have a clear place we wanna go. If we can execute the success function and then give that arbitrary commands, then we can basically take over the program. Does that make sense to everyone? Does anyone have any questions on kind of the thought process of how we got here? So now what we're gonna do, I'm not sure what you mean by that question because this happens all the time because think about, this example is very clear that this buffer size 100, but if this buffer was declared in a function that's in a different program and then passed into several functions that get passed in a character pointer and somebody else says, oh, read in 700 characters, they actually have no idea the size of that original buffer. So this type of stuff can happen all the time. Yeah. Okay, so what we need to do, so now what we're gonna do, so we've downloaded speedrun 0, 0, 0. I guess the first thing to do is we'll pop it into Ghidra. So I created a project. I'm going to now, I believe, import that file. Geez, I really don't like, I wonder if I can just drop it in there. Downloads, of course. All right, speedrun 0, 0, 0. Elf file, and so it's stuff that it's already learned about it, so we hit okay. This gives us some results from importing, which is kind of interesting. Like it's telling us how many bytes there are, all kinds of interesting stuff. That it's a 32-bit binary, so it's x86 is the processor, little endian. It tells us some additional stuff down here that libc.so is the specific libc library that this program is using, because this, if we ran a file on it, so if we do file till the slash download speedrun 0, 0, 0, we'll see that it's a dynamically linked. So this means that it uses libc and it dynamically links to it, but that's okay, we don't need those necessarily. And then we'll double click on speedrun 0, 0, 0 to open this up in Ghidra. And it's saying it hasn't been analyzed. Would you like to analyze it? Yes, I would definitely like to analyze it. There's all kinds of analysis that happens here. I actually don't know exactly what all of these things do. I haven't played with Ghidra at all really, besides this, but we can basically not really worry about this right now. We can just hit analyze and then see what happens. So okay, so then where do we go from here? So we have our speedrun. So interesting thing in the upper left, we have all the different sections that are in here. In the symbol tree, one of the interesting things is we have all of the functions. So since we actually have the C code here, it makes sense, let's start with main. So we have the main function. And the very cool thing, I think some of you use this on some of the challenges in the CTF. We can see that actually, what happens is, so in the center here is the assembly code for main, right? Here's the load effective address and add a push. We can see that there's calls to set, oops, call to set V buff, which is what we saw. A call to a function called say hello, a call to a function called what do they say, right? The binary was not compiled with debug symbols, but it was not stripped. So by default, and you can actually see that in file. So it says not stripped. If you stripped a binary, it takes out those symbols. So you can actually see this in object dump, if we go to main. So this is how you get things like a function called main and then the, so you don't have, you lose semantics in terms of variables, right? But you have things like say hello, you have what do they say, say goodbye, these types of things. So you may hear me, may not have the name. If you don't, you can actually, you should be able to rename it. Edit label, yeah, you could call it last. So you can actually change that and it'll change it throughout the program, which is pretty cool. Okay, and the very cool thing is on the right, we get the decompilation output. So here we're actually able to see this is what the, what Ghidra attempts to do from looking at just this x86 code and tries to decompile it to C code, which is very helpful. So we can see there's some stuff, there's the set VBuff, there's say hello, what do you say, say goodbye, which was exactly what we saw here, right? It's actually very similar. You'll notice there's some weirdness, right? With these get PC thunk, there's weirdness around the set VBuff, right? You have a lot of casts in there, which we don't necessarily have. These constants, right? Null, which is zero, this underscore ion BF, these all get compiled to numbers. So it doesn't have the semantic information to know what exactly that is. And we can look at a function like say hello, interesting things in here. So actually rather than, so what happens is if we look here, we can see that say hello is a printf, but the compiler decided to change it to puts. And the reason is because the string is constant, so we don't need all that logic of printf. So the compiler knows this and it translates this to puts, which is kind of interesting. So anyways, we have this here, we have this here, we have, let's see, I wanna see the, I would like to look at it. It must have a, to be perfectly honest, I haven't used this a ton. So I don't, it's usually like a graph view. Does anybody know how I get to the graph view? Function graph, probably, maybe? Yeah, there we go. But where's main? Okay, there's there, window. What if I go to function call function graph? Oh yeah, because the simple function. So anyways, you can see different graph views. This is like an IDA, this is Ghidra. This is open source by the NSA. So you can actually all just go download this and use this. Let's see, function call graph. So this is kind of cool. You can see which functions call, which functions in the program. You can expand that out to see, oh, that's actually pretty cool. You can see what functions call this, what does what do they say call? It calls printf fgets. So anyways, that's very cool. Okay, I guess I shouldn't spend all the time looking at that, although that is cool. Okay, so then let's go and let's get it into environment where we can run. So here I am, I'm on my Mac, right? And so if I go downloads, speed run 000, it'll say permission denied if I do chmod plus x to make it executable. And then try executing it. It'll say exec file format error. And this is because my Mac can't execute this, right? Because it's not in the right format. So I need to either, this is why you need access to either something like the assignment six server, or you can get access to, you can run a virtual machine locally that has windows. And what we can do is copy this to, so we're gonna use scp to copy this here. So what this is is copy, just like a copy from to copy, from this file to umbuntu ad hack me dot, so you can put in your username and the server name. And then the colon says where you're gonna send it afterwards. And this is relative to your home directory. So you could try to do something like this, but you don't have permissions to that. So we'll just say dot and it will copy it there. So now when I SSH there and I do LS-LA, I should see, yep, speed run 000. And just to check, I can do speed run file on that and I can execute it, speed run 000. And I can see hello any last words, type in something and it said, you said this. So now we actually know why when I sent a new line, it didn't keep listening for characters, even though it's reading up to 699 characters. So we have it here where we're running it. So now how do we exploit it? So one of the things I always like to do is to just make sure you can crash it, right? So we're very confident and now we're kind of, I think it's better to look at the, the Ghidra code rather than the C, but we can see here if we can overflow this buffer at least 100 bytes, maybe more, it should crash the program because we're overriding the saved instruction pointer on the stack. So what I always like to do is always just verify that you can actually do this, right? So what I do is just pop in a bunch of A's. I'm not even really counting because I don't care. And so we can see, it says you said this and then it says segmentation fault core dump, right? So we don't know exactly why it seg faulted, but we know that it did. So this, so like part of this process is looking at what information you can look at statically, right? So we looked at the C code, we saw that there's probably a buffer overflow vulnerability, but to actually verify that, we need to input that causes it to seg fault. So this behavior that we're seeing dynamically by running the application is a great sign that now we can exploit this. And now the next thing I always do is say, okay, I can seg fault it just running it. What if I run this in GDP? Now the very first thing before I run GDP, I highly recommend, I see people that come to my office hours for help, but they're not having installed Jeff. Just seriously, just run this. I don't want to install it for you even though technically I could. I'll get to that question in a second. So run this command, install Jeff so that your GDP just looks so much better. So speed runs zero, zero, zero. And now all I'm gonna do is run it with not that input, but the A's that I sent, I'm just gonna send the exact same input because I know this crashed it, copy, paste, enter. And now it's just beautiful. I'm getting a crash. It's saying that it cannot access memory at address 61, 61, 61, 61, which is lowercase A. Oh, for LLDB? Yeah, I don't know. I don't use LLDB very much. So I don't know if there's a similar tool for this. So the buffer overflow works against C++ programs because they have the same problem if you use static buffers, but better. I mean, different languages like Java doesn't have this because Java, every time you access an element of an array, it checks the array bounds, right? Which means you're preying a performance enhancement. No, so I installed Jeff and then you just run GDB. So all that Jeff does is, here I'll show you, it adds this little bit here and installs a little Python file that does all of this stuff, which is super cool because then when you run GDB, it automatically loads this and then I can run and of course I don't have my awesome payload here. But let's go back up. Oh, joy. Oh, I don't want to type in all those As again. Ugh, fine. I gave in. It's not installed on this homework server but you can install it because it's just a local thing. It's literally like one line command you type in or copy and paste in and it installs it for you because I can show you, I mean, really quickly if I move GDB init to .gdb, init.back and then if I run GDB again and I throw in a bunch of As, we'll see that it segfalls but I don't get all those nice registers or anything out of that. So okay, so a bunch of As. Okay, cool. So I can see that I can crash it. So now this means I can definitely get it here. But now comes the question of how do I exploit this? So there's a couple different things to go through. So the nice thing about the servers, I've actually already installed PwnTools on here. So PwnTools has a lot of different stuff. I talked about it before and we'll definitely get into it in later. So run file on your command to C, you can see that it's a 32 bit, oops, that's the wrong one. So speedrun 0, 0, 0 is a 32 bit executable. So one of the cool things that, and I talked about this that the cool functionality that PwnTools adds is this check set command. So it tells us that there's no stack canary, which is nice and we already got it to crash, which is great. And it's not a position independent executable. So now comes the great thing of how do we get, how do we figure out, so we have a buffer overflow. We saw that we can control the instruction pointer. We can control where we go because it tried to access our payload, the 61, 61, 61, 61. Now we need to figure out where do we go. So we actually, we already saw that here when we looked at the C code, we have a success function. So we have a function, if we jump to it, we will get a shell. And we can actually test this. So we can open gdb, oops, that's not a file. So we can open gdb, we can set a breakpoint. So b is the breakpoint, main is the symbol of the where I wanna set a breakpoint. I'm gonna run, and then I'm just gonna jump to success. And that dollar sign means I'm now executing a shell. Of course, I ran this in gdb. We can't do this against the remote system. And as you'll notice on assignment six, when you run a program in gdb, when you debug a set UID program, it drops privileges. So it's not running as that user. Because with gdb, you can literally modify memory and do whatever you want. It's just a debugging tool. So I know, so what I did here is I know that I can, if I get to the success function, I will be able to exploit it. So yes, so I highly recommend gdb. The gdb manual is, the time you spend with this manual is time well spent. I definitely haven't spent enough time doing it, but there is insane stuff. Like, look at this, reverse execution. You can rewind a program and go backwards, which is crazy. You can do all kinds of cool stuff. Macros, yeah, anyways, it's a very, very, very cool tool. Okay, so now we'll click out of here. So the question is, where do we wanna go to? What is this address of success? So actually, if you'll notice, we already know this here because it's telling us, continuing out there, but I wanna go through different ways of finding that. So one thing we, is we have the program in Ghidra. We can look at the symbol, look for success, and say where is the first instruction of success located? It is at 08046561, is that matched with what we saw here? 08048565, that is interesting. Why did it jump here? That's weird. We'll use this address. And if we do, if we use object dump and we look for success, yeah, this must be because the symbol that it uses for success maybe is at that memory location or something. I'm not really sure why it does it here. I guess technically you can, but you know, you should just jump to the first part of the function. So we know if we can control the instruction pointer and we can bend it to 08048581, then we can get this to exploit. So we need to now answer two questions. So we know based on studying and looking at the stack that that buffer that we're writing to is some distance from the base pointer. So I'm actually gonna show you two different ways in order to determine this. One statically looking at Ghidra and the disassembly, and then one with executing the program. So let's look, so the vulnerability was in what do they say? So this is our what do they say, right? So this is pretty much exactly, we have the character pointer here. Okay, so what I like to do, so we know the vulnerability exists on this F gets, right? So we know from this F gets is where we're gonna be. So remember, what are the arguments to this F gets? It's the buffer is the first argument. So what's the first argument to this function? Arguments get pushed onto the stack right to left. So the last thing that gets pushed onto the stack here, this push EAX, whatever EAX is, is going to be the address of our buffer. So then we go up a little bit more and say, okay, what's in EAX? So remember the syntax here that it's using is the destinations on the left. So this says load effective address, whatever's that EB, whatever the address of EBP minus six C, move that into EAX, oops, what was that? That's weird, okay, where was I? F gets. So what is that EBP minus six C in this function? What do they say? Or what can we infer is there? Yeah, that's the address of buff, right? That's where, and if we look at what is six C, actually, this is why I always like to have the calculator app up. You can also probably change it the format here, but I can look it and see that that's 108. So now I always like to go back here and say, okay. Now, what does the function frame for what? What's it called? What do they say? Yeah, what do they say? What does the function frame look like? Right? So we know that buff is here. So it's somewhere on the stack. And we know that there are six C, so 108 from buff to where EBP points to, right? Everyone agree with that? It's literally just this value here. It says to calculate the address of buff, take EBP minus six C. So that must mean the buffer points here, and it means EBP points here. Now, the question is, and maybe we can actually open up PowerPoint and look at this. Going back, reviewing slides, always a good idea. If we go, I don't know what slide it's on. I was gonna say a random number, but if we, I think we can look at this at the function frame level, yeah, great. So here is, yeah, so here's a random slide. All right, so loading this into memory. So we say, okay, here we're in this function my copy. We have the base pointers at FD2B8, which is here. This pointer here. So if we step back a little bit, we say, okay, what is saved onto the stack right where the base pointer currently is? What is this FD2D0 value? Does anyone remember or can refresh our minds? Yeah, it's the previous stack frames saved base pointer. So it's the saved EBP is on the stack, and then what's four bytes above that? Yeah, saved EIP, the saved instruction pointer. And so our goal is we want to overwrite this and control this with whatever we want. Cool. So the question is, how many bytes do we have to write to get from our buffer to start writing into EIP? Right, we actually just from this diagram have all the information we need. We'd say, okay, how far is this buffer from EBP? Right, it's 108, we saw that. And then we need four more bytes. So we need then four bytes to overwrite the saved EBP. So this is four bytes. And then there we're done, and now we're overwriting the saved EIP. So let's test this. So how can we test this? Okay, I'm gonna stop typing in, well, I guess let's build up. I will just run a little ipython. I'm gonna use a times, so we said it was 112. And I'm gonna print this out. So print out, so this is crazy Python syntax, which literally is useless in 99,999% of the time, but it's really useful in this hacking stuff. So here I want 112 a's. So this says repeat that string that many times. And then I'm gonna do a bcde. So print out that plus bcde. So I should be, this bcde should be the address here. So let's do that, print that out. I'm just gonna copy it. I'm gonna do gdb speedrun 0,0,0. I'm gonna run it, I'm gonna pass in this input. And bang, we can see that, look at this. We've overwritten the saved instruction pointer, 62, 63, 64, 65. So everyone see how we were precisely able to determine the offset and the amount of buffer we needed to do to overwrite this saved instruction pointer, which then gets us to do what we want. Cool. Can I show how to find fgets again? You mean like in this? Like this just came from like reading the code basically. And then looking at the man, oh, in Ghidra. Yeah, yeah, yeah. Cool. Yeah, so in Ghidra I went to the function that did this, the what did they say? So no, Ropgad and Roper don't have functionality to find the padding automatically, at least that I'm aware of. So that's part of kind of automated exploitation techniques can do that. But they're not quite at the level where they can do these programs yet. So Ghidra we look at here and we look at this function and we can see this call to fgets. So this is what we're looking at here. So wasn't anything fancy, it was just looking at this function and seeing what that is. And then seeing what gets passed here. So we know that the last argument, the first argument to fgets, which is the last one pushed onto the stack, is at EVP minus six C. It's all about figuring out where that buffer is. Okay, so this is one way to do it. The other way to do it is more of a dynamic way. And if we actually look, and if you look in Pone Tools, like I said, Pone Tools has really, really crazy amounts of functionality. I mean, like you wouldn't believe, like turning shellcode into bytes and the checksec stuff that we talked about, I guess debugging things in GDP, all kinds of stuff, really patching helpful stuff to patch files. Is the reason the compiler did 108? Just because it decided, yeah, just for alignment issues. But this is why it's looking at the source code itself is always dangerous, because you'd look here and you'd say, oh, well it's definitely a hundred characters above, but this is why the source code can lie because the actual thing that gets executed is this assembly code. So by looking here, we'd see it's 108 and not 100 offset. Okay, the cool thing we will look at here is Pone Tools has this cyclic, or cyclic, I think is probably the right way to pronounce that, where you can specify you want two, let's say in this case, 200 bytes, and it will generate four byte offsets that are all unique. So you can see the first one is four A's, the next one is B and three A's, C and three A's, D and three A's, E and three A's. And so we can use this as input. So we can do GDP, run, give this as input, and we can see that it, and I can even examine, this may be, print it out as a string, I wonder if I can do this. No, okay, that didn't work. But I can see that where it stopped was at 64, 61, 61, 62. So 61 is A, 62 is B, so 63 is C, and 64 is D. So it stopped at B, A, A, D, bad. And if we look in our string that we had here, so if we look in here, and actually I'm not, what I like to do is use something like Emax for this, just a scratch buffer, B, A, A, D, oops. And now, so I didn't find it. So why didn't I find it? Because this should be B, A, A, D. Yeah, because it's backwards because of the endianness. So now I need to search for D, A, A, B, and we can see that it's right there. So now this actually told me exactly where this input is that controls the instruction pointer. So that now if I take this and I say, what do we want it to be? Let's, I can take these four bytes and do B, B, B, B. Oops, GDP it, run it, input it, and I messed it up. Why did I mess it up? Because something changed somehow? How is that possible? I must have messed up the input. Let's run this again. You always wanna make sure that everything runs the way you want it to. Okay, there we go. I must have not copied. I was missing a first A, so you can see this starts with four As, and this starts with three As. So be, so now if I change this to B, B, B, B, and then rerun it, type it in, 42, 42, 42, 42. So now I'm able to 100% control that string that gets passed in. Cool. Okay, so this is great. So we know exactly it's 112 is the padding, then save DIP, and then we can get it to go wherever we want. So remember, and what's happening is we're filling in this buffer, overwriting saved EBP, and then overwriting saved EIP. So what we need to do now is if we set saved EIP to this location of the success function, which was 08048561, then we're good. So how do we do this now, right? So how do I type in 08? So let's look at, man, look at the ASCII table. I'll go for 08. We will see that, yes, here as in hex, 08 is actually the backspace character, right? So how do I, like, but if I do this, right? This is kind of the frustrating thing I know about doing this, like how do I actually type in the backspace character? So if I do that, it's gotten rid of my input, right? If I do a backspace, it doesn't say A and then slash eight. And if we go back to the ASCII table, what are the other characters I need? So I had 08 is backspace. I have 04, which is apparently end of transmission. I don't even know what that is. I have, let's say 85, which is, which is not even on the ASCII table, 85. And then 61 at least I know is easy because I know that that's, should be A, right? Yeah, lowercase A. So even I did this, so what if I do this and then I do slash B like this? What is it actually sending? How many bytes get sent into the program? Four bytes, yeah. The double quote, the slash, the B, and then the double quote, right? So this is four bytes in there. So, and even if I just tried slash B, right? These actually get passed in exactly. So we need a way to input bytes into the program that we wouldn't normally be able to do this. So there's a couple different ways to do this. There's like, like it's been suggested, there's, you can actually use Python for this. Python is nice because like we saw with IPython. So we can take this print, we can take this little snippet of Python code. We can do Python dash C, so to execute this as a command as a, oh, hello. That's not what I wanted. We can say, hey, interpret this argument as Python and execute it. And so now we can do that and we can represent as a Python string any bytes we want. So we can do 08, wait, sorry, slash X08, slash X08, slash X08, slash X04. And if we run this through something like hex dump, it'll show us that the bytes here are 08080408. We can also, so the very cool thing, you actually don't, if you're not comfortable with Python or don't wanna deal with that, you don't even have to. So I'm gonna try to not do that here, although I recommend doing that because it can be very helpful. So basically with echo, the echo function has this dash E command, which says use back, use slash, just like you'd expect. So you can actually use slash B, you can use all these and you can use the same format of slash X and then a hex value. So we can actually do, but we can't do echo A, I don't know, times 10, because it will treat that as a string. So what we can do is we can tell Python, hey, give me 112 As, so I can know exactly what that was or because I already use this in cyclical, right? I can actually use this. Okay, say echo dash E, all this and then I know whatever I put here. So 0x08, 0x04, 0x01, 0x40, pipe that to hex dump. And now I can see that I'm able to control this. So 04084010. So I'm just putting in random values. I'm not putting anything important here. So everyone see how I can use this to create arbitrary input, right? I can create whatever bytes I want. What byte don't I want in my input? Yeah, slash N, probably the null byte, although I think F gets may actually be fine with a null byte, but we definitely don't want a zero A because when we read the man of F gets, we said F gets read, reading stops after a new line or an end of file. So a new line in Linux systems is considered a line feed or slash N is what you're used to. The hex value is a zero A. So that's the only character I don't want in my payload. Okay, so I have this. So now, like we said, we'll do this correctly. So now I have this address here, 08048561. I want N now. So we've talked at Nazium about endianness. So I know I need first 61. I then need 85, I then need 04 and then I need 08. So I can look at this then see that this outputs 85, 81, sorry, 85, 61, 0804. Why does it do it like that? Yeah, the cyclical size should be 112. We just started with 120 and figured out where it was inside there. Okay, HD is better than the hex dump. This is great. So we can see that we have 08048561. Perfect, so we can see that we have this address in little endian format. So now, okay, so now the question is how do we get this into the program? Because if we remember our program reads from at least here standard input. We'll talk about the network service in a second, but here it's reading from us as standard input. So what do we do here? Well, I just said I can't copy and paste these bytes in, right, if I did something like this, it's, oh, because of all the new lines. Um, so it's, if I try to do something like this, it's, wow, it's hard to figure out what happened, but basically what happened is this became my input and then it just did a bunch of gibberish and garbage. Right, so I need to actually pipe this exact input into the program. So I actually already have a nice mechanism to do this, right, I have this pipe. So I can use this, I can say, okay, use this as input into speedrun 0, 0, 0. We can do this. I can see segmentation fault core dumped. Is this what I wanted though? Yeah, no, the shell didn't execute. Why not? So let's debug it and figure out what went wrong. So one of the things I like to do the other way to do this is you can output this to a file and then input and then we can run speedrun 0, 0, 0 redirecting the input to the file, right? So these are all different ways that I can get speedrun to read from standard input as if it was reading from this file. So this is one way to do it. I can see the segmentation fault. Now I can GDB this file and I can run it using the input from input. We see here that it had a seg fault, that's weird. So, okay, two things to look at. You can't print that value. You can't print slash X and slash B. So let's go to main. And so, oh no, main is not what we want, but we want, what do they say? And so this is the F gets, oops, this is the F gets. And so the instruction that we really care about is this leave instruction because at this moment is when control should start going over to success. So now we need to debug our exploit and say why the heck isn't this working? So we get this memory address and what we're gonna do is set a breakpoint. Now remember when we do breakpoint main, it's setting it as a, it's setting it as the symbol main, but what we want to do is set a breakpoint at a specific memory address. So breakpoint at 080485E8. And then we do R to run, so we rerun this again. And okay, great. So then we look at here and we say, okay, I've hit breakpoint one and I wanna say, let's examine the stack. So examine 20 in hex, dollar sign ESP. And we see what's the next thing on the stack? It's 08048561. So this return instruction should take us there. So let's go there. So now I'm in success. I push EVP, move the base pointer into the stack pointer, push EVP, subtract four from the ESP, call this get thunk thing, add a value to EAX, subtract C from ESP, doing all this stuff that success should do. Now I'm supposed to be calling system with these values. And so I'm calling system, it should be bnsh. So if I jump over that, I called system and then what happened, right? I didn't get a shell and if I continue, it crashes. I probably should have debugged there a little bit, but so let's look at system, right? So the system library function uses fork to create a process that executes the command of bnsh. System returns after the command has been completed. No, let me do this again. Actually, I'll just set a break point on success. So what I'm doing is doing, so in GDB, n means next, like step over in a debugger, s means step, and then when you're debugging on the assembly level, it's next instruction and step instruction. So all I'm doing is saying next instruction and then when you hit enter in GDB, it automatically just reruns the last command that you did. So I'm literally just hitting enter here. So we'll get here, we get to system, so we call system correctly and we can even look, the value in eax is zero, right? Remember, functions put the return value into eax. Let's look at the return value, the system function returns the exit status of the shell as returned by weight PID or negative one if an error recurred. So we didn't get a negative one, this means that we're actually executing bnsh. This should be great. Oops, ah, okay, restarting, sorry. Okay, next instruction, call the system, what happens here? Well now actually the funny thing is we have now we're leaving from the success function, but where do we go after this? Well, we messed up the stack, right? This is the problem of what happened and this is why we're getting this crashed now. So we're successfully executing success. The problem is bnsh is returning. Why is bnsh returning? How does bnsh work? So I do this, bnsh, what is it doing right now? It's awaiting a command from where? Input from where? Yeah, standard input, it's reading in standard input. I write ls, it figures out what to do, executes it and writes the results back to standard output. And in this program, I'm gonna look at the C, in the program it's no different. When I call system with bnsh, it reads from standard input. What, when I run it and I go with this, what is the input I'm giving to this program? Yeah, I've given it this input here. It actually reads up until here, it reads in here up to the new line and then all of this is, you can think of consumed by standard input, consumed by standard input, sorry, consumed by the fget. And then when we execute system, bnsh says, okay, read something, it gets an end of file because there's literally nothing more to read and so it's done. So we have two options here, I'm gonna show you two different ways. So the first thing we can do is we know that when will fgets stop reading our input? Slash n, exactly. So we can do slash n, we know after this it's not gonna read any more of our input and then what do we want to have happen? Yeah, I'm not gonna type leet, it's not gonna do anything, but let's do id and then a slash n, right? So the idea is I want this input to go to bnsh. So we run that and did that work. These are always great, okay. Don't worry, I have another way to do this, but. Let's run it. Okay, we call system. Okay, it should have executed successfully and it should have read for our standard input. Okay, why didn't id output anything? That is interesting or did it and we just didn't see it? No, because we ran it. Okay, let's try another way. So we'll go back to our echo. We'll do just the payload. Yes, I needed the slash n before id because I needed all of this to get sent to fgets so that we'd go here and then id would be sent after that. Let's do this. We can cat use cat. So if you do cat dash, so most programs will interpret this meaning standard input. And actually if you, why is cat called cat? It's actually called it because of concatenation, not anything to actually do with felines, right? So what we can do, we can actually say cat input and dash pipe that through speedrun. There we go. So here we go. So what exactly happened? So what I'm doing is I'm passing in my input which pipes it, which sends all of this input, this payload to overwrites the instruction pointer. And then this dash means, hey, wait for input from me and it passes all the input from me to speedrun. So I can do ls-la, I could try something like leet but that doesn't, not gonna do anything. But it's exactly as if I have a shell here. I'm talking to bin sh here. And now that I'm able to do this here, the question is how do I do it against the remote service? So did everybody see that we were able to use this cat trick in order to handle this? Yeah, the only thing we changed, the payload is exactly the same as we used before. So the payload is everything like all of our overwrite and then the address we wanna go to and then a new line. Sorry, I don't know why that zero zero is in there but that shouldn't be in there. There's nothing in there. So it's 08048561 and then a new line. So all of that gets passed into the thing. And then now when we pass that input to speedrun, we add this dash which says, hey, cat program, send everything from the first file because it's a concatenate, right? Send everything from the first file and then send everything from the second file where the second file is our standard input. So cat keeps waiting for input from us and then sends it to the program at the end of the pipe. And when we do this, we now have a shell. So we actually have a way to talk to this remote system, right? So we can actually take this, we can say netcat18144.160.224, 9000. So I sent this, I can run ID, I can do ls-la cat flag. So now I've been able to steal that flag and read that flag from here. So input here is just a file. This was just a file that I created. You could literally call it anything, right? So here I'm just using this as a file to store this input. You can actually use, you don't have to use a file here. You could do, oh no, no, you can't do it like this. There's probably more clever ways to do this that people like me can do that. But yeah, you could do something like this, cat input and see what's the remote address. So something like this is the result of all of our work. And there we go. Are there cases where you need to return the save base pointer with the specific value? Yes, so sometimes this is called, overwriting the save base pointer is a technique called stack pivoting. So you can look that up. And there's cases where you need to do that. Like let's say, like in this case, what if, so you can actually try this yourself. You can do, what if this buffer, what if this was instead of 700, what if it was 108? Or, sorry, what was it, yeah, 108. So only enough to overwrite the base pointer but not enough to overwrite the saved instruction pointer. You can still exploit that. You can actually even exploit this even if it's just an off by one error. If the address has null bytes, then yeah, you'd need to work around that. So if our address, so like if this address, if the success function had a new line, where's the, so I wouldn't say you need a completely different approach. What I do is, so remember this is just the epilogue which is actually not that important. So if this had a new line in it, you could actually try going here or you could try going here or maybe you could use an off by one error. Cool. The address in main, so the address I was debugging with was what do they say this one? Cause this is where we've, at this point, we've called fgets, we've overwritten the stack and we're just about to return. So you can have all these tools locally. It's just all this stuff, the PON tool stuff all runs on this machine as well. So you actually don't need any special tools locally. You can do it all on the binary PONED server. Yeah, it should be like the, this was useful remember for GDP, being able to run the program, being able to debug it and being able to come up with the, this exact address and this cyclic payload. Yeah, the hyphen after cat means read from standard input. So like for instance, if I wanted to do, so this is actually, if you've never seen it before, this is the literal point of the cat program is to concatenate two files together. So this says output input file and then output test.cpile file. So dash means treat standard input as a file. So once you have a shell, then you can write any command. So I didn't put it there, but the file, the flag is in slash flag. So you just do cat slash flag and you should be good. Okay, I know I'm running over. So if you need to leave leave, I think what I'll do now, I mean, I don't know if it'd be useful, but I can, then you're definitely don't have a proper shell or you're not talking to the net cat, right? If it'd be helpful, we can rewrite this so that it uses PONED tools. So you can see what that looks like. Would anybody be, it'd probably take about 10 minutes. Yeah, okay, cool. Okay, I'll try to do this reasonably fast. So let's see. Okay, cause we already have everything we need. So if I was normally gonna do this, I'd show you, but I'm getting a beach ball for some reason. But cool. Okay, well, the first thing I'd probably do is open up an old exploit, but I won't do that necessarily now. I'll try to do it from scratch and you'll probably even see me PONED tools documentation. So getting started. Okay, cool. So I'll also, I guess handicap myself. So most exploits are written as like x.py for some reason and I'll do this in VIM. So I'm gonna import PONED tools first. Now I need to talk to the service. So what's super cool about PONED tools is you have different ways of talking to the service. So what I like to do is first have the connection be the process because that way we can debug it locally and you can actually even debug with PONED tools, which is really cool. So we first make a connection and then, so our payload was, I'm gonna have to open up a new terminal. So that I can get all that info I need. Okay, so we know, so we make some kind of payload P equals we have, we'll use A's this time. So we know we need 112 A's, right? So we make a payload of that. And now we need to add our address, which was, I will just go look at it in Ghidra. So our payload, our address in Ghidra where we wanted to go to was the success function. So we go here to success, we need this. And then we can do, how do you do this in PONED tools? It's packing integers, there we go, P32. So we can just say P32. So this tells it, and actually the cool thing is by default it'll use whatever your current architecture is little Indian or big Indian. So we don't have to worry about that. So we can do 08048561. And actually the really insane thing about PONED tools, you can use it to read this ELF file, speedrun 0, 0, 0, and automatically put in the address of success. So you actually don't even need to manually figure this out as long as you know you wanna go to the success function. So I have this, I have this, and then actually this is all the payload I need, then we will do connection dot send line. So there's a lot of different ways to send stuff. So send line means we add a new line at the end, which is important because we're talking to F gets. So connection send new line, send our payload, and then it should be just interactive, I think. Yeah, interactive. And then what's cool is now PONED tools will actually give us a fake shell. Like it'll create a fake shell with the prompt. So it'll be clearer that we're actually talking to this. So remember, I'm not exploiting the remote system at this point, I'm actually exploiting my local one because it's creating a process. So let's save this. So here I was able to exploit it. So here it said hello any last words? Yeah, because I put it in this directory. So it's starting this process. So I always highly recommend always, always, always exploit based on a local system first so that you can make sure that you can exploit it. And then now that this works, now I can open up VIM, change this exploit. And usually what I do is comment this out. And then you can see here how to interact with a remote system. So it's just remote. And it was, what was it? Yeah, there we go, 18. That IP address, which we need to look and see. Yep, it needs to be probably a string. So we pass that in as a string and then pass in the port as an integer. It was 9,000, write that out, run it, and I'm in. But yeah, so this is, you know, this is why getting used to Pone Tools and being able to do this is very, very helpful. You can all, also it allows like really nice interaction with the system, but I just wanted to show earlier, right? We built this without using any Python or anything. So you don't need any requirements on that. Yeah, cool. So I hope this was helpful. Yeah, yeah, for sure. Definitely feel free to post that code. So we can actually see what access I have. So the ID command tells me who I am. So this means that I'm running as the user nobody. Or you can also do who am I and I'm the user nobody. So I don't have root access on the system. But like we said, we were able to go from this binary here that only really gets input and output. Doesn't allow you to open any files or anything. And now we're able to do that. So yeah, good job, everyone. And yeah, thanks. Good luck with the finals. I think everything's going pretty well for everyone. Yeah, handle assignment six and it was a really fun semester. I'm a little bit sad we weren't able to spend the final class in person, but I think you all did a great job. I'm very proud of all the progress that you made this semester. So think about how far you've come when you're poning levels on assignment six and doing capture the flag stuff. So see y'all.