Submind YouTube summaries
Thumbnail for CSE 365 F23 11-22-23 "Binary Security Pt. 1"

CSE 365 F23 11-22-23 "Binary Security Pt. 1"

Watch on YouTube

Video summary

The video introduces the core concept of binary exploitation by explaining how attackers trick a computer into executing user-supplied data as code. This principle relies on the Von Neumann architecture, where there is no fundamental distinction between instructions stored in memory and data; both are simply sequences of bytes that the CPU fetches based on an instruction pointer. The lecturer illustrates this with examples like SQL injection and cross-site scripting, showing how these attacks manipulate servers or browsers to interpret input as executable logic rather than harmless information. At a lower level, modern systems like JavaScript engines utilize Just-In-Time compilation to dynamically generate machine code at runtime, demonstrating that the ability for data to become code is an inherent feature of this architecture rather than a bug, though it creates significant security vulnerabilities when control flow can be redirected onto attacker-controlled memory regions on the stack. To demonstrate these concepts practically, the instructor walks through a vulnerable C program using the `gets` function, which reads input without checking buffer boundaries. By manipulating the return address and passing pointers to specific functions as arguments, they show how an application can accidentally jump from reading data into executing that same memory region as instructions. The lecture highlights the importance of understanding x86-64 calling conventions, such as using `RDI` for the first argument and `RSI` for subsequent ones, which are crucial when crafting payloads to redirect execution flow. A key takeaway is that if a programmer or attacker can control where these pointers land on the stack, they effectively gain the ability to execute arbitrary code simply by arranging bytes in memory that mimic valid assembly instructions. The tutorial then focuses on writing "shellcode," which is compact machine code designed to spawn a new shell process with elevated privileges once executed. Since standard input functions like `gets` stop reading at newline characters or null bytes, crafting effective shellcode requires careful attention to byte size and content; for instance, using the XOR operation (`xor`) allows an attacker to zero out registers efficiently in just one byte instead of multiple instructions. The video also explores advanced techniques such as embedding strings directly within instruction opcodes by leveraging endianness rules or utilizing system calls like `execve` via specific register values (e.g., setting `RAX` to 59 for the Linux x86-64 syscall table). Furthermore, it discusses constraints imposed by different input functions, noting that while some allow null bytes, others restrict newlines or whitespace, requiring attackers to work backward from their desired outcome and design payloads that survive potential mangling of the input stream. Finally, the lecture emphasizes reverse engineering skills as essential for overcoming these limitations, suggesting a strategy where one starts with the goal—such as spawning a shell—and works backward to determine what bytes are needed before they reach the vulnerable function. This approach allows attackers to bypass restrictions like limited buffer sizes or forbidden characters by using jumps over corrupted sections of code or compressing their payload into minimal space. The session concludes by introducing tools like `pwntools` in Python, which automate tasks such as assembling shellcode and interacting with processes interactively, thereby reducing the manual effort required to craft complex exploits while reinforcing that all these capabilities stem from a deep understanding of assembly language, memory layout, and system call mechanics learned throughout the semester.
Read the full video transcript
let's start recording because now we're moving on to now for the final module you're going to be digging in and going back to putting the skills that you've been building throughout the semester of understanding and learning web application of sorry not web applications because we just covered that um but what we've learned about assembly so you've been learning how to write assembly code you've been learning how to reverse engineer programs now we're going to start putting those pieces together to actually exploit vulnerabilities in binary applications and first we'll start with shell coding so the so fundamentally what we're going to want to be doing and where we're going is trying to answer the question how do we trick the computer into executing some of our data as code and we actually have already seen an example of this and we've been doing this so when we're doing a SQL injection what are we trying to do like what's the goal of a SQL injection to run well anytime I interact with the application it's running SQL code on the server yeah so specifically we trick the server into taking something that should be our data right because it's data that we provide to the application so it should just be data that's stored in the database or used to query the database but we trick the database into treating that as code as part of a SQL query if we think of a SQL query as code similar exact same thing with cross site scripting right cross-site scripting is fundamentally you're tricking the browser into interpreting your user data as JavaScript code this also goes into command injections are exactly the same thing you're tricking the the shell bsh into treating your user data as code now we're going to go those are all kind of I would think at a at kind of a high level now we're going to actually go down to the Machine level and start thinking about at the Machine level what is um um how how can we trick the CPU into start executing our data as code and this goes back all the way uh to like the first some of the first initial designs of computer architecture so the yeah there we go so the Von Newman architecture this is from 19 45 which is a design of a computer system um this is kind of a fundamental difference in CPUs and computer architectures is fundamentally a Von Newman architecture sees and stores code is data so how does the CPU we've been talking about data when we access things from memory right what's like the lowest level of thing that we can of information that we can access from memory bites yeah just bites right it's bittes in memory so if we ask for a bite we get a bite if we ask for uh quad word we get eight bytes I think that's right I don't know the size is but ask for one bite you get one bite two bytes you get two byes kind of flipped around four bytes same thing eight bytes and fundamentally your the instruction pointer where your Cod your program is going in order to start executing is exactly the same thing so there's fundamentally no difference between code and data right the code that you write in assembly is compiled down to bytes that are loaded into memory and we tell the CPU start executing from here but fundamentally there's absolutely no difference between the code that you write and provide that's in those bits that those bytes in the computer's memory versus the bytes that you're storing as data and that goes all the way back to Von Newman architectures um whereas Harvard was another style of architecture at the time that stored data and code separately so here you'd have a different memory for code and a different memory for data um almost everything that we've been using and learning is a is based on the Von Newman architecture does anybody have a device that is not in this list like on them or know of them x86 arm myips power PC spark risk risk is definitely on there for sure toaster a toaster what what CPU does a toaster run so a lot of them actually will run one of these things usually be like a cheap myips processor um there definitely are use cases I think uh very specific things I haven't seen these but they definitely do exist of these crazy architectures um and so what we're going to be talking about and focusing on is what happens if the CPU mixes up code and data what happens if the CPU starts executing or starts executing something but it's actually your data and not code like one thing to think about is from everything you've learned about assembly and CPUs does the CPU have a way of determining this no and this actually is a good thing so uh anybody know how so JavaScript we looked at JavaScript right how does your browser execute that JavaScript yeah so it has a JavaScript engine that um used to be JavaScript was really slow because it would interpret each line essentially so it go through and do whatever it needed to get done but as it became clear that JavaScript performance was incredibly important they do things like what they call just in time so jit compilation where they figure out oh we're executing this code a lot which means it's very important which means I'm going to take that code compile it to x86 put it in memory and then start executing just that code and so now it's executing um x86 instructions instead of interpreting it which is incredibly fast and you get insane speed UPS this is why JavaScript engines are so fast now because they use all these crazy tricks and the way they're able to do that is because of this architecture they can dynamically create code x86 instructions at runtime and execute that so you can think that actually gives you a lot more power I mean it enables powerful things to be able to do that in this vuman architecture so it's not something that anybody would really want to do and say hey let's fix this fundamental problem and how tell the CPU ahead of time what's code and what's data because we actually want these this thing in our applications we want to be able to do that so it's a weird example okay anyways the one of I see sorry I'm trying to understand what this is trying to get at but uh that makes sense oh oh oh I see ah okay wow that's a weird example okay so I guess we can execute this example please tell me I have a dojo spawn up yeah okay cool [Music] so ah I already have an example that see okay so here's my program let's just run this so we have to run it with special compilation options which gets to some things I'll talk about don't tell me things are dangerous so I'm seeing weird Behavior right I'm running it and I'm sometimes seeing helloa and I'm sometimes seeing illegal instructions let's look at the code so the code here if we look at the main function we have some fixed character array so a buffer of 10 24 bytes on the stack we then call the gets function with that so gets if you're not familiar uh this does reads from standard input and puts the data wherever we send it so it's going to put all of our input at name here and then specifically what it's going to do uh gets is really bad if you actually noticed uh when we we were compiling here it actually told us that should have told us tells us that the gets function is dangerous and should not be used and if we look at the man page that's because it reads a line from sandard input into the buffer pointed by S until either a terminate new line or end of file which it replac with a null bite no check for buffer overruns is performed this means that it will it has no idea what size this character array is and it will keep writing whatever the user's input is and because you cannot know in advance how much data will be sent this is fundamentally an insecure function but let's look back at this so we read in to name 1024 bytes we then uh seed random based on the time this just sets up we're setting up a random based on time and now we say if random mod 2 so what's the mod two going to do even or odd so roughly 50% of the time we'll do this otherwise we'll do this and so what we do here is say hello we call the hello function passing in name is the first parameter by two is the name one so that's when we passed in a and we saw farewell it said hello a and then said farewell which was what we saw but 50% of the time it crashed and if we look here it says it passes in as name Name by one and it passes in as n as by Funk which is the function here the name so what that's what that's going to do is start trying to execute code on the stack let's look at this in GDB that's not right wait do I not have my okay I can't I can't live like this there we go sorry I need to use uh I don't need to but it's way better for me to use Jeff so we can see things okay so let's look at the code here x20i so we're look at instructions I want to stop so let's break it hello because that's where we'll be uh is that function call and then we can bring up the here no here the code so so continue uh what do you want to say blah blah blah okay now we call hello and we know based on the uh so we can step through here uh what's the calling Convention of x86 64 how do I know in which of these registers is which arguments I actually can't remember I think it's RSI RDI but is that right actually maybe RDI RSI I was hoping someone would know we'll look it up yeah RDI RSI so I had it backwards RDI RSI RDX rcx cool so RDI which is name has this which is the address of by 2 and RDI wait I said that the wrong way RDI which is the first argument name is our string blah blah blah our SI the second argument the function we're going to call is here here so if we step through this we're setting up the function frame doing some stuff calling the print F so we're calling print F and passing in this argument to blah blah blah that's where we see hello space blah blah blah so we should see that up here hello blah blah blah blah blah then keeping going we see a call to RDX x5i let's look at what this is this is by two so if we look back at the code we would see that we will see that we are here at buy two so this will after we go here this will go into buy two and it will say farewell so let's run it again okay now I'll have to run this I guess 50% of the time okay uh rerun okay is this gonna be very annoying okay well there's an easy way to fix that and that's what I was just telling somebody else when you control the thing you can do whatever you want boom if you don't like it change the code okay compile it GDB it break hello okay so I'll type in csse 365 okay now we're calling this function and RDI what we're going to Output is here by one but now let's say we've somehow tricked it into pass we're passing in a pointer to a function that we're going to call but that's actually the buffer that we have on the stack that we were just reading characters in oh no what what are the odds container gets killed right in the middle did you do that on purpose Conor okay okay now here we are so now we've accidentally as a programmer passed in or we've tricked it into passing in to swapping those two values so passing in by one as our first argument and the buffer on the stack so as we step through we'll see the print F so so we call the print F and if we look at the output it will say it definitely should say oh I didn't uh sorry I stepped into it so I'm gonna do fi to jump out of it Finn and it says hello blah blah blah blah blah what's this blah blah blah blah blah it's not it's garbage in the sense that not asky characters but specifically what it is is it is if we go here right what the program is doing is it calls print F and passes as the percent s the second argument to print F so what's going to be interpreted as a string is actually this byy one function so if we examine this as a string it will keep printing out these characters until it gets to a n bite and so because it doesn't know right again these functions are very stupid right print F says print me out a string take whatever memory address I give you and just output it until you get to a zero so let's look at the call at the call here we call RDX and now we have this value on the stack that came from [Music] us but it will be interpreted as instructions and will be executed so I can step into there and I can actually see that we're actually kind of executing some stuff until we get to here we try to dreference RSI and it blows up because that's not valid thing and that's why we get a seg segmentation fall but if it's executing some code that comes from us do you know how to create bytes that the computer can interpret as x86 64 instructions to do what you want yes because you had a whole module writing assembly code you've written a web server and assembly code you've read code written code right that's what we've been building up for because that's now at this moment you've tricked the computer into interpreting your data as code cool so essentially what this is doing is it's actually jumping back to our code on the stack because that's where that buffer existed in the function and just executing and interpreting those bites that it got from us as code okay do we want to make it do something we'll get there in a second okay but and this is an incredibly old concept so this is actually the um Morris internet worm was one of the earliest uh this actually took down the whole internet they um because there was a a bug in the replication strategy of this um in the replication strategy of this worm where it was supposed to look if it was already running and if it was it would like kill itself but there was problems in that so this worm would just infect machines and then get on those machines and then look for other machines and infect those machines and there would be so many copies of this worm running that it would slow down the machine to where it was unusable um and so it actually used a lot of different attack vectors it's really cool to look this up uh but in in this finger D service this uh used this shell coat injection the other crazy thing is this worm was I think cross system and cross architecture compatible so it would like had its own Shell Code that it would compile for the Target architecture and then go there it was insane and they literally had to shut down the internet so they had to they had they distributed patches for the whatever the services that were running and they all had to like shut down their machines because if one like if a couple machines were infected those would spend so much time trying to look for other infected machines that would slow those machines down so they had to like shut everything down disconnect them update their own local copies of stuff and then turn them back on so that the internet could work um it gets even crazier if you look into this uh the person who did this RTM he's actually I think a professor at MIT was or still is um and at the time he was a grad student his dad was also I think the director of the NSA at the time um so it's like really uh anyway super interesting um so this is like a fundamental con things that we're learning here okay cool so how do we write Shell Code you actually already have a very intuitive understanding of this because you've been writing assembly code that you can turn into bytes uh that's one of the key things that we've learned so this is with the if you go all the way back to when we were uh doing the assembly module you gave the program bytes that it took and executed essentially you were writing little Shell Code programs you just didn't really know that yet um and so what we actually want to do if we get to where we're trying to go shell here has a very specific meaning it this is what like a hacker terminology this is like you're trying to pop a shell means that you are trying to call execve bsh so that that way a shell is now running with the permissions of the target program so for instance on the dojo the programs you know well actually I think we have looked at this since right so we we learned about set uid in the access control module so this means that it's owned by root so when it executes it executes as root and so if we can get it to execve vsh now we have a shell as root and we can do whatever we want so that is our goal as attackers and but there are some constraints so think about where we were here right think about there's some user data on the stack and then let's say we could we redirect control flow onto our code one of the key things is that Shell Code needs to be position independent so it it has to not matter where that code lives because otherwise you don't know exactly where it's going to be in memory and you can't rely on any fixed layouts the other cool thing is you can share Shell Code then is there's all these repositories of different Shell Code that does different things that you can then [Music] reuse um cool okay so so I had my hello let's do uh shell code code. s oh wait but if I yeah there we go I need my prefix there or let's do this actually this is good so anybody remember what exit code 60 is I would bet it's exit I actually don't remember how to get all the output though as ah there we go look at that okay do test. S8 so cool okay so this is like ah okay nope that's not right anyways uh okay we already saw this so test. s this is some assembly code that probably I wrote when we were doing these assembly modules I have no idea this was so long ago that it's out of my brain at this point but we're moving one into RDI 60 into Rax and executing a CIS call and so using exactly what we did in the assembly modules right compiling that to an object file then using object copy to copy that to a binary I now have this binary file that's 16 bytes so I had my hello that's reading from input from us so by running that in I don't get a Segal anymore why because I'm running real x86 code not like a string that I had of csse 365 so let's debug it just to check break hello and then I can run it the cool thing is you can do redirection here inside GDB so here I'm running it and I'm saying use this file ASM dobin as the standard input run to here hello I can look at my arguments uh RDI is by one RSI is this I can double check x-5 I so and examine five instructions here shows me move one into RDI move 3 C into Rax CIS call I can start doing next instructions here until I get to the call and now I can step and I can see I'm actually on the stack this s fffff this is somewhere on the stack we're moving one into RDI we're moving 3C into Rax and we're calling CIS call to exit with error code one so now we've tricked this program into executing x86 64 code that we control of whatever we want so now if we wanted to let's grab [Music] here cool okay so now I want to do execve I want to call this CIS call how do I do this do you remember CIS call table your friend the CIS call table exact v e is 59 oh we so close Okay uh in here so move Rax 59 CIS call so that'll call the right one now I need to get uh so now I'll need uh so RDI needs to be string bin sh R SI needs to be zero and R we said it was RDX right yeah RDX needs to be zero so the zeros are easy what's a way of zeroing at a register without using a move exor and our input to the program may be limited when we did gets it read up until a new line but our input it may only get 12 16 bytes that we can trick it to execute as code so uh one of the reasons I use xor here is because it's only one bite this is what I know from experience where they're moving zero into RDX takes I think four or five bytes because it encodes the zero as like four null bytes um so that's actually if you really want to go you're not I think you're only doing three levels of shell coding but if you want to get into it more further and further levels add more and more restrictions to your shell code like you can't use certain characters you have to do it in 10 bytes or eight bytes or what's the final one Connor do you remember it six byes I think six by Shell Code um so it forces you to get creative and understand and be able to to do those things okay so I exort RSI Rd RDX now I need to get this string so normally we did something like this right was it had to data section have something like uh actually can't remember how to do this was it asky sdz like this yeah B sh I need to give it a name let's make sure that compiles that's shocking does that actually work okay we're not referencing it but that's okay okay so then we do that and then we want to move into Rd I what was it like offset of bsh but I'm not sure yeah I don't know either but try it but then definitely look at it to make sure it makes sense wow is that not that's because uh of Shell Code okay this makes sense of why we thought that might blow up ah see messed up yeah but I think it adds the null bite at the end which is important yeah but it's saying it doesn't exist so or unknown pseudo op I could do the null bite myself that seems Sil what was it yeah good job see to add the null bite ourselves do we crazy people okay let's make sure okay cool so here's my input so moving ah my offset definitely did not work um okay I'm gonna GP because I feel like I did this yeah move offset buff so add what why is it not Intel syntax this is driv me insane Ah that's right I feel like you're just tell this is like I feel like I'm just dancing okay so the offset of is not working this is what I'm double checking is this in here uh okay let's do is it something like this yeah oh there we go did that work hey there we go okay so load effective address so calculating the current instruction pointer plus the offset into there move that into RDI clear out RSI clear out RDI move 3B into Rax call CIS call let's always before we just randomly try something and decide if it works or not I always like to debug it first so let's break on hello okay now here I can before I even do anything I can double check this by doing checking here so RSI I know is my buffer so x2i load effective address RP plus 5 plus F which is this address exor exor CIS call and I can even check that address make sure it's the string bsh so let's step through print F skip over that because I don't care about you print F Now call RDX step into there so now I can see loading effective address into RDI so RDI should now have the string if I look at here RDI now points to the string bsh xor RSI xor RDX move 3B into Rax CIS call executing no Pro new program that looks like success this is a classic problem uh so did it work or not work it work but it yeah let's look at this I think this should help illuminate okay so if we scroll up we see our execve bsh so this is exactly what we wanted it's executing and we could look at exactly all these things step step step but the key thing is it's reading from zero which is standard input and it's getting back zero why because the standard input of this program is the output of cat ASM bin so if there's no more in put to read we gave it our whole file and it read it all so you can um The Cheap Trick actually the best way to do this this is why you use pone tools because you can change it to interactive and just interact with it and not have to worry about this you can just send the bytes you can uh write your shell code in your python script in pone tools assemble it and then send those bytes along uh another the cheap trick is to just add a so Dash here means the command line so concatenate sorry not the command line uh standard input so first output assembly. bin and then wait for input from the command line from standard input and then write that to standard output so what this will first do is write out to assembly. bin everything and then if I do things like now I'm typing on here as if I'm talking to that bsh command through the cat uh the other thing I noticed why was it hanging here I typed in LS it didn't do anything that's because gets reads until a new line so we didn't actually send it a new line uh so we could figure that out and then we could do things like um how do I do anyways cool so we just wrote little Shell Code already and we can fundamentally do whatever we want so we'll check this Shell Code out that's crazy this is like exactly almost what we did well I guess is do string implied do ask EZ so it does the thing yeah see we didn't even cheat and we got there together as a group so exactly the same code here effectively uh just slightly different right um so this is uh how we put data in our code which we already saw we can use string we can use asky Z with one I uh we can useb to put bytes directly in our code uh we can this is another uh kind of classic technique is having the data be part of our instruction so we can move this is you have to read it from right to left because of endianness SLB N I know 2f is a slash so I don't know what the other ones are I'm just guessing but if this is correct SLB i n slsh well I miss mess up slsh and then null bite so moving that into RBX pushing RBX onto the stack now the btes on the stack are the is the string and so we can move RSP into RDI so now because because RSP points to the start of the stack so now we have our string there on the stack but we can also do anything so we can we don't have to just execute a shell we can have it let's say uh one of the classic ones is a connect back Shell Code so if you're executing a remote system you can have it make a TCP connection back to you and have a spawn a shell that is listening to your input on an IP port so you set up a thing that's listening there and you can type in commands it goes executes it and tells you the response uh this example is calling open flag and then sending that file to file descriptor one what's file descriptor one normally zero is standard input standard output yes so send the flag whatever is in that file the scriptor to to standard output um and so you can step through this but this is exactly this should look very similar I remember debugging with some of you in recitation your assembly code it looked very similar to this right doing or and building a web server you're just doing system calls this is exactly what shell coding is you're just doing it for a let's say to achieve exploitation rather than to build a web server cool okay we don't need to go over this because we have that running it great if you oh Dev standard in that's lame uh we saw that anyways uh tools on here so you can use I guess should we do this same example using pone tools would that be useful to show okay all right and I'm going to cheat and have my code up here uh I guess do most of you use from pone import Star yeah I hate that but fine uh okay so P equals uh process so execute the hello process um then so what we can do and a way to do this is to write your assembly code here now because it's special pone tools you don't need to have all this stuff at the top and everything so all we're going to have is this extra code so let's go here so now this is a string that exists so I can write my code uh in as a python string and to double check that this is actually working let's I'm going to open up another terminal open up a handy dandy IPython to have it right here as I execute um from pone import Star so let's try this oh I see because ASM oh but I shouldn't call it ASM because isn't that the function yeah uh okay broke it yeah okay Shell Code well we will call you Shell Code all right there is an error and you'd probably have to dig through you can try to figure out what's going on here but I can already see this -32 is wrong we did not set up the context uh context. Arch is that right equals x a664 there's actually another really cool way to do it um how is somebody doing that do you remember from the workshop we did where they said it based on the process Contex or Contex something like that context. EXE yeah let's see oh yeah okay I'm not going to get into that but let's set the architecture correctly now when we assemble it now we get our Shell Code bytes okay so that should be good we can even check by I think we can do disam I like to do this just to double check and of course it prints we want to print it out so I can again remember it's exactly what I did before uh with object dump right now I'm just doing it in Python and using IPython to kind of test that the little pieces of my script are going to work okay so assemble it and p. send line and I know I want to do send line instead of send because it gets is going to read until the end of the line okay and I'll do p. interactive so now I have the terminal here I can just interact with it pretty cool questions so you already have all the skills you need to be able to do all this stuff you've already been using pone tools to interact with the process to parse complex things you've learned assembly I guess you don't even need me to teach you anything right they all laughed at me okay okay but there are are some problems that can occur um these are things these are just more of a reminder you may have forgotten this since you have built a web server and assembly but one thing is be very careful about the size of the memory access that you're using right so we all talked about that the difference between accessing one by versus two bytes versus four bytes versus eight bytes right so you can do it implicitly based on the size of the register that you're using so BL versus RBX it's going to be one by versus eight bytes you can also specifically use these um uh annotations I guess uh to specify to the compiler that you want to move a single bite so this is move bite pointer or word pointer or dword pointer this is being very explicit telling the compiler compiler you only want to access one bite um yeah like we talked about so there was something I didn't show we could have actually really messed up our Shell Code uh and it could have been very annoying if there was what what would have happened if there was a new line in our Shell Code it would read Because gets reads a line so it would read up until that new new line and then stop reading the rest of our Shell Code and only execute half of our code um actually let's look at that we can easily simulate that let's say in here there was a DOT BTE hex1 is a new line character yeah so instead of getting a shell now I get a segmentation fall and the reason is well if we looked in here we would see that this new line This 10 is actually causing it to only read in part of our input we don't we won't see the sing let's dou check okay let's read from here it didn't readed that in why did it read that in H ah I was wrong it's 10 in decimal not 10 in HEX see this is why I you have to double check your assumptions I could have just assumed that all day long Okay so let's look at our so load that and then we can see and this will drive you crazy if this happens to you that your string isn't even in there so it's hard to see that half of your input didn't even make it in and that's because it's not that the program necessarily forbid a new line character uh hex a it's the fact that by reading and using gets reads to the end of a line in a new line so you can't have any new lines and so this is why uh being careful about these things if I was really good in my python script I would have called an assert and I would have asserted that in my shell code there are no new lines that way my python script blows up rather than waiting and having my payload not work and then having to debug the remote system so this is why depending on how the the program is Reading in your input um for instance string copy can't have a a null bite whereas scanf gets gets line F gets can have no bytes but can't have new lines um scan F also uh goes to a white space so you can't have sometimes a hex 20 which is the space character or tabs um often times and this can get very fun is your shell code can be mangled or changed and right because it's input from you the program May operate on that input and then you corrupt something and redirect control flow to your data so it could be compressed or uncompressed or encrypted or decrypted so the trick is to work backwards so start with what you want the Shell Code to look like and just like you did in the reverse engineering modules you started from the key that you wanted very similarly you can start from the Shell Code you want try to work backwards um sometimes there may be parts of the input like let's say it takes your input and then right in the middle it writes some text file or some input that you can't control um you can do if you know that and you should know that by understanding the program you can use tricks like jump so you can execute part of your shell code and then jump over just like in the assembly module when you had those knops the knops of like 53 bytes that you jumped over it's the exact same tricks that you can use to write Shell Code that can survive being overwritten um other tricks uh you know if we control standard input and standard output then we can get a shell or get it to give us the flag but sometimes we may not be able to read the flag back maybe the program closes standard input and standard output maybe uh it's a remote system and so running a shell doesn't make sense and we can't do a reverse shell and so what we can do is use things you can use tricks just like you're learning now in web applications of doing Blind SQL injections you can do similar techniques using shell coding you can do timing attacks so you can check the value of a bite and sleep for a certain amount of time and you can write a script that does that um so there's all kinds of cool things there other things to remember is that your code is being executed in the context of the program and the program has a bunch of registers and a bunch of data that's already in location so as you think about uh shrinking things you can actually think about do we need a shell can we put the flag somewhere else in some other file that's already open uh do you need to communicate the flag in band uh so you need to think about all of these things and with that you'll be great good stuff