 Okay. All right. So good morning. All right. It's Friday. Thanks, everyone, for being here. I know you're in the final sprint of the assignment three. I know for some of you, you're learning quite a lot by not passing levels, which is always good. I'll tell you this. It just makes it that much sweeter when you finally do break that level that you've been working on for so long. Some tips, just general tips when you're doing these things. Sometimes it helps to take a break, like a half hour or so, and you can come back to it later. I've definitely done this where I've been working on problems like one or two, and I go to sleep, and I kind of wake up and I have an idea in the middle of the night, and I go and try it, and it doesn't work, and I go to sleep. And then it's not until I come back later that it's like, oh yeah, it doesn't work. This is a good thing, and you try something else, and it finally works. From a cognitive science face, it's true, too. So your brain keeps processing subconsciously on it while you step away, and that processing is a lot more powerful. This is why I tell students, especially when they're working on projects, it's why you don't want to start at the last minute, you want to start at the beginning. That way you let your brain kind of think about the problem, even if you're not doing the whole thing right away, just starting on it helps. Some other tips that can help. You should treat this, so you're all computer scientists, right? So you should treat these like a scientist, right? Or you have hypotheses, and so if you're doing something and you are convinced that it should work, everything should work, but it doesn't, right? As a scientist, you should probably go back and recheck your assumptions, right? So what kind of assumptions are I making? Am I assuming that the input that I tested the program on in GDB is the exact same input that I'm testing on the actual system, right? I had personally had this error even yesterday where I lost, I forgot, a slash x when I was typing in address addresses. So it looks right in my string, but it wouldn't be, execute the code, and I was typing in GDB, and I finally found that they're huge or not right because of the bits there. Anyways, yeah, so always, you know, when you get stuck, that's the best way to get unstuck is to try to go back, rethink through what you're doing, reestablish, prove to yourself that everything you're building on actually works the way you want it to. Any quick questions on high level solving problems? So if you have just source code, just binary, what's your approach outside of, I mean, so running it in GDB, but once you, all you have is a similar, I mean, I don't know, it's kind of daunting a little bit. Yes, so if you just have the, so one technique I've actually used is the, you can kind of black box test it and just throw a bunch of data at it to see what happens. So if you can get it to crash by somehow buzzing it manually, I would run Ltrace and Strace on it, right, just like you did kind of for the first levels to see what's going on. If those aren't really insightful, then I would, depending on what the program is, you can kind of try to reverse engineer it in some sense, like look at the assembly and just try to write the equivalent C code. Like that can kind of help. So you're kind of doing like manual disassembly. So what about stepping through an instruction by instruction? Yeah, stepping through an instruction by instruction, figuring out exactly what it's doing. The thing you got to keep in mind, right, is when variables get like, because you only have a limited set of registers, right. So when you're trying to trace through a variable, it gets overwritten, right, that means that no one has the value that it has, so it could represent a new variable in the program. But you can use techniques like looking at EVP with the offsets to get a sense of the size of things. I would try to draw the stack statically and try to look at what's on the stack statically. You can also check what the registers hold during a point of view. Yes, so yeah, as you're stepping through, right, you can check when you're doing your single step, you can check the value of the registers at all those points. Yeah, and you can get it to crash, then you're kind of in the money, because then once you get it to crash, you can work backwards to figure out why it crashed to see if you can control that crash. So yeah, you know, you have to figure out what it's doing and find the variable. Level 15 is hard. Any other high-level questions? I don't actually bring all 15. I don't think so. I think people are still at level 15. There's like a three, four-way tie at level 15 right now. That is a really tough one. Performance, it's like if we're using percentile and we can override a particular address on location or else, but it's still... Not a register, memory. The address is like if you count the number of... We're going to go over performance today. So why don't we do that and we'll see if that answers your question, because we're going to go over that super intent. So on Wednesday, we looked at the print app and we looked at the functionality rate. We saw what happens when we provide more arguments than the print app function has. We saw what happens with the percent n, right? So what does percent n do? What was that? Length. Length, yeah. So it writes out the number of characters that have been written to at that point in time. This one is only for that particular print. For that particular print after print. Yes, so how many elements... So this would be... This would be put five into this variable length because H-E-L-L-O had been written out. So in that print app, how many characters were written at that time? Before you begin with that, you said that you'd be talking with a product on Friday. Oh yeah, we're going to go to Monday. I want to get rid of this stuff. Forget about the stuff first. Don't worry, it's coming. Okay, so how can we actually exploit this? So we have these two things, right? We can positionally access arguments with the dollar sign, right? And we can use the percent n to actually write anywhere. So as we saw on Wednesday, this gives us a very cool primitive of being able to write whatever we want, wherever we want. So let's look at a simple, vulnerable program. So this has a function meaning it's going to open a file temp log with append only, right? So a plus means append. It's going to call some function adlog, it's going to close that f and return zero. So in function adlog, it has a huge buffer of the line. It declares two variables, i and res. And while it's going to read a byte from standard n, right? And put that into the address of line i. So it's going to put this into the buffer. It's going to check if the result is zero. So if we didn't read any more, we're going to exit zero. It's going to increment i. And then it's going to check if i is the end of the buffer, then exit, right? So if we put too much, this is going to exit. So 6, 5, 5, 3, 5 would be the last one we actually wrote in here. Because we just incremented i. Then we checked if that last character we just read was a new line. Then we set the next character to be zero, right? The null light. And then we break out of this while loop. Right? So is this vulnerable to a buffer overflow? Yep. Is it? Yes. Yes, fine. Because the last character can be a not return character. The last character can be not return character. So we can have more values than 3, 5, 5, 3, 6. So i can be greater than 3, 5, 5, 3, 6. How? Once it gets to be 3, 5, 5, 3, 6, it exits on this line. What? Oh. Yeah. More characters than 3, 5, 5, 3, 6. I mean you can have 3, 5, 5, 3, 9 and it will still work. But to get to 3, 5, 5, 3, 9, i has to be 6, 5, 5, 3, 6, right? Which means it would have exited, yeah. So if the last character is not the new line character, it will continue till it finds the new line character. Yes. It will continue until it sees either a new line character or it returns zero. Result returns zero where it didn't get any bytes and the file. Or if i is 6, 5, 5, 3, 6, right? So the most we can copy into here is actually 6, 5, 5, 3, 5, because this just incremented i. So the last byte we can write is line 6, 5, 5, 3, 5, and then it will write the null byte if that was a new line. So this is actually, I think this is perfectly safe. This is only ever going to write up to 6, 5, 5, 3, 5, and maybe the null byte and if there's any more in the program it will exit. Yeah. It's not a new line character. When? Not until 6, 5, 5, 3, 5. 6, 5, 5, 3, 5. That's a very nice guy. Like the very last one doesn't sound like an old 720 file. Oh, wait, wait, wait. But no, no, no, when it's 6, but when it's 6, 5, 5, 3, 5, when it reads in byte 6, 5, 5, 3, 5, right? It's going to write it and then it's going to check i. It'll be 6, 5, 5, 3, 6. And then it will exit. So there's no null. But it exits. The program stops, terminates. Nothing happens. Like this is, so this is the important thing, right? So even if we could overwrite this, the whole buffer, when we call exit, the process terminates. This is the sys call. The kernel terminates our process. So we never return from add log, which means we never control this EIP there. I think it's safe until you add the next command. You're missing the right one, I think. Yeah, so we've got to think about when we get out of this loop, right? If we get out of this loop, we know that we act so. Let's see if you agree. When we leave this loop, look, when we leave this while loop, our line will be null terminated. No. No. No. Why not? Because 6-5-5-3-5 could be a character which is not back to the same. Exactly. But then it will exit. Exactly. No, no, no. So the last character of... Is 6-5-5-3-5. Yes. Then it exits. So the last character you can actually write is 6-5-5-3-4. If that is a null, if that's a new line, then it'll set it to be 0 and break. Otherwise, if it's no more input, it will also exit. Or if it's any more characters, it will exit. Is backslash n the only character with which you can specify that it will be an input to the standard in? Could there be any other characters other than backslash n? What do you mean any other characters other than backslash n? So this is a read from the standard in. So I'm saying that backslash n, we are assuming that backslash n is the last character of any input that is entered by the standard in. No, not any, but it doesn't have to be. This is just saying that if it is, if we read in the slash n, then we're going to null terminate this and exit the loop. Otherwise, we're going to continue forever reading input. If we ever can't read input, we exit. And if we read too much input, then we exit. So you cannot exit this loop without doing a slash n. If you give only 10 characters with no slash n, you're going to hit this res equals 0 and exit. If you give more than this, if you give a bunch of input that's no slash n in it, then you're going to exit from here. So this loop is only a breakout if we null terminate this. Then we print, we print out to that file f to 10th log, the line, and then we return. So this is print f on our ball. Where did line come from? Standard in, where did standard come from? Us, the attackers. So we can control that, which means we can control what is being used as a format string. Well, this is a little preview. So we can compile this guy. Actually, we'll compile it with all of our fancy flags just to make things slightly easier. And we're just going to run it. Remember, this reads from standard in, but it doesn't output its return into standard out. It outputs it into this 10th log. So this batch line says echo test underscore new line into format simple. And echo, I believe, will add a new line to that, so we don't have to worry about that. It will run format simple with the standard in coming from the output of the echo command, and then we will run a tail. The tail command shows you the last line of the file, and the dash one means show me the last, only one line, the last line of the file of the 10th log. So what should it output? Test line. Test line, right? So, well, shouldn't be a dog-send there. Right, it should output test line. Now, what happens if we do test line percent x percent x? Where is this input from us being used? In the program. In the program? We're in the program. In the printf of the program, right? So in, at this line here, the input that we send in is going to be interpreted as a format sharing by printf, so these percent x's are going to do what? Yes, it's going to look at the arguments on the stack, and print them out as x values. So, let's say it prints out one and zero, whatever. Okay. On this next line, right, so now I want to know what's on the stack. So I'm going to first put some a's, b's, c's, and d's. And it actually is important that I do four, four bytes here. We'll see why. But I want to do four, four bytes here first, and then I want to print out the stack. So, I'm using the percent p, which is going to print them out as hex, right? And I'm using eight of those, or ten of those, or whatever, just to see what's on the stack. So what I'm looking for is I'm looking for, in the output here, where is my 41, 41, 41, 41, which is all a's? Where is my 42, 42, 42, 42, 42, which is all b's, all c's, and all d's? Right, so looking at this, right, so we can see, okay, we print out this a, b, c, d, right, this came from this part of the string. Right, this said, okay, here is all a's, all b's, all c's, all d's. Then we had a one, okay, actually I should be honest, this line here was on a different version of the program, so this is why things are different. I was doing things and I think I recompiled it with the other settings, and so it changed the way things looked. Okay, that's fine. Just to be quick. Okay, but we can see here, right, so the first percent x outputs o x one. Right, do you agree? I'm sorry, the percent p. The first percent p here outputs x one. The second percent p outputs zero x, 41, 41, 41, 41, 41. The second, the third percent p outputs all 42's. The fourth, one, two, three, yeah, the fourth one is the c's, and the fifth one is all the d's. Right, everybody agree with that? And then above that, there's much more to come. So our goal here, we want to be able to write somewhere any value we want. So everybody see that we put in the values of all a's here and we saw that with two percent p's, we were able to get to that value, right? So this means what if instead of all a's here, we have a memory address, and then we could do a positional argument with a positional two, so two up and an n, and that will allow us to choose where to write. So as we saw, we can use the percent n to write to any memory address, right? With this, so we'll see exactly how we can do this, but by using this, right, we control values on the stack, so we can put memory addresses here and get print out to write to those memory addresses for us. So the question is what to write to? So when you do buffer overflows, what are you trying to overwrite? The save you get a key usually. Maybe also the save you get a key, but you can do that as well, right? Do we want to do that with the format strings? Could we? Yeah. If we control that, we can get that to jump anywhere, right? But how precise do we have to be? Very precise, right? So if you think about it with a buffer overflow, you just overwrite an address and you can actually pre-calculate exactly the offset and where the IP is, or you can just overflow a bunch, right? You could overflow 50 or 1,000 of that memory address, or you could do a clover memory, and you're going to get that save EIP. But with this, how do you know the address of the saved EIP on the stack? So after all the local things end, there will be the base pointer and then... Yes, what was the... Address, a GDP. So you can use GDP, but what happens to the environment and the stack when you execute a GDP? So the environment pushes it down. It pushes it down and moves it. So you're specifying the exact address to write to. You have to do this before you execute the attack. Right? So... You have to not only do so in a buffer overflow, right? You need to be precise about the address that you write in, about where you go to. But with a format string, you would have to be precise about the first the saved EIP that you write to, and then the actual value that you write in there, you want that to be a buffer too. So here you're trying to hit two moving targets, right? So you have the EIP that you have to get correct, and then you have the address in the shellcode that you're writing in there that has to be correct. So this is difficult, but we have the ability to write anywhere. So we can do even... Well, we can use what other people have studied and learn from their techniques to improve our technique. So the global offset table, which we mentioned way back when we were talking about processes and health binaries, the global offset table has the addresses of all the dynamically linked functions. And you can view these with the readElf command, or actually, object.com will do this, with the reload, so this is all the relocation, information in the health binary of this format simple. And so it's going to say, well, this isn't that important. I think this gives the address of the table. But it says here, these are all the values in the global offset table. So this means that the function read is going to jump to whatever's at memory address, 80804497474, whatever it is. And fclose is going to be here, and fopen is going to be here, printdown is here, and exit is here. The idea is the linker uses this information at runtime, it finds the binary that defines fopen, it loads it, puts it in memory, and then copies the address of the fopen function into this memory location exactly. So this is what the linker uses to write into to tell the program where to go. So let's look very briefly about this. So if we disassemble that format simple example, we'll see that there's a call to this fclose function. If you look in there, it says call this specific memory address. So even though we're using dynamic linking here, this address is fixed about what's going to start to execute this function call. But what actually lives here, what are the instructions that lives here? Is this the fclose function? No. This is, we can do object dump, we can look exactly at this memory address of what precisely it does. The very first thing it does, it says jump to star 0x804978. So what's that 804997D8? So what's this actually do? So does this mean jump and start executing at this memory address? What's the star mean? A dereference, yeah? Take the value inside that memory address and start executing there, right? So all this code is all static. This is why the linker only has to write in the address of the fclose function into this memory location 0804978. It has some other stuff here. And so if we look at the, so this is that entry in the GOT table, we can see that this address that's in the GOT table is exactly the address that fclose is going to jump to. So if we write to 0804978, we write to that memory address, right? When the program calls fclose, it's this jump star is going to jump to whatever we wrote into that address. And that's going to be constant and that will happen every single time. So let's look at the program. Why am I talking about fclose here? Is the second left slanted for me? Yeah, so, right? So we think about, okay, at fprintf, let's think about when this line executes, we can overwrite any memory in any location in the program. Any memory in the program with any value. So like we said, we could do the state VIP in ad log, right? But we have to be very precise. We have to be very precise so we'll be overwrite. We could also even overwrite the state VIP of make, right? We'd also have to be very precise. We can see that after this happens, ad log returns. And then what happens? fclose is called so if we're able to control that, if we're able to write into that memory address that we just saw, we'll be able to, instead of calling the fclose function, it'll jump through our shortcut. So you see that if we overroot fopen, it wouldn't do us anything, right? It doesn't overwrite in fopen. It doesn't help at all because our program doesn't call fopen after we do our overwrite. The same thing with the exit function or the read function. Sure, we could overwrite them, but what's the point? So by looking at this, we've seen if we write into this, the old memory address, yeah, that's fine. So by writing into this memory address, which is slightly off because I had to do this several times, then we can control what happens when we call fclose. So we can take control of the execution of the program. So we started with this, right? So we had this output. We saw that we got the a's, the b's, the c's, the d's, and we saw exactly what the a's were. So we saw that we could actually access these first four a's. We can access these first four a's by doing percent two dollar sign p. So the dollar sign is the positional. This means we get the second argument on the sac. So the first one is 0x1, the second one is 0x41, 0x41, 0x41. What is slash before the dollar sign? Escaping what? Escaping the dollar sign quiet. Yes, so dollar sign and bash means like a dollar sign shell or dollar sign patch or dollar sign echo. So the slash before the dollar sign means interpret this as a literal string, as a literal dollar sign. These are some of the details you have to get right when you're doing crazy garbage and developing creative pipeline programs that output shell code and format strings. But let's have a look out for it. If we run this, what do we expect to see? 0x41, 0x41. Only 0x41, 0x41? Oh, 0x41. So interpret this as printout. What would you do with this string? Oh, that's the answer. A, A, A, A, B, B, B, C, C, C, D, D, D, D, D, D. Then, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41. Exactly, right. 6, 4Bs, 4Cs, 4Ds, because the format string looks at this string here. So now we want to try to write into this address, right? This is where we want to write to. So let's change instead of the four As, let's put that address. 080497D8. And I'm putting it out with X here, you could also do B if it doesn't really matter at this point. So now, what do we think we should output? Should we output As again? No, there are no more As, right? We should have put it up in front of the garbage, whatever these bytes are in ASCII or UTFA or whatever. But then we should see the Bs, the Cs, the Ds. But then what should this be? Yes, this memory address. So this is a sanity check to make sure that the end in this of what we put in there is what we expect. Because the X is going to say interpret this as a X number, where the P is going to say interpret this as a pointer, also pointed out in X. So the value that we write to there is going to be the actual value that we write to. So if we do this, we'll see that we have these weird walkies. Bs, Cs, Ds, 8, 0, 4, 97, D8. This one's wrong. I had to update. I thought I updated them all to D8 this time. So I did half of them and compiled it. And then I interpreted it and compiled it again. And then I changed everything. Super long. OK. Then now what happens when I change this X to an N? So what's this going to do? What is it going to write where? How many bytes have been written? 16, 4 times 4, right? A is B is C is D. So it should write 16 bytes where? At this address, right? So then what do we expect to have happen in the program? What do we expect the outcome to be when we execute this? So what are we going to do? We want to have it closed if we do all this. Whatever the standard would be, if you were to go over here, it would run that thing. What we're going to do is jump here again. So what's it going to do? It's a segfault. It'll segfault because it's going to try to jump and start executing it. 16, right? Memory location is 16. So let's see. I missed the segfault here. Yes, the segfault. Yeah. Yeah, well, that's the segfault. So we need to test this, right? So we'll output this to a file using GDV when you have to redirect standard in. It's kind of a pain. So I usually take the exploit, put it into a file, run GDV, and then run that program redirecting standard in from that file. Give it a better do it. Please let me know. So we do get a segfault. And we get a segfault here. It says at 10, at hex 10 to 16. And if we look at the registers, we'll see that EIP has the value of hex 10, which is 16. So we've actually successfully been able to change the instruction pointer at this point. So now we've gotten how to get where to write. We've seen that we can control where to write by putting that address in and then by putting how many offsets we need to do in there and using a percent n in order to overwrite. How do we control the value to write? How can it output and wrote 16 in there? We have to put up to that point. So how do we control that? Where would you go to learn how to control this? The man page. Yes. There is an option called the field width. Format strings are way more complicated than you've ever thought. I learn things every time I look at it, so I expect. The idea is by putting characters before. So this statement means print out whatever value, but make sure that it has 200 characters. So put it out with 200 spaces total. So if 0 is going to output 0 and it's 1 space, then output before that 0, let pad it with 199 spaces. So the outcome after this executes means that it has written out 200 space characters. So by using this, and by using this before we use percent n, we can control exactly how much output we use. So this actually can allow us to change exactly how much we want to output. So what's the number that we want to write? So in general, what number do we want to write out into that? G of t. The address of the shell code. So on this machine, let's say I went to GDP like you've already been doing. I put the shell code in the environment variable. I figured out about where it is. So let's say for this case, it's at ff ffca f5. So anyway, well, let's not guess. So in decimal, this is 4 billion, which would be 4.2 gigabytes of data if it printed out all that data. I actually don't know if we can do this. It probably maybe would work because computers are terrible. But we can probably do a percent this value, x, 4,994,953,717x. That would output that had that much exactly this value that we want to overwrite it to. And then do a percent $2 sign n, or a percent $2 sign n, whatever the format was that we had. And then that will write that value there. But the problem here is we're actually going to write out 4.2 gigabytes. The printf is actually going to write that much into the log file, which is not exactly what you want. You're trying to take this machine down, not ruin everything for everyone. So don't try this on the server, FYI. OK, so the question is, is there a better way? So we think about what we're doing. We're trying to write that whole address at ff ff, all in one shot, 1% n. But just like we can print out characters, we can print out decimals, we can print out longs, doubles, different size of data, the printf actually allows us to write out with that percent n different sizes. So instead of printing out 32 bytes, the idea is we're going to try to split it up so that we first write just one byte, the ff. And then we'll write ff again, and then ca, and then 5f. And as long as we put these four exactly in the right order in the right place, then we've successfully written the right value there. So we have to go to the man page. And if we look there, we can see that percent h hn will act as a signed character. And it will write a signed character, which would be only one byte. So this percent h hn will allow you to write only one byte. How do you even think? So we want to write out, let's take the first byte. We want to write out 255. So do we just do percent 255x in our printf string and do that before the percent h hn? Or percent, it'll be $1.92 h hn. Shake your head, why not? We have to account for the addresses that you have in the string. We have to account for those 16 bytes that we've already written out. So we saw that we wrote out 16 bytes. So what we're going to do is to get to ff, we need to say ff minus 10, or hex, 255 minus 16, which is going to be hexef. So we're going to want to first write out 239 bytes. Everybody see how we got to this number? Cool. So then this is what we're going to look like now. Now our string's going to be, OK, we have the same address, 080497D8. We have all these C's and D's. And then before our n, we're going to have percent 239x. So percent 239x is going to output 239 characters, add that to the 16 characters we already output. And then it's going to look at the second value up on the stack and write out to that address as a character. So write out the number of, as a byte, the number of characters that we've output, yeah. Won't the x pop something off the stack? Yes. So the x will pop something off the stack. But how are we accessing the percent n? But it's direct. Positional, yes. So this is why using positional is nice because otherwise we have to worry about where we are on the stack. OK. So we'll do the gdb, do the run thing. I'm kind of, this one if we get that. So then we get program received signals, sig ill, illegal instruction. And if we see where this happened, this happened at 080483ff. So where did we write to? Yes, the last. So we wrote to the least significant bit. But we're overwriting ff. So where did we want to write that to? Because of the most significant bit. The most significant bit. So how do I change my format strength, Caleb? Make what? d8 and the db. Yeah, so we think we have four bytes, right? So all those bytes at that address 080497d8, right? There's four bytes there. We've controlled the least significant. So this means we want to control the most significant. So we need to go from where we are 1, 2, 3 bytes up. So we need to increment this d8 by 3. And that would give us db. He said gding, looking at the next answer. We don't have to change anything else. So now when we run this, what do we expect to happen? Yeah, the ff should overwrite that 0801 first, exactly. And so gdb run that, we'll get a second fault because it tried to access ff0483a2. So now that we've controlled this first byte, right? Now we want to write what to where plus 2, sorry, what? What byte are we trying to write? We have, oh, I guess I should go back to that. We want ff ffcaf5. We'll just do this order. It doesn't matter what order you do it in. Let's say we want to go to the next write ff. So how many characters do we have to output to change from what we currently wrote to what we want to write? None, no change, right? We've already written out that. But can we just do another percent 2 dollar sign hhn after this? No, there's a 2. No, why? Because I'm not sure if we have to adjust the 239. What do you mean the 239? Oh, so we just said the 239 means that at this point it's going to have output 255 characters. So any percent h as we do after that, we'll write out ff. So we've already got, we actually don't need to do any adjustments. If we did, for instance, if we're going from bf to ff, we would have to make some adjustments and do another percent x. But where are we going to write this ff2? So we just use this percent 2 dollar sign hhn again. We need to change the db to da. Yes, we need to change the db to da, right? We need to write one byte lower. But if I change it here, then what's this going to output? This ff will be over db. Yes, the other, we're not going to overwrite the first byte. So if you notice we have four bytes that we want to overwrite. And we have four sets of a's, b's, c's and d's. So if I change these b's to be 080497, we'll say da, then how do I have to change the new percent hhn to da? Because 2 is going to get to the a's. So what's going to get to b's? 3. So I've got to add another one like that. So I'm going to change the b's here to be 080497 da. And I'm going to add a percent 3 dollar sign hhn. So this is going to then go up, access this address here, and write to that second most significant byte. And so it'll second fault again. It'll tell me that it's second faulted at ff, ff, da, 4b. Awesome. So now we are at ff, ff, da, 4b. We want to be at ff, ff, ca, f5. So what's the next byte that we want to write out? Ca. So how do we do that, though? Are we at the maximum? We're at ff, add 255? Get the overflow there. Yes, we're going to overflow it, right? We're considering that as a byte, but it's not, you know, byte arithmetic. Oh, it's an unsanitary. Yeah, it's actually unsanitary. So when it overflows after 256, it's just going to go rack line back to zero. So the way I like to think about it is, so how many do we need to go, so we have to rack around, right? So how many do we need to go from ff to zero? One. And then we just need to get to ca. So cb. So it's, yeah, one plus ca, which is going to be cb, which is going to be 203. So now what we're going to have, right, putting this all together, so we're going to change our Cs to be 0804 97D9, right, because that's the one before a, right? Because we want to write the third most significant byte for this second least significant. That's a weird position to be in, right? So we output the three here, this outputs, this writes the second most significant byte, then we need to output 203 characters so this will reset our counter from zero back to, if we think about the characters we've outridden so far, as a byte, it's only ca bytes. Then we do another percent, this time four, so four we'll get to the Cs and it will write out ca there. I'm going to do this, so why did this work? Nox. Yeah, my nox. Yeah, those my nox are awesome, right? I don't need to do that to be a significant byte. Right, I don't care what it is. My nox, actually, and this one is actually really big. So then I can use that in here. So now I do this in here, so now we're all super excited, right? We've got a payload that works in GDP and we throw it into the actual program just like we did normally. So what do we expect to happen? What do we want to happen? So let's say this is our traditional no new line shell code that just executes a new bin SH. Yeah, we want a new shell like the SH prompt, right? But when we run this, it doesn't crash and I had to do the brackets to say that it's the exact same command prompt as it was before, like it wasn't a new bin SH prompt. Let's think about it like this. Format simple, where's it reading from, the standard in? Like this, where is format simple reading standard in? From the pipe, from the output of the echo command, right? When I fork a new process, or when I exec a new process, that process inherits all the file descriptors. So when it tries to read from standard in to read your prompt, what is it reading from? The same. The pipe, the pipe from the echo, right? So format simple has actually read all of that input. Sorry, I didn't get this part, what is this? Yeah, so if we go back to here. Okay, if we go back to the program, right? The program's reading from standard in, right? When you pipe an output from one program to the other and bash, it means that instead of reading from standard in, like when the program reads from what it thinks is standard in, it's actually reading from the output from the other program, right? So what's happening here is this program read all of the input from that echo program, right? Because it read from the new line, it got the new line, it stopped to call the print out, it returned to call the F close, and then when it called F close, it execs bin SH. When it execs bin SH, bin SH tries to Inherits the file descriptors. Inherits the file descriptors. So it's standard in is still the output of the echo command, right? Which there is no more output because it's already been all read. So SH goes, okay, there's nothing to read and it just stops and exits. And if you actually S trace this, you can see that that format symbol does exec bin SH, so you're not crazy like that's what it did work. So we can actually use, so we can use a little technique, we can actually, well, I like to do it like this. So we use the cat command, so cat, right? We can cat name two files. So it's first gonna output all of tests, which is our file, and then dash means standard input. So when I type things in, so it's gonna pass this all to format simple. So this means when format simple reads, it's gonna read from test, right? It's gonna read to the new line, that exploit's gonna happen, and then it's gonna wait for characters from me, and it's gonna pass characters to me from my standard in to the program. So I can do something like who am I, and it'll tell me I'm out of D, and I can say ID, and it'll output my ID command. So I have a shell, but the shell's reading from this redirection, because the program itself wasn't reading from standard in. The program itself was reading from the pipe. We need to make our input to the shell go through this pipe, essentially. This could be, yeah. But you could also just add it to the test file, just have commands with a backslash in and into the test file. I think the way this is written, yes. Because it's using the read to read. In general, though, there's buffering that can happen in, like, they're using scanf, or get, or any of these other kind of functions. They can buffer input, and then when you call the exec, that whole, all those buffers get erased because you're starting a brand new process. You're replacing that memory with a branded process. So it's a little trickier. That's actually why there's other, if you look online for shell code, there's other shell code to, like, get to do a remote connect shell, so it will connect to host IP and host port that you specify, so you can type in commands and get output, because it's not always the case that you're gonna be able to get your standard input to the program. Yeah. You don't actually need echo on that one on the first line, right? If you remove back on the big boot. In this one? But you need to pass it to standard inn, right? Yeah, but would any execute the Python effect. No, do you think that has a command and you won't run? What do you mean? So you fude format symbol space this, backject thing? Yeah, you're gonna create that, after a result. Oh, oh, oh, oh, oh, oh, oh, oh, oh, oh, oh, oh, oh, yes, I see, I don't need this symbol, yes, you're right. It's just having. We don't need a code like this one, yeah. Yeah, that's good, this is redundant. All right, good. Okay, so this actually was a vulnerability that occurred in actually a lot of places if you look up format string vulnerabilities. So the localization system is defined by the C standard library and so it allows you to put error messages in other languages. So you can specify this language in an environment variable called language. And so when an error is found it searches this database for the right message. And so you could A use this to output, to provide a user supplied language file which then would be used to execute a format string vulnerability. And so by doing this you can trick any program to, you can take over any program with this. Okay, so what do we learn? Yeah, how do we prevent it? That's what takes an example. Never correctly give you user input. Never give user input to printf command. Yes, you should always, always, always, always have a constant string as the format string. You should never, just never do it. If you want to do a string that even you know is safe, do double quotes percent S and then pass the string you want to output. So like this is bad, always bad. This, good, right? This, still bad, right? Even if you're passing variables, right? Because they can always access more and write more. And part of the problem is that, yeah, so there's no validation of parameters. So, all right, so we will stop here when we come back. We're going to tie it up with protection. So, tie it up for a while and we're going to bring it back. So that's it. You don't turn it up, you take the same options. No, I think, you're not using it. You can use it. I'm not a thing for that. You didn't pass it? Oh, I think I'll pass. Actually I think you should get to think it from the action to know that that is only one.