 Before we dive into this video, I wanna offer some love and give a little shout out to my good friend Overflow. Some of you that have probably seen some of my older videos, I recently did a giveaway with his original beginner malware analysis course. Now, Overflow is back in action and he's putting together a new advanced course that's available online at courses.zero2auto.com. This time, Overflow is teaming up with some other researchers and they're providing with this course lifetime access to all the material. And this new course has kind of a schoolhouse vibe to it. There are new chapters released weekly. There's custom malware samples that are kind of fine-tuned, curated and hand-picked to train your reverse engineering skills effectively and efficiently. You're on the fast track to learning reverse engineering. They offer their material both in a video format as well as PDFs containing theory on low-level concepts like windows internals. They even have an exclusive e-book all about reverse engineering, several different modern malware families. There's exclusive access to the zero-to-hero intermediate course containing more than six hours of content from exploit kits to even analyzing the ASUS Shadow Hammer incident. Right now, the course has over 15 hours of content with more being added each week. And the additional bundle contains exclusive access to the malware information sharing platform and sandbox, which can be used to share YARA rules, newly discovered malware samples and more. On top of that, they even have a Slack channel for the course so you can interact with other students. You can share the learning process and experience not just your own learning but grow with others as well. So I'm super excited about this course. I hope you are too. There's a lot of stuff that I wanna work through in this especially for my day job. I'm super fascinated by malware. I wanna learn a little bit more about that Windows realm and get closer to the low-level concepts, get closer to the core with all this stuff. So if you are interested in this sort of thing, please, please, please go check out zero-to-auto.com. If you are interested, I have a coupon code, just my name, all caps, John Hammond, and that will give 15% off to the first 100 people that use the coupon. So jump in and let's go learn some malware analysis. Hello everyone, my name is John Hammond and welcome back from the YouTube video. Today we're gonna be looking at more of the Down Under CTF challenges and I wanna put a disclaimer, right? So I'm gonna be doing a binary exploitation challenge and you've probably heard me before, I try to convey that I am transparent about the learning process and you've also probably heard me say before I'm not the best at binary exploitation, reverse engineering and some of that stuff. I have a lot to learn in that regard but I want to get better at it slowly, eventually, surely sometime, hopefully. So this is my attempt at the return to what challenge that I had solved during the competition and it's been kinda cool to see run-ups and watch other people and see how other people solved it as well. So I'll try and showcase some of that also but that's enough of me talking, let's get to my screen and let's showcase it. So here I am on the Down Under CTF scoreboard. The game is over at this point but let me take a look at that return to what challenge. It's, I guess, rated medium and the dynamic scoring brought it down to 200 points. It says, this will show my friends and we have a dynamic remote target we can connect to with Netcat. We also have some downloadable files and I'll go ahead and copy that so I can download it. I'll fire up my terminal here, hop over to CTF, DU CTF and I think I had in the Pwn category. I'll make a directory for YouTube, return to what and let's hop over there. W, get this thing down. I'm gonna use attack capital O to save the output file because I see the real name is return to what, hyphenated. I just don't want that token in my file name because that's gonna be really messy. There we go. Now I have that return to what file and I'm just gonna run the file command on it to see what it looks like, what this thing actually is and it is an elf binary or Linux executable. 64 bit, that's good to know. So we're gonna be working with registers of RAX, RBX, RIP, et cetera, rather than EAX, E for the extended 32 bit registers. Now we're working with the R, so larger than that. And not stripped. Okay, so we should have some debugging symbols we could look at. Let's go ahead and connect to that remote service just so we kind of know what we're up against. I'll grab this Netcat link and I'll just slap it in my terminal here. It says today, we'll have a lesson in returns. Where would you like to return to? Didn't do anything. Okay, that didn't work. Weird. So we gotta figure out what this is, what this binary file is and what we can do to exploit it. Given the name of this challenge, return to what? Originally, I thought like, oh, is this a return to libc attack? And I thought like, okay, maybe that's something feasible. I've done something like that at some point in the past and maybe I could tinker with that one more time. As I learned more about it, as I kind of unfolded, I realized it was a different kind of return reference and we'll get to that. Let me just kind of walk you through my immediate process as really what we're working at here. We could just go ahead and run like stuff, simple things like L trace or S trace. I should mark this as executable so I can actually run this binary locally. There we go. And I could run something like L trace and it looks like I set a virtual buffer. These are probably all things so that the remote target will behave, prints out all this output. Today we'll have a lesson in returns. Where would you like to return to? And then I see it running the gets function, which is peculiar and interesting because gets is a bad one. If you actually check out the man page for gets, I think it's man three to see the programming side of it. Is that right? Yeah. I'm using Batman or bat the repository for nice color coded command line stuff. If you want to have color coded man pages and cat files or like to be able to display them in your terminal, go check that out. The good description says never use this function. It'll read a line from standard input in the buffer pointed to S until either a terminating new line or end of file, which we'll replace with a null byte. No check for a buffer overrun or overflow is performed. You can read more about it in the bug section here. It's impossible to tell without knowing the data in advance how many characters gets will read. So that is our immediate and knee jerk reaction flaw in vulnerability that we could latch on to. If we want to do some other things, simple stuff, okay, maybe the flag is just in the binary. We could check it out with strings and that was that command that I ran. So as to see something interesting volume here, maybe that's the vulnerable function. These are the strings that it prompted out. Nice, nice. And that was just that strings command that I ran. Obviously, okay, this is very, very basic and fundamental like study and reconnaissance that we're doing. We should really be doing some static analysis in something like a disassembler between Ghidra or Ida Pro or R2, Radar A2 or Hopper. I can open it up in Hopper. I can also open it up in Ghidra. Let me see, do I have Ghidra readily available? I do. Let's see if Ghidra will behave with this. I have a pixel size window. Okay, looks like it's good. I have some stuff old from Google CTF. Let's import a new one. Let's go actually to, can I type in this? So I don't have to, I can't type in this. That's stupid. CTF, do CTF, Pone, YouTube, return to what? Return to what? Load it up. Yep. Okay. Okay. And then let's go ahead and Ghidra that thing. Boom. It's not been analyzed, yes, analyze. It's the first thing I learned when I was working with R2 or Radar A2, you just pass in a bunch of A's. So it's like analyze, analyze, analyze. Tell me everything you know. With that, we can go take a look at some of the functions in here. I do see a main function. I don't know how well you can see this. So forgive me, maybe we should do this in Hopper. I don't know if I can zoom in on that at the very, very least. But I'll copy this code so you can at least get a better idea as to what it is and what it's doing. I'll put it in sublime text. This is the main function. And all it does, it says puts. Okay, today we'll have a lesson in returns and then it calls that vol function that we saw. Okay, so if we click on that vol and we go to it, we should be able to kind of see that here over on the right hand side for it's pseudocode disassemble. Vuln, it looks like we're gonna be reading into a character array or a character buffer, a string potentially, 48 bytes. Where would you like to return to? And then we run gets on that input and that's literally it. We return out of that function, we go back to main and there was nothing else to do. So it's done and over. All we do is we run gets. Gotcha. So this looks like the setup for a classic in common buffer overflow. And typically you could see that in a couple different ways. You could see that with one pre-planted win function where it'll just display the flag for you or it'll give you a shell and that's nice and easy because you just grab the address of that function and you overflow to the point where that function will be called. And then game over, you've won. Other thought for that is if that binary is running with an executable stack or with DEP, the data execution prevention or NX for non-executable bit, if that is off, if it's on, then we don't exactly have a lot we could work with. There we go. Okay, so I ran check sec, which is like checking the security functionality of a binary there. I believe that's part of Pone Tools. Yep. And it looks like, okay, we know we have a 64-bit architecture and NX is enabled so we can't execute off the stack so we can't use shell code. That would be the avenue we would go down if we were to be able to execute off the stack. We would try and load some shell code into a buffer, potentially the same one that we have given our input and execute it so we could run arbitrary code. We could have a flag, we could run a command, we could get a shell, we could do whatever we'd like with that, maybe carve out a code cave, whatever you particularly want to do. In this case, we can't do it. So crap. Okay, what do we do? Regular gets function that will give us a potential buffer overflow but nothing to easily do with a buffer overflow. That's annoying, right? Gimmick here, and this is where we'll transition to, okay, our actual agenda. If we have this buffer overflow and we know we're not doing a return to libc attack, we know we can't use shell code, we know we're not buffer overflowing to any other potential function or anything, what could we do with return oriented programming? Return oriented programming. Here we go. Return oriented programming or ROP is this computer security exploit technique that allows an attacker to execute code in the presence of security defenses such as executable space protection and code signing. So that executable space, excuse me, executable space protection is what we just looked at. And X is on, we can't execute off the stack and we don't have anything that we could easily, easily latch onto. So with return oriented programming, you essentially are wanting to find small snippets of code or small portions and fragments of the binary itself of its real instructions that will do something in a function, and maybe I can find this in Ghidra. We'll get portions of code and instructions up to a return function because that return function would normally allow it in the program to bounce back to where it was previously in its operation. So you look for just what might come before a RET call or a return call. In this case, it's gonna pop RBP or another case it's going to do something else. And you'll try and find as many of those things as you can and those are going to be our Rop gadgets. These tiny, tiny bits of code, these fragments that allow us to beep bop around the computer binary and eventually chain together what we want the program to do. Maybe call a function or load arguments and do particular things. That's what we can do with return oriented programming or Rop. So you'll chain Rop gadgets. The way we could do this, again, Rop gadget. Don't quote me on this. I think that is from Pone Tools. Let me do a quick Google search. Rop gadget I can type is in, maybe it's a, okay, it's a product with capstone. Yeah. So it's not strictly Pone Tools. Sorry, forgive me, but it is something you can pip install. Rop gadget is super duper easy. You run Rop gadget and you say, oh, I need a binary to actually look at. So okay, you run tag, tag binary and then we pass in our return to what program. So this is going to be super duper cool because this will show you as much Rop gadgets as it will find. And you'll notice at the end of every single, one of these obviously it ends with a ret or a return or something that will bounce it back to where the program was previously. But you take these small snippets of code, whatever they might be doing in assembly or the actual instructions of the computer program to slowly build out and load things into registers to be able to call and execute other things. Okay, that was a long-winded explanation that we probably didn't need to go over because a lot of you already probably knew what Rop was. For those of you that didn't, I hope you enjoyed that quick Safari Crash Course deep dive. Now that we know what we're doing, we're going to end up using Rop gadget. We can see all of these potential things that will help us get data into registers and that's going to be super duper important for us because if we want to run code, if we want to create an exploit, obviously we want to get a shell, right? We're going to fire this off to a remote target and we want to get access to that machine. We want a shell on that target. So we probably want to call system and execute a command to get us a shell. In order to do that, we need to stage the arguments which are going to be translated as registers, what we're working with in assembly here. We need to get those aligned with the proper values in the arguments. Since we're on a 64-bit processor, 64-bit calling convention, the way we're going to end up doing that, the way that we have to get those arguments in the right place is very, very specific. If you were to Google x86 calling conventions, x86, that's going to be 32-bit, right? We want particularly 64-bit. So we'll go over to this down section, x86-64 down here. There are two ways to do this. You could use kind of the Microsoft rendition of x64 calling conventions. You also have the system v, excuse me, your system, I guess five, system v AMD64 ABI. This is the system v 64-bit rendition of calling conventions that it's followed on Linux and pretty standard among Unix and Unix-like operating systems. The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, R9, et cetera, et cetera, et cetera. Nice, okay. So we know we need to get our arguments into RDI and then RSI for other arguments, et cetera, et cetera. That's the gleaning information that we needed to get out of that, to understand and know how that works. This is great. This is good. Problem is, we're probably gonna end up working to work, we're probably gonna end up wanting to call the system function, but that was not in the binary that we have here. We have, I guess, main, which is the, there to find function, vol, again, there to find function and we have gets and puts. So that doesn't exactly help us. We need system, but this is probably going to be ran with libc or the library of other C functions, but we don't know what version of libc the program is using. Like we could see it on our host, but that doesn't help us. We wanna know it on the target. We need to know it on the remote service. So we need to go ahead and get a leak out of this program to be able to find where are you putting your functions? How are you defining the structure and the layout of the code here when you run this program? Where is libc and all of its functions being loaded? So, all right, I've done a lot of talk. Now let's kind of set the stage. Let's start to create a Python script and let's work with this. I'm gonna be using PwnTools, so I'm going to from Pwn import all and then I'm going to load up this binary as an elf object. We can do that nice and easily with PwnTools. So I'll just say elf equals elf and then if you wanted to, let's just go ahead and print that out. Lowercase elf is going to be my object and variable name. I can see the sun starting to come in here. So if I get a halo on the side of my head, fair warning. Let's fire that up. Okay, and we've loaded that binary and you can see it's gonna give us that check sec output very, very nicely for us. Great. With PwnTools, you can do kind of some peculiar stuff. You can actually check out the symbols that this binary might have. There's that print function over there and I'll spit this all out and that's a lot of output and it's gross and it's hard to look at. What I like to do is actually import pprint because pprint will let you to like pretty print and that will allow you, hey, if you have a giant blob of a dictionary value or big long list and you wanna kind of get that output line by line, you can pprint it. It is relative pprint module pprint function so you have pprint dot pprint. I prefer to just from pprint import pprint so you only need one pprint. I've said that word a lot at this point. Let's stop doing that. Run this and now you have a much nicer and cleaner output. So these are going to be the locations, kind of hex kind of where they are, the data that they're loaded into in the binary at that point. So you have our volume function that we saw there. It's Python's denoting it in decimal but we can know okay, regularly that would read as a memory address in hex. You can see the same thing for gets, you can see the same thing for puts, et cetera, et cetera and of course we have entries in the GOT, the global offset table or the PLT, the procedural linkage table. We really care about okay, where is the global offset table address going to be because we're going to want to use that to determine where our puts location will be or our functions will be or our gets location will be relative to the libc library that we load. So let's start to work with this a little bit more. We know that we have a potential buffer overflow but we still haven't found it yet. We've just kind of talked through it. So forgive me for that. Let's go ahead and try and hammer this binary. When you run return to what and it asks you for input, you probably would just hold down your A key and be like I don't know and you might get a segmentation fault. Question is where did that segmentation fault occur? What is the value of your instruction pointer? Did you clobber the instruction pointer or RIP that register? So I'm just going to check that with D message and it looks like it got a general protection fault way down here. So I probably had a little bit too much A's in there. I want to be able to know how many A's or input that I provide the character that I'm going to use to overflow that buffer. So I'm going to end up just sending it as actual, I don't know, string that I can multiply and manipulate with Python. I'll use Python taxi to run just a simple command as a string. I'll print out the A character 32 times and I'll pipe that into the program. 32 characters didn't seem to do anything. So let's try and amp that up. I'll bring that to 42 characters. Still no SEG fault. So maybe that's not enough to fill the buffer. And we saw in the code anyway that we know we have a buffer of 48. So we've got to be just a little bit after that. How if I bring this to 52? Still didn't crash, okay? What about 62? There we go. Now I see a segmentation fault. Let's check out D message one more time and awesome. I see a SEG fault at 4141, 4141, 4141. So all of our A's, all of our hex representation of A. I have one, two, three, four, five, six in there. And you can tell this is a bad instruction pointer. So I want to take out those six A's. I want to try 56. And I want to see if 56 is the sweet spot where I just barely touch the instruction pointer and now it's clobbered with a null byte or a zero. It looks like it is. So 56 is that sweet spot. That's where the offset is where I can start to make a mess of that instruction pointer. That's good for me to know because I can use that in my exploit. Let's say offset is gonna be 56 and let's start to automate this. Once you have this L file, you could simply run elf.process and you'll have now a communication like vehicle, right? You can interact with that process. You have some PoneTools tubes to interact with that. So let's do print. Let's take note of that offset before I drive away. Let's try and receive information from this p.receive. And let's Python three, eight. So I can run that script and there's our beginning line. Today we'll have a lesson returns. Where would you like to return to? I'm gonna receive until new line so I can be a little bit more explicit in what data we get and when. Let's do that twice. There we go. And now we should be at our input option. It should be asking us and allowing us to type something in. So let's put together a payload. Let's try and say payload equals let's create a list here. This will be kind of fun because we're gonna wanna smartly join together what our exploit chain stuff is going to be. We're gonna use that A value times our offset so we know that we flood up to the point of the overflow and we also want to determine where we're gonna go next. I'm just gonna actually end up sending it like Bs just so I can verify that okay, we do have our instruction pointer overflow. We can clobber that. And I'm gonna join this payload together. I'm only using it as a list here so you can kind of visually see the pieces of our payload but then I'll put it all together into one line so I can just simply P.sendline our payload and then make that process interactive. And it should error. It will receive that segmentation fault in our case, right? Let's send it, there we go. Switching interactive mode, it got end of file while reading an interactive and it just simply died. We got a segmentation fault or a SIG-SEV. If I check out D message, did I send it too much stuff? I might have, let me turn that down. Or maybe I just won't be able to see it. There we go, no, no, no. I got our 42424242. So send to couple B's. Good, rather than our A's. But we know that offset is correct. That is what we want there. Okay, enough of me talking. Now let's get back to what we want to accomplish. We don't know where libc is or where all these functions that the binary has loaded are. So we need to be able to determine that. But by default, we don't have a memory leak. That's not something that our program will allow us to do naturally within the operation and functionality of the program. But since we have that overflow, we know we could call other functions. Like if we wanted to, you could just simply run voln one more time. I'll show you that. We were able to go ahead and do that elf.symbols earlier. If I were to index that with voln, it should bring us right back to the voln function. Let me show you that. I'm going to type and I need a bytes there. So let me actually, obviously that makes sense, right? That's a good point that I should bring up. We said elf.symbols voln and that only returned that integer value or as to where that function is in the program. We want to go ahead and pack that. So let's do p64. And that will put it in the correct endianist notation that the binary will be able to read it and understand it. So let's send that along. There we go. And it says today we'll have a lesson in returns. Where would you like to return to? And I sent it the overflow and the address of voln again so I can get back to that voln function. And there we go. It just ran it. Where would you like to return to? So we ran voln twice. We know that we have control over this program because of this overflow. Cool. What can we do to get information about how this program looks at runtime? Where can we get this memory leak? Well, we know we have puts and puts will display something out to us. So we could use puts to get the address of puts which is funny, right? We could use it to get the address of gets. We could use it to get the address of whatever we'd like that we have access and visibility on within this program. So let me try that. I'm gonna do that with PwnTools and I'm gonna use a little bit of PwnTools magic here. I'm gonna say Rop equals Elf, excuse me, Rop of Elf. Now, PwnTools allows you to do Rop return, excuse me, return-oriented programming. It's kind of overpowered and really, really cool but it allows you to take the concept, hey, conceptually I know that I need to call this function and I know that I need to pass an arguments within RDI and all the other registers that we researched and looked up earlier and it will kind of put that away for you so you can focus on creating your Rop chain. It's kind of nice. So if we create that Rop object, we could simply put together a Rop.call and let's try and run puts. Easy peasy. It's gonna be able to determine and know or at least it should be able to be determining and knowing, okay, where is this going to put, excuse me, it's going to be able to know the location of this in the binary. If I just include this as a string, I don't need to explicitly say Elf.symbols puts or Elf.plt or gft. It should be able to track that down when we're calling it but we do want to include it in the arguments. So if I were to say puts and then I'll use a list here for the arguments of that function, let's use our Elf.got puts. There we go. And now we'll try and call our vol function just to go back to where we were previously. So that creates this little Rop chain for us but we actually need to put it in our payload so let's do Rop chain and then let's see if that will work for us. I might not have that syntax, right? Let's try it one more time or maybe it does need me to be explicit. Let me do that just to be sure. Let's do Elf.symbols puts and Elf.symbols vol to try and send those along. There we go. And that still failed. Maybe, oh, I should probably explicit, I should probably be explicit about the architecture here. Let's do context.arc equals AMD64 for a 64-bit processor in our architecture just telling poem tools like, yo, this is what we're up against and let's see if that behaves, please. There we go, okay, cool. So this payload, this output, right? This data that we've just received is going to be the value of puts or where that puts location is and how we're loading libc. Let's actually extract and carve that out. I wanna be able to see that data. Let's do p.receive until as we did earlier and let's just say this can be puts and I wanna print that out. Let's do a log.info so we're being very poem tools like. Log.info puts found and get inside the string, please, at let's use an F string so I can really easily just throw in like hex of puts. Easy peasy. And I should probably strip that. Yeah, let's do rstrip to get rid of the new one at the end and then let's try and L justify it so it's gonna be padded with eight null bytes, right? See that, let's see if that works. Python 3.8, ljust argument two must be a byte string of length one, not a string. Oh, sorry, that does need to be bytes because that was read as bytes. Original hex number, bytes object cannot be interpreted as integer. Oh, that is, that is, it's gonna be returning to me as bytes so I need to get the numeric value out of it. I think I need to use 64 that. Before I do all this, let me just make sure that it is actually what it should be. No, can I print puts? Yeah, so it gets it, but we need to strip it, rstrip as we did earlier. Sorry, I'm gonna be frantically going back and forth and I know that's not helpful to you. There we go. Okay, now let's take the integer form of that so let's use 64 and unpack that. There we go. Unpack requires a buffer of eight bytes. Ah, so that's why we need to ljustify that of eight and bx00, send that along. Okay, there we go. And now we can smartly print that out with our log info so we did find the puts value. There we go, puts found there and we want to now look up where that might be in libc. So I'm gonna use a libc database over here and I have this good one online, libc blue cat me. So let's look for where puts might be with this address. I'll do find and now I found libc6, amd64, 2, 3, 1, 2, ubuntu. So this is probably definitely my libc version which doesn't help us, right? Cause we still want to do this for the target. Let me download this anyway, just to kind of give you the proof of concept and then we can trigger this and switch this to do it for the online or the remote target. Actually, you know what, Lowe? No, no, no, we've been going on for a long time already. Let's just do this on the remote host. So rather than running a local process, let's do a remote connection of that shall ductf and I think it was what, 3,001? I'm a reverse search, my net cat connection, sorry, 30,003. 3,0003. There we go. Now let's see if we get some good output for it and we don't need to print these things anymore. Let's make a nice exploit. Python three, Ape connects to it, loaded gadgets, puts found at this address and we're back calling Vuln one more time because of our ROP chain to find that. Let's look for this database. There we go. And I see a libc6 2.27 Ubuntu AMD64. So I want to look for the AMD64 stuff because I want to make sure that it's going to be the architecture that we know. This AMD64 makes sense to me. So let me download this. I'm gonna go ahead and put this in the same directory as our script, pulling that down. There's a little bit of stuff to it. Let me pause the video. Okay, that has downloaded. So now let's load this libc in. We found the address of puts so we can get the libc. So libc can now equal an elf of this file. You can take in the elf representation of that libc that we downloaded. And we can go ahead and start to build out another ROP chain. Let's do ROP on libc. Super easy. Now let's go ahead and try and call what do we want for our ROP chain? Let's call system. I guess let's use like libc.symbols for that. If we're being explicit as we have been, just for maybe good measure. And then we need to pass it, something that we want to go ahead and call. We wanna call bnsh, right? We wanna call a shell. So we need these arguments passed in as a list. And you could use libc.search to find an instance or location of a specific string. And that's probably gonna end up meeting to be bytes. So let's pass that b prefix in. And I'm gonna use bnsh, and then I'm gonna include a null byte back at the very, very end because we want it to terminate that string. So this will return an iterator. This will return like a list of things you can loop through. So let me actually wrap that in next so I can just get the first occurrence of it. That should spit that out for me, right? Let me actually just write out that payload after the fact so we can make sure and we can see it in there. So we've called system bnsh and now let's just try and call like exit so we can cleanly get out of there. Let's run libc symbols exit and that should be a okay. So because we know we've got ourselves back at the vuln function previously, we can just start to build another payload. So what I'm gonna do is I'm just gonna take all that up there and the offset remains the same. We have a new Rop instance and now we're gonna end up trying to call system bnsh and exit and cleanly work through it. So we send line and we go interactive. So let's see if this will work. Vingers crossed. Actually before I do, sorry, sorry, I wanna go ahead and do like a with open payload WB, WB to write bytes and let's as h, h.write payload. Just so I have the sanity check to make sure, okay, that libc will work a okay. Let's Python three eight puts found loading gadgets for libc that's gonna take a long time. That's okay. And now I have this payload file. So I'm gonna hex edit this payload. It has a, a, a, a, a and seemingly nothing else interesting in there. Where's my bnsh payload? Did we find bnsh? Did we run system? Why did that fail? Oh, you know what? I'm sorry, I got ahead of myself. I was just rearing to go and I kind of forgot the fundamentals. Hey, once you've found libc, we need to put the base address for the runtime of that program in the same context as our current rendition of libc. We need to tell it, hey, okay, libc, I want you to map your base address to what is currently being used and ran with that program. So because we found the puts address from the currently running program at binary, we need to be able to say, hey, libc, your address should actually be some quick math. We need to take the puts address that we found minus your rendition of your already loaded puts value. So that should kind of stabilize and normalize our libc and make it the same address as what the program is running it as and where it's loaded. So our libc that we found and just take the difference as to where you previously had libc. And now let's actually just log that out. Let's say, hey, libc base address determined to be libc.address and let's try to run that. Found our puts, there we go. We found our libc base address and that makes sense. That those trailing zeros makes this look normal and sane to me. How about our payload? Can I strings that payload? Can I hex edit that payload? I don't see a notion of BNSH in there, but you know what? Let's try and fire it away. Maybe my payload was kind of muffed up. Let's just try it, send it. Let's see how we do. Python 3.8, load of 14 gadgets. No. Did it die? Did it die? Yeah, it died. Okay, got EOF. Try to run commands. Try to see if it would get the shell. Did we, no, no, no, yeah, okay. So we load it and then it determines the address. Totally fine. And that all looks right. I'm just gonna, maybe we don't need to be telling it to use libc symbol system. Let's just use system and have it try and get that on its own. Maybe that will make me feel a little bit better. And I removed that right, but now I realize I want it back because I like that sanity check. So I control Z to get that right payload down because I wanna be able to see if it will tell me the payload that I'm working with. Next libc search, bnsh, and that's all right. Load of gadgets. It still died. Exit at payload. What the heck? Let me, we are getting that value, right? Let's call puts before we jump in just to see if we see it. It has been sh. What? What? And I am, okay, okay, okay. So that shall work. That shall work. Nice, good job. Great work everybody. This is exactly what happened when I tried to solve it the very, very first time. I got together this system calling lib, calling bnsh to get a shell and I didn't know why it didn't work. I would run it and it didn't give me the shell when I would have expected it to. And then I just tried like debugging. I tried to put a puts here and just make sure that I was actually finding the string that I thought that I was finding. And then when I included that, the shell came through. And I don't know how or why that made the difference because I think like, oh, it's not stack alignment. Is it? It's not doing anything specific or peculiar just by calling puts beforehand. Is it clearing out registers that I'm just not aware of? I don't know why that worked. Regardless, we do have, a neat, nice little exploit script. So if you can follow along, let me do a quick crash course and overview because I know I went through a lot and kind of got pulled in different directions. That probably wasn't the best for learning but we're working with Pwn tools. We don't need Pprint anymore. I set the architecture for this as 64 bit because we know we're working with a 64 bit binary. We did our own testing earlier manually and found the offset as to where we can start to overflow the instruction pointer is 56 characters in or 56 bytes in. So we're working with our local binary. We connect to the remote service and then we do some Pwn tools magic to start to do some Rop or return oriented programming. First we need a memory leak so that we can actually determine where are these functions being loaded specific to this binary at runtime pertinent to its libc. So we use that puts function to actually tell us where is the puts function. We use it to display it on centered output so that I can see the address in the global offset table as to where that function is. That's our memory leak. That's allowing us to go find and gather the proper location of that value and get the libc that we need. We call back to the original Vone function so it allows us to once again send another payload and we put together this Rop chain after our offset we send it along and then we leak out that puts value. With that, we could use the libc database to try and find that version of libc and we could determine that proper base address just by simply, okay, calculating that offset and finding the difference there. Now that we have libc, it's easy. We can run home. We can just go ahead and call system with bin sh and get a shell for some reason. And I'm excited for hopefully some of you explain to me why did I need to put this other puts in there? I'm still trying to learn. Always been transparent about that. Why did that work? Payload new regular offset as we did before and this Rop chain to load libc. Send that payload along. We don't need to write it out anymore because we know that that would work. I'm also not positive why it didn't include the location there but maybe that was just getting the values or like the location of this bin sh and not actually the string, literal string itself. So I'm totally okay with that I think now that I've stared at it enough but thank you for putting up with me and tolerating that. So, okay, this has been an extraordinarily long video and it really didn't have to be but I wanted to get in the weeds as to what all of this is really doing and how this makes sense. So let's give it one more bird's eye view. You can see all of this code and we'll put it side-by-side our exploit script running and off in action and we can get a shell on that target system. So who am I? Apparently we don't have a username but we do have ID values and we can go ahead and cat that flag. Awesome, awesome. Thank you so much for watching. Before I tune out, I do want to showcase some of these official writeups. They had some over on their official stuff. They have that noted here in CTF time. Can I get to the events, please? Oh my gosh, there's a lot of scrolling on the CTF time page. Event tasks and writeups, great, great. Let's go check out return to what? Six writeups. Cool. Let's open every single one of these. So let's see what everyone's doing. Just for our own learning. Gets provides the buffer overflow. Okay, they do the same thing we do essentially with Rop. I don't know what that is. The go-to table is leaked with call of puts. Yep, that's only possible because PIE is disabled. Does that make sense? Position independent executable would kind of rearrange and make that very, very hard to do. If the position independent executable were on, then I believe that entry for that function would be a different place every single runtime. I mean, it is, but we wouldn't have the actual offset. We wouldn't have the real value. It would be some distant location. Oh, they use one gadget. Nice. Pwn scripts, that's cool. What? Holy cow. You can just find it with libc database. Wow. I need to look at that. Okay, slick. This is why you read writeups, guys. You learn things you had never seen before. That's awesome. What is this hex? What is this hex 38? Python, is that 56? Yeah, it's just the offset that he found. Okay, did you automatically find that? Did Pwn scripts just like figure it out for you? All right, I don't think he covers that. Ooh. Okay, so yeah, they link Pwn scripts over here and I definitely want to take a look at that. Very cool. That's slick. All right, I want to look more into that and I hope maybe that's some gem that you got out of that. If you, thanks for sticking with me for so long, long, long video. Return to what? They use Rop here? Yep. Okay, they do the exact same thing we do. Yours is a little bit prettier than ours because they're using big capital letters. Nice. Okay, yeah, same thing for what we do earlier. You get a shell. Easy peasy. Return to what? Let's check out this one. Once again, using Rop. All using Rop. I thought the official one, let me pull it up. I'm gonna get the Discord open. Okay, here we go. Excuse me, the official write-ups that they posted in the Down Under CTF Discord did include kind of the raw rendition here where you're not using the Pone Tools wrapper to do Rop. You are a little bit more manual and including your pop RDI. So you can get some argument into the specific register. You can get your argument in there as needed for either PUTs or the bin SH that we have to supply for our system with C. So that's kind of neat and good to look at. You do their offset, you use pop RDI and then you get the argument that you want to pass in for the GOT value of PUTs. And then you call it with PUTs and you go back to the main function. Nice and easy. And then after that, we leak out the memory address as we did earlier. Looks like we go ahead with libcbase. What is this constant value? Does that need to be there? What is that? Leak minus, okay, that must just be like, is that their known value of the base address or something? And they add on the original libcbase. Oh, oh, oh, oh, oh, oh. Is that like where it's loaded from for that binary and they're adding in the others for system and bin SH? I'm curious what that is. Yeah, at libc then. Okay, okay, that, okay, okay. Send out of that, that second payload. Very cool, very cool. Okay, thanks for bearing with me, everybody. Holy cow. That was very, very fun. I hope you enjoyed it a little bit. It's very, very cool for me to look through write-ups and to kind of understand a little bit more. It's also very cool and fun to solve it in the moment. But I definitely want to look into this. Is that PoneScript's tool? That's something that I've got to check out. I hope you guys do too. Alrighty, this is a super long video. Thank you so, so much for bearing with me, everybody. You've been fantastic. You've been an incredible audience. I'd like to thank the Academy. I'm just kidding, this is stupid. Let's try and end the video before it gets too weird. Thanks so much for watching, everybody. If you did like this video, I know it's been crazy long. Please do press that like button. Maybe leave a comment, maybe subscribe. You know I'm super, super grateful. Thank you, thank you, thank you. I can't thank you enough. I hope to see you this weekend for the Beesides Boss and Capture the Flag competition. I'm hosting it. I've got a good team of people, incredible challenge developers that have put together a ton of great challenges and you're gonna have a blast and I'm really looking forward to it. So please, please, please come hang out BeesidesBoss.ctf.games. I'll see you there. Have a great day, everybody. I love you.