 Hey folks, Adam DuPay here, and today we're going to be looking at ponables.kr, the passcode challenge. So looking at this challenge, we can see that it's 10 points, which is the most points we've seen so far. And so the description says, mommy told me to make a passcode based login system. My initial C code was compiled without any error. That's always good. Well, there was some compiler warning, but who cares about compiler warnings? True that. Who does care about compiler warnings? Actually that's a joke. Compiler warnings are very serious. You should heed them as we will see here. And we can see that this flavor is it's giving us SSH access to a machine, passcode.ponable.kr on port 2222, and that will tell us and take us where we want to go. So if we look here, LD dash is not what we want. We can see LASH-LA. Again, we're in a situation where we have passcode.c, which we can read. We can execute passcode and passcode. Interesting. They keep switching how they do these around, but I guess the end result is the same. So the owner is root, but the group is passcode underscore ppon when the flag is readable by group password code underscore ppon. And so we can see that this S means that the group setUID bit is set, the set group ID bit is set, which means that this passcode binary will execute as with the group privileges of passcode underscore ppon. And so this means we should be able to get our flag. All right. So what is this passcode? So let's passcode.c. Whoa, this is much bigger than any binary, any C code we've seen so far. So let's kind of, I like to take kind of the broad overview strokes here and kind of look at like what are the high level functions and then start digging into what's actually here. So here we have three functions. We have a login function, a welcome function and a main function. So let's start at main. We'll just walk through it. So main printf toddlers secure login system 1.0 beta. It's going to call the function welcome, then call the function login and going to print out that, okay, now they can safely trust that we have the credentials. So presumably we've got to bypass this login system somehow. So let's look at welcome. Welcome, name, character buffer 100 on the stack, printf enter your name, scanf percent 100 s into name and print out welcome space percent s to your name. Okay. So the first thing we think about as we're reading this is going mentally note. Okay. There's a scanf here. I need to look at the man page for scanf to see this percent 100 s. What happens when, so even though I know it's going to limit. So if you've never seen this, you need to understand what this means to scanf. But this 100 s means it's going to copy in a hundred characters and only a hundred characters. Now the question is what happens if you enter in more than a hundred? What happens to the terminating null space, but that's okay. So we have that there. And then we have the login method that gets called after welcome. So the login method is an int pascode one, int pascode two, and it's saying printf enter pascode one, scanf percent d into pascode one, then f flush standard in. And then a comment that says how mommy told me that 32 bit is vulnerable to brute forcing. So enter the second cat passcode, passcode two. So it gets passed in the scanf and then printf checking. And then checks if passcode one is equal to 338150 and passcode two is equal to 1371337 then print out login, okay, call system bin cat flag, otherwise print out login failed. And so now we would think that this would be a very easy thing to break, right? We'd think that, okay, this is simple. We just have to pass in passcode one is this value and passcode two as this value. But let's keep that in mind and let's start executing this to see what it does. So again, like I always say, we want to run file on it to understand what it is. It's a set group ID elf 32 bit binary and we can run strings on it. Just to, you know, normally, I don't necessarily think we need to do this if we have the C code, but hey, you know, always good to see that it is what we think percent s welcome. Great. All right. So let's run this. So this should be on first look, this should be incredibly easy to do, like super easy enter you name you name. So that it says enter passcode one and two and let's do this again. Hello. Okay. And then I don't happen. Okay. Then let's do this passcode one should be that 338150 and we get a segmentation fall. So what happened? This is when something that seems to be crazy happens that we should then go back and recheck our assumptions and say, okay, why did this actually happen? So going back to this code and looking at this code, if we've relook at this scan f function, we have scan f percent D, but we can see what we're passing in here. So normally the scan f function is going to read in percent D means read in something as an integer and write it to the address of whatever is passed in as the argument. But here passcode one is not being passed in an argument. It's not a integer pointer. It's the actual integer. So the value of whatever is inside passcode one will be passed into this scan f function, which is crazy. I mean, this is, so this is a very interesting problem. So that's what's going to be written there. So what we want to do is we want to try to recreate this, this fault in GDB. So enter your name, yellow, passcode one, not that, let's do this one. And so we see we get dropped into the debugger and it's saying that the thread passcode one stopped for reasons except. So there's a seg fault. That's what's happening here. So we can see, we can look at the back trace here. So this is what's the nice thing about GEF is showing us this back trace. We have main we called login, which called underscore underscore ISOC 99 scan f, which called this method. And we can see we're in here and the syntax here is we're moving eax into wherever edx points to. So if we look here in the registers, we see that edx has the value f7, e7, 5C, AB, which so we're trying to copy what's in eax. So eax is, this should be our value that we put in. So let's verify that and yes, this 338, 150. So that's what we want to be in there. So we have 338, 150 and for this value, we're trying to copy it here, but why are we copying it here? Right? This seems like it doesn't make sense. Like what is this f7, e7, 5C, AB? So let's breakpoint on the login function and restart it. So yellow and now we're at the login function. So actually, so we can see because we're using the symbols here, we didn't actually breakpoint at the very first login method. So we have login 0, login 1. So we have pushEVP. So this is the epilogue to the prologue to the function to set up its current stack frame. PushEVP, you move the stack pointer to the base pointer, subtract 28 from the stack pointer. So if we print out X20WX$EBP, so we can see some of the stack actually here. So we can see that there's things on the stack. One thing we want to look at, let's pop over to Hopper which we haven't opened it up in. So now we want to read this executable. We want punnable.kr, we want passcode, we want to give us passcode and start this analysis. Good. So we will have main, we know we're in login. So we see, okay. So we have this printf which first prints out what we want, then we have the scanf. We have the format argument which is not super interesting, the argument to scanf. So what we do have is the next value to scanf. So this is what's going to be put on the stack. So what are we moving? We're moving EBP plus 10 here, 10 is negative 16 into EDX and then moving that onto the stack. So it's going to be the second argument to scanf. Let's see if the pseudocode, yeah, so it's saying variable 10 with this parameter and it would look better. So we can do things in hopper, like go here, tell it, hey, this is a string, this is a string, this is a string, this is a string and then if we go back to our function and now it'll actually look a little prettier, what it'll say, hey, it's call scanf%d with variable 10, passing in variable 10. So we can, so this EBP. So let's look at EBP. So we'll examine 20 wide X EBP, dollar sign EBP minus 16 and we noticed something interesting. So what's going on here? So the compiler has put and has stated when it compiles this that passcode one is going to be at EBP minus 16 and passcode two is probably going to be right above it at EBP minus 12. We'd have to look at this. So let's go to the second scanf, we have variable, variable C, so EBP minus 12. So this means let's draw a nice stack, well, I don't know about nice stack, but hey, we'll draw a stack and you'll just have to deal with it. So let's go here. So this is for our login function, this is EBP and we have at EBP minus 16. So this is minus 16 and sorry, minus 16 and then minus 12, so four above that. So this is passcode one and this is passcode two. So what's happening here? Well, it's because when we look at this code, it's using passcode one, the value that's there and passcode one from the C code has never been initialized. So what it's going to do, it's just going to move whatever's on the stack at that location of EBP minus 12 when this code executes, it's going to move that onto the stack and it's going to write four bytes of whatever we put in there. So we can see that actually this here, this F7E75CAB, if we continue executing input something for passcode one, that's where we get a seg fault because we're trying to write to that memory location. So this is interesting, right? And so where did this come from, right? Well, we'd have to trace through the program and we know that as a program executes, the stack is going to be used and reused as functions are called. So when we look back and we look back at this code and we see what happens, well the first main gets called, then printf gets called, and then welcome gets called and huh, that's interesting, we actually can control a hundred bytes on the stack when welcome gets called because all of this gets copied onto the stack. Then we have this login function that gets called that then this passcode gets used. So one of the first things let's do is let's look at the scanf system called, sorry not system called the libc call, and let's look and see what a scanf do. So scanf, specifically we're looking for when we have a percent with a number. So s, let's see, so these are all the conversions. So percent s says, so this is important, we need to specifically understand the semantics here to know what input can we get into this function. So percent s says matches a sequence of non-whitespace characters. The next pointer must be a pointer to the initial element of a character array that is long enough to hold the input sequence which is added to hold the input sequence and the terminating null byte which is added automatically. The input string stops at whitespace or at the maximum field width which ever occurs first. Maximum field width is specified as part of the format. So percent n, so n can be the maximum field width. So that's exactly what's happening here in our code is we have percent 100 s so that should allow 100 bytes to be written to name. So what we want to do now is just see what happens. What happens when we give an input that's 100 characters. There's many ways to do this. I kind of like a super simple way right off the bat is let's just print out and I like a because my name starts with a. So we'll print out 100 a's and we'll just use this as the name. So we'll go back here to gdb, we will restart, we will enter our name as this. We now are remember we have a break point at the login function. So now when we print out our ebp minus 16 we can see something super interesting. So now we can see at ebp minus 16 we control those four bytes. The last four bytes that we write to this program that's going to be the four bytes here. So if we continue, we should put in some passcode and now we'll get the edex register is 6161616161. So now we have a great starting where now we can. We can control the value that's inside this passcode one because the stack is being reused and by reusing that now we can overwrite anything that we want. So now we have a way to by putting any four well okay so not exactly but by putting four bytes at the end of our payload because remember the buffer is going to write up. So the idea is when welcome gets called it just so happens that when welcome gets called so these are the same stacks. So let's say the name buffer is here somewhere name that's a really bad name 994 bytes are going to be done and then another another four bytes the last four bytes and then actually the closing zero because if we look very carefully back here if we restart that and enter in all those a's and we look at the stack we actually zeroed out the thing that was right above us which is pretty interesting so so we have all those a's and we wrote that zero byte so now we completely control this passcode one parameter that's going to be used as a pointer to overwrite with input of our choosing so this is when welcome is called and this is when login was called and because the stack is being reused we can control the default parameter the default value that is inside the passcode one. So now we have a way to overwrite but what so what memory addresses can we overwrite so if we go back to our scan F let's go for S S is super annoying we'll just go down we will look for S let's see a sequence of non-white space characters so this is a key element so we can we need to choose an address that does not have a white space character in it so this is our first thing and so we have basically a primitive that will overwrite what we want so now we want to so we can control the address we need to figure out what to write though so where do we actually want to go what's our target so let's look at the going back to our C code we actually see hey there's actually something that does so normally when we're trying to exploit a program we want to somehow call system been S H or dump to bash or do something like that but we look here there's actually a target here if we can just print jump to this maybe print F login okay and this call of system as long as we can jump here then we're actually good right we don't even need to pass this check as long as we can somehow get the code to execute there and if we look at login we can see that there's this call to system and so we're moving this onto the stack and then calling system so and we'd want to quickly check 080485E3 off the top my head oh so this is the value that we want to overwrite so let's make some scratch in our buffer let us do we are doing passcode so this is what we want to write we want to somehow jump there and because we're giving our input in terms of base 10 we want to make sure we have this correct so we want to jump to that address because that'll get us where we want to go so that's the input we want and so we have what we want to write but where to overwrite that is the current question where do we want to overwrite so this is when we want to then go ahead and look at let's go here read elf dash a we want to look at all of the elf so what is everything that's going on here so there's a couple different options I'm gonna pass this through less there's a couple different options of what we want to overwrite so read elf is going to read the elf header file and tell us all the different sections in the binary and how these map onto the program's memory so we can see we have a lot of section headers something that's interesting that I actually haven't done yet would be to play with the dot detours try to write that address to the dot detours or to the dot Finney another aspect would be to overwrite the GOT the global offset table and that is this lovely relocation table here the idea is the dynamic linker uses the global offset table to because well so when a binary is dynamically linked that means the .so files of libc and other functions are not included in the binary so they need to be loaded at runtime well how do you link up something that's loaded at runtime you need to be able to put where that memory location is so you can jump to it so and remember we can't have any white space characters or null bytes that's another thing so when we look at the global offset table we want to look at the code itself and say okay here is when we're going to overwrite something so what is a function that we can overwrite well one thing would maybe be printf and we can look at this look at printf printf has a null byte in it so that's probably not gonna fly we can look maybe at scanf so where's the scanf scanf here the problem here that maybe doesn't immediately jump out to you and I actually tried this it doesn't work is this 20 so the problem is with this 20 that is not going to work because 20 hex 20 is a space and so that's not gonna work either but if we look at this lovely f flush which happens right after us we can it is 0804 a 0 0 4 and the important thing to note is it's a 0 not 0 a if it was 0 a a new line this would not work that is a white space character but it's a 0 and so the goal is let's overwrite 0 a 0 4 a 0 0 4 and so how to do that well we need the output to this program oh I should go copy that let's see I want a flush and actually if we look now from kind of a game theory gaming perspective if we look about this code it's interesting right so there's a bunch of print s bunch of scan s but there's only one call to f flush so if we can see yeah so there's only one call to f flush in this entire source code and this is really suspicious it seems like this was almost put in here specifically to get us to do this right so what do we want Python dash c so I want to print a 94 times plus slash x 0 4 slash x a 0 slash x 0 4 slash x 0 8 into let's say temp testing and then I want to run passcode given temp testing and so I want to test this first so let's go here let's create this file and then we'll rerun gdb with this input of what I call it temp testing so now we can go look at our thing and now we can see that we have 0 8 0 4 we have a null byte here that's interesting so what happened 0 4 a 0 8 0 4 a 0 0 4 maybe it's 93 you know let's print out this value hmm let's look at the stack 61 61 61 0 4 a 0 okay all the bytes are getting in there 0 4 a 0 4 0 8 different values oh it should be 96 I don't know why I started with 94 all right that makes more sense this is why you always want to debug it locally so you can test to see if things are working so now you can see 0 8 0 4 a 0 0 4 that means we should be overriding and now when it asks us what we want as input we'll give this continue at so it said login failed so it says enter passcode 1 enter passcode 2 and remember this makes sense because it's we're putting in our name but then we also need to input passcode 1 in the same standard input so we actually need this as part of our exploit so here we have a new line then we have this new line and then we shouldn't have anything for passcode 2 so that should be fine but we'll put one in anyways and see what happens okay let's change this to 96 this is the super interesting thing and the thing that gets me really excited about security is you need to have everything be very very very precise or it's not gonna work the address is correct let's continue boom there we go process is executing new program bin dash remember this is local it's executing cat there's no cat file there so we should be able to actually just take this exploit let's run this here let that run to generate us a temporary file I think this should work and then we will run it and assuming our environments are the same I mean not exactly the same but assuming our exploit does not depend on the specific output of the environment or any ASLR type things that should work and it doesn't and that's why the great things about why you want to change the global offset table because it does not move when the program executes those are fixed addresses there we go and it says enter your name welcome all those a's sorry mom I got confused about scan f usage oh now we have a frowny face instead of a smiley face that's lame but now if we authenticate I'm sure it's gonna tell me I am not logged in but that's okay and here we are and we have found the flag so this was a super interesting I really like this challenge because it looks on first glance when you look at the C code they're like nothing's wrong maybe there's a problem with the scan f and what you realize is it's one character right it's the fact that there's no address of operator in this passcode one that makes this program vulnerable and that is super interesting and not only that if you couldn't necessarily control the stack you may not be able to get something useful in this passcode one value in that space so what you overwrite you either may not be able to control or you may not be able to alter so that I think is something that is super interesting and I really really enjoyed this challenge so I hope you like this walkthrough and I'll see you folks later