 Welcome to the combined phenolytesal speech. I have the pleasure of speaking with Harvard Flag here. Hands. And what we are trying to do is actually to bootstrap most of your script kitties into real hackers. So the idea here is we're quickly talking about the mindset. Then a lengthy session on finding vulnerabilities and quick part on writing exploits. And if we got time because the speech got cut short, as you just heard, we're talking about exploiting non-standard stuff. So the first and foremost thing. Who in the room knows who John von Neumann is? Raise your hand. See? That should be everyone in this room. The thing is, every computer today is built after something that's called the von Neumann machine. And this basically makes no difference between data and code. It doesn't matter if Abyte is data or if Abyte is code. Data can become code and code can become data. The only difference is that the data your instruction pointer points to is the code that's executed. That's it. And all exploitation actually depends on this fact. Second thing, please start opening your eyes. If you have something that crashes, don't just ignore it. Don't just click away, Dr. Watson. Don't just delete the core file that was just produced by a web server. Please don't. Take the time and look at what happened. If you surf to a web page with your favorite web browser and your web browser disappears and you surf to this web page again and your web browser disappears again, this is something you want to look at. You want to know what this web page does. What this web page actually sends your web browser to make it crash. Take a debugger, look at it, try to find out what happened. Don't ignore everything. Most people just walk around and stumble across vulnerabilities every day and just don't realize. So start looking. And here's a warning. Don't make any mistakes about it. This is real work. If you want to do something, you have to learn the stuff. You have to take a lot of time to find vulnerabilities. If you write exploits and they're supposed to work on something else than your local computer, it takes time. You have to find out how to make them reliable. So you have to really put some work in there. This doesn't come for free. And unfortunately, just in case you don't want to waste your time, brain is required. So if you don't have one, just don't waste your time with the rest of the slides. So finding vulnerabilities. Here's the deal. What we want to have is ODE, and possibly our own ODE. So what we're looking for is something that handles network input. Anything. As long as it handles any type of network input, we are fine. We look for something that runs on a remote system. Reason being, if we exploit our local system, it's kind of boring because, well, we know the administrator password already. And please, if you look for vulnerabilities, look for something that is complex enough. Except for Cisco, it doesn't really make sense to try to attack a UDP Echo service. In case of Cisco, it's different. So what testing methods do we have? We can do manual testing, pure groundwork, make your hands dirty. We can fast stuff. We can do static analysis. We can div stuff. Or we can do runtime analysis. So manual testing basically means you use the thing the way it is intended to be used. You use the client that's supposed to be used. So if you're testing a web server, what would be declined? Yeah, a web browser. So sometimes you have things that are a bit more complex. And you should actually then use the client that gives you the most functionality. What you're doing is you just use all the functionality and look at things. You look at the reaction of the target to valid input. What is it supposed to do? Most people start installing web server and try to find bugs without knowing actually how the web server works. You have to observe the difference between valid and invalid input. Because if you're having just the idea of how it reacts to invalid input, you cannot compare. You don't know what's going on. And you need to find out what's going on. The most important thing is here, observe the correlation between your actions and network, the network communication that's going on. Both timing and what's been sent. So please start using the sniffers. Start looking at what's happening at the cable while you're using something. So you look at information that's transmitted before and after the authentication. Well, after authentication is sometimes kind of boring. But still, if you have a normal user account somewhere and you want to become a more privileged user there, it could be interesting what data is transmitted after the authentication. But of course, if you have something that is pre-authentication, it's even better. One very important thing is the configuration of your target. If possible, try to set up the service you're trying to hack yourself. Don't have someone come in. Like, don't call your local system administrator and say, come to me here and install this Oracle database on my computer so I can start looking for Lichfield type of vulnerabilities. No, install it yourself. Because only that way, you understand what they're thinking. You understand how this thing is built and how you're supposed to configure it. Maybe you actually find something really simple that everyone overlooks and you see because it is a configuration mistake and not something that you would find with a debugger or with static analysis. And look at the login capabilities. Instead of reverse-engineering the whole thing, if you can set it to full debug mode and it will tell you, I'm tcp.cline417 and just receive the packet. And then it tells you tcp.cline1,007. I just passed the packet. You know exactly what's going on. So there is absolutely no point in wasting time. Just check what it can do. Things to look for. Input validation on client side. Really bad thing, really widely used. Simple example with a web application. If you have a web application and you go there and you enter a bunch of As and it tells you you're not supposed to enter a bunch of As but never talk to the server, it is client-side authentication. This is why it is important to look at the cable at the same time you do something. If it is client-side authentication, then you have, of course, the possibility of changing the client to something different like a code script and send a bunch of As over and see what happens. Because they may assume that since they are checking it on the client side, it is already checked and already fine and nobody could possibly enter a bunch of As. Also, look of input that is modified before send. Again, web browsers, for example, modify special characters by doing HTTP encoding before they are sent over. Now, maybe you want to send over this special character and yeah, you have to, again, look at the cable, what's actually happening when you click on something. Then you have to look at pre-authentication data. Stuff that's sent to this target service before a username and password combination is validated. So this could be your host's name, this could be username, this could be certificate content which is really interesting. I recommend if you are having a web browser and you often use some servers that use HTTPS, I recommend building a certificate with a really long, distinguished name. But don't be surprised if you install that as a client-side certificate and you surf around the internet and leave just burning, crashed web servers. Date and time strings also can be really interesting. If your date suddenly has a 10-digit number amount of hours, some servers react funny. And version information, often clients send their own version to the server so the server can determine what type of client it is. Here's a good example with SAP. They do that for every packet so you can per packet decide what version of the parser you wanna use which is really interesting because the parsers are not built to parse a packet from version four and the next packet from version one in the same session. What are things to look for? Again, the network protocol is really interesting. You have to determine what type of size field you have or if you even have size fields. If this protocol is just static sizes, the assumption could be made by the programmer that is only this type of information and this size of information coming in per packet. So if you send more, you might actually overflow a buffer. More interesting, far more interesting are dynamic fields. Here's a good example. If you see something that has like a four byte number, most of them zero and the last is eight and then you see eight characters following it, this first part is probably the length field. So we have one length information here. Then you can start playing around. What happens if you change this length information, let's say to something with the most significant bit being one and suddenly on the server side, it becomes a negative value. Interesting enough, often you see this, you have a terminating zero here. So this is then really interesting because there are two information points on length. So the code on the server side called use either the length field or the terminating zero, which is often done, actually it's often implemented as the length field is used in the beginning of the pausing and then everyone else just assumes it's a zero terminated string. This can go wrong and this is what you want. Also, timing, concurrent connections, lots of them. If they're having a table where they hold all the information and you put a lot of connections in there, this table might overflow. Or fast sequential connections. If you connect fast and disconnect and connect fast, you might get things out of sync. Adventures of manual testing. There is no need for additional tools. You don't need any lead hacking tools. All you need is your client, the browser and well, your brain. You become familiar with the target. This is very important. Don't start hacking something that you don't know how it works. You have to understand what principles they said and what they were looking for to do and what they ignored and what they didn't do during the development cycle. You have to understand who you play against with. Those are humans. Those are humans who wrote this code and those are probably stupid humans. You just have to find out how stupid. Another advantage, it uncovers as you have seen client-side security really, really well and this is really fast. If you find a client-side security, you can be pretty much sure that you can exploit your service somehow. This advantages, of course, it has a potentially high learning effort, but what hasn't. It often just provides clues like the protocol we just saw. You can't just change the protocol in the sniffer and re-inject the packet. You actually then have to go and write additional code to try it out. And there is a high dependence on the tester, which is you. There is a high dependence on how much you're actually thinking about it. What you usually find with that is the classic cross-site scripting crap, code and SQL injections and web applications, protocol-based overflows as we just saw, and application logic failures. Application logic failures are not found by any other testing methods so far and can be really, really useful, especially if they're web applications that have a logic that, for example, depends on transporting the price information over the browser. So if you understand this and you see this, you can change the price and get your favorite toys for free. So it is best suited for web applications, Java crap, proprietary clients, and of course, internet explorer as lately often seen on Bucktrick. Another way to test any target is fuzzing. Fuzzing is basically the idea of creating a rough client that exceeds the policies of the normal client. So for example, again, if you're talking HTTP, you're creating a client that is able to support a 4,000-character long URI. So, and this is then used to send it over to the server. It is best suited for documented protocols because you have to re-implement the whole thing. So HTTP comes to mind, FTP, RPC, DCOM. Again, web applications can be really interesting and protocols with many field combinations. Let me show you why. There are two different ways of doing fuzzing. There is what I call CMAR manual fuzzing, where you actually go ahead and you write a short script or program that does the first two, three steps that the usual client will do and sends over data, which is potentially malformed. You're manually changing single information one at a time in this client and just send more in this field or more in that field or change the length where you hear or play with this while you're there. It is your imagination, what you can do. You run the code, you evaluate what happens. You look at the response. You look at the timing because it could be interesting if you send 10 As and it takes a second and you send 100 As and it takes two seconds. Obviously it is handling those As somehow. So maybe you wanna use that later in the exploitation process to make sure that the process is busy for a while. The other approach is automated fuzzing. In automated fuzzing, you actually write a script or program mostly actually almost all the time using a fuzzer framework and basically what you do is you write a program to generate random crap in large amounts. So you're coming up with all types of random crap you wanna send there and then put that in the program and let the program deal with increasing the amount of crap or combining the amount of crap and doing other stuff. All you wanna do is let it run for a while and see if your target crashes. So what you try to find out with the semimenual fuzzing, as I just said, you're looking for unexpected responses. If the web server for a file that's obviously not there, response four or four file not found, this is a expected response. If it responds something like you got blue hair, then you should investigate that further. You look for modified data. One example here is if you send data to a web server, let's say you send a bunch of As, just a few and you get an answer back file not found but it converted those As either to Unicode or to HTTP encoding and has therefore sent you something like percent four, one percent four, one. This is interesting and you should really look into it. Why? Because he needed three bytes for this encoding and you send one byte. So maybe there is a miscalculation. Maybe it didn't calculate correctly how much bytes it needed for the encoding. You look for changed timing behavior. Does it take longer or is it really quick? If it disconnects really, really quick, it probably just crashed. And of course you look for target crashes best on the system itself. So if you have the target, you should attach a debugger to it while you're doing the fuzzing and see if the debugger at some point in time pops up and says, wait a minute, this just wrote over way too much data. The procedure here as I said is you go ahead and try to get your script work in the first place. Please, I've seen that so often. People writing scripts for attacking something and iterate through all possible ways to send crap there. And if you ask them, okay, can you modify the script so that it works? They're like, yeah, try this and it doesn't work. So make sure it works in the first place. Make sure in the beginning it is valid input that you send and then start throwing crap at it. Change the fields one at a time. Don't change everything at the same time and then try to figure out which of the changes actually made the target crash. Send it over, inspect what came back, change the fields again based on what came back. You always learn something. Even if it just says no, you learn something. You learn that it handled it and that it didn't wanna parse it further or actually parse it and say, no, this is not good. Yeah, and then repeat until you found a bug. Here's an example where a timing issue was found. There is a largely known company that writes security and secure software and this has a timing issue with fanciness. Timing issue here is, if you connect too often to PC anywhere, it will not really handle the loading and unloading of the DALs that it needs to paint this fancy little icon in the right corner of your windows. And so instead of like changing the icon color from green to red to let the user know that someone connected, it changes its state from running to crashed and pops up a message box. The automatic parsing procedure is a bit different. Here you have to start thinking in the beginning and then just wait. You have to define what vulnerabilities you wanna look for. What type of vulnerabilities? Do you just look for overflows or do you look for format strings as well? Do you look for integer related crap? You have to define that and then write the script or program depending on your father framework. And then basically you just start and let it run and let it generate all kinds of interesting messages and see if the target crashes. Again, you should attach a debugger to it, especially on Windows because there is this thing we're coming to this later that's called structured exception handler. And so the programs are actually allowed to handle the situation where they fucked up themselves. So if they fucked up, they can say it's almost like a lawyer. They can say, I registered this lawyer here and if I fucked up, please call this lawyer and he's dealing with that. This is the same procedure. And if you don't attach a debugger, you don't see this because the lawyer is just dealing with it and behaving as if nothing happened. And you have issues with process forking as well. So we're covering frameworks. The most well-known is Spike by Defatel. It is currently 2.9. It's a block-based father written in C. So your housing programs have to be in C as well, which of course is kind of like error prone. It has some functions for sending data and some functions for doing iterations on screens and creating bigger blocks and more amount of blocks and stuff like that. Unfortunately, there's almost no documentation so you better read the demo programs that come with it. And since it is written in C, you often have things like this. Don't make a mistake. This courtroom here is actually the fuzzing program and not the target that got fussed. Also, don't trust it too much. Here we have a case where it just fades to calculate the content length field for HTTP correctly. And so the web server doesn't even pass the request because yeah, it figured out as well. There is another framework that's not released yet that will probably be, if you're lucky, over the time of DefCon. It's written by Michael Addington. It is a Python object-oriented class structure and consists of server elements, including generators for static elements, transformers for all kinds of encoding and decoding and crypto, which is kind of handy if you're fuzzing something that's in an encrypted communication. It has protocols so it can actually handle the state over multiple protocol messages, publishers for sending the stuff actually or putting it in files or whatever is required and groups and scripts so you can even put more abstraction on it. And the good part here, besides being actually real good code, is it's actually documented as well. This is a full-blown HTTP get request fuzzer in Python using Peach. So you see you don't have to code lots of stuff. You just have to specify what you wanna do and then send it over. Advantages of CMAR manual fuzzing are, as I said, the try and inspect procedure that you follow. The fact that you're actually looking at responses and learn every time you send something to the server helps you to really fast find all kinds of vulnerabilities. And of course the good thing is if your script sends a bunch of As in this or that field and you get the target crashed, you can use this script and slap your shellcode and return address in there instead of the As and see what you got in exploit. Automated fuzzing is really good. If you have a good done fuzzing script or program, you can just throw it against all kinds of different service and see what happens. So you quickly find stuff. And due to the fact that it is a program, you can basically have all kinds of combinations done. So if there are many possible combinations in a protocol you're attacking, then you should use this. Disadvantages, of course, you need to understand the protocol in the first place. Otherwise you can fuzz it, but it's kind of like boring because the server will never pause it. The tester has to rely on the fuzzle. So as you see with a spike example, make sure that the stuff actually makes sense. And you need a debugger on the target system, as I said. So I'm passing on to Harvey here because static analysis is actually where he grew up with. So he's more fit in it. Good afternoon. Well, we went over looking at stuff manually, like at the application behavior, and we went over fuzzing. Both is not being my world. I usually take in December and read the code. So yeah, but I'll be talking about this. Well, I'll just skip this slide. Anyhow, the stuff you'll need for actually reading a binary is, you need the binaries at the target, which is sometimes a hassle because people tend to charge money for these sort of things. So you need a decent disassembly, and there's only one decent disassembly out there, so get yourself a copy of IDA. There's a free evaluation version on a web data rescues website, and if that's not enough for you, you might have to share the few bucks. Well, it's worth it, definitely. Well, yeah, you need to know what self-language the target is written in and so on. The two more important things we should need to know is fluent assembly and fluency because while you're reading assembly code, that assembly code has been generated by a C compiler from Valet C code. You're looking for bugs at the C level in the assembly language, so understanding how those two things interrelated is a basic prerequisite. So how do you go about finding bugs? First of all, we find reachable references to functions with vulnerability potentials. String copies, sprint def, and so on. All right, nevermind. So you look for places where there might be bugs. We can start with stupid things like string copy and sprint def and so on, and the fact that you're still finding these sorts of bugs really shows you what the current level of quality and commercial class or software is at. Then, once you've found a function that you basically drop criteria for every function, string copy, sprint def, you might actually find functions in the binary which are not standard C functions, but which have certain criteria where they can blow up on you. So you try to drop criteria for every function when it's like what preconditions that I have to satisfy in the arguments. And then you go to the binary and you first look at all the calls, look at whether the preconditions are satisfied, and the preconditions are not satisfied, like meaning there might be a bug. You basically check whether the user input, like whether the attacker can influence the arguments coming in there which will violate the preconditions. And then you basically just try to find out whether stuff is reachable, obviously. Since you're looking at the entire program at once, you're very easily relive code where application is starting up and relive conflict files. Now, being able to find an overflow on a conflict file is not all that useful because you might have to be administrative to meet the conflict file in the first place. So you kind of want to limit the code constructs you're looking at to those parts which are actually usable from the main user input parser. So after you've found a function which violates the preconditions and you are sure it can influence those things, you should pause, like, read upwards of the code to see whether the application is checking the input somewhere because it's usually quite annoying to spend a lot of time working your way down through the tree, like through the call graph, getting to a certain point, and then finding out that the application was checking certain values right at the beginning or whatever and you just missed it. All right, it is, I mentioned before, and that it is really important to make sure that the function you're trying to reach is reachable from the main user input parser because if not, well, yeah. Hey, I have an overflow in the pausing of the password file. It's very good, but if you can write to it, you don't need the overflow. So you kind of want to find codes which read user data first, which might be received, received from, there is a received, there is a received from. You might want to know a bit about RPC when you're looking at RPC components and so on. Then you just follow the program for until you start seeing code that actually does some pausing on these things, like comparing strings. If you have a mail server, look for the SMTP strings, ALO, hello, whatever, and then use the location of that main user input parser in the call graph in order to identify whether the locations you've found actually reachable, meaning you try to find out whether there's any path in the program that can lead from the main user input to the function where you're trying to find the bug or where you have found the bug and you're trying to get to it. All right, so the more important things to remember when you're doing this. In order to find bugs, you have to know the language, like the C language better than the programmer. Your job is being a smart guy and a mid picker and a language lawyer about C. So the NZC standard should be your Bible. It's the best 10 bucks you'll ever spend. You should read it, you should read it, and yeah, you should read it. And it is really important to know that like having small gaps in your knowledge will make you miss, like have large gaps in your auto coverage, meaning if you don't know a whole lot of assembly, you miss a lot of things. If you don't know a whole lot of C, you miss a lot of things. So make sure you get your basics straight, like you know assembly well and you don't see well and make a point on being very, very, very rigorous on understanding what the language semantics are and what is quite useful in order to learn more about the bug process or about bugs in general, read lots of open source diffs. Nice part about open source is they told you, oh, we found a bug here, we're fixing it. Now, you should read those diffs. It's just kind of like crossword puzzles. You print them out in the morning, sit down with them, what the heck, where they're changing, why are they changing it, what's the implication, and where might this have happened, like, are there any other places where this might have happened? Because oftentimes, somewhere, we'll give you all the rates where. And usually, like critical kernel bugs will be fixed in the development kernel first before they're back-ported. So that gives you like a few weeks advantage. Yeah, so, another thing which you really should always be aware of, that different compilers work differently and there's an astonishing number of compilers that are just plain broken. Like, they have buggy libraries, they don't implement the C standing correctly. The balance here is a fantastic example. If you malloc minus one, like four gigabytes, you're trying to success and corrupt the heap at the same time, because what happens is they take the size, like the size of the request, and they add the size of the control structure which they have to use for managing the block. So you have minus one, you add eight for the control structure, you add seven. You allocate seven bytes, and then you add an eight byte control structure into the seven byte. So just calling malloc minus one will fuck up the heap. And there's a lot of things like this, where compilers just don't implement the standard correctly and are broken in themselves. And then there's a lot of code that will work in one compiler and be vulnerable in the other one. So those are definitely places to look. The interesting thing about the standard is in many places the standard itself is ambiguous, where it doesn't really phrase explicitly what should happen when f3 is with a size zero and so on, or f gets with a size zero. And then you just read through the standard and you're like, okay, this isn't a bigger statement. Let's see how different compilers work to it. And then you just see a compilers question left, right, and center. That's a bit of a lawless mindset. You have to become like a C language lawyer. You read the standard and you see that there's ambiguities with something not totally clear. So, you should get to the point where you can solve two more questions like these. If you look at the following code there, then signed into A with a value of 10 and assigned into C with a value of minus one. Then unsigned into B equals 10. And then you have the condition if A plus B is larger than C, which means 10 plus 10 is larger than minus one. And interestingly enough, that condition evaluates to false. Now, this is relatively easily explained in terms of, well, A and B, A and C are signed, but B is unsigned. So, if there's an unsigned component of the compilers, compilers will be unsigned. C will not be interpreted as minus one, but as foreboding. Obviously, foreboding is larger than 20, fair enough. Now, the interesting part comes at the bottom where you have essentially the same comparison with different types. Instead of having an int and an unsigned int, we have int and an unsigned short. And all of a sudden, the exact same comparison evaluates to true. Meaning, well, 10 plus 10 is all of a sudden larger than foreboding in C. And where's that coming from? So, quoting from the ANZE ISO C standard, page 57. It's called integer propagation. If an integer can represent all values of the original type, the value is converted to an int, otherwise it is converted to an unsigned int. These are called integer promotions. So the trick basically is, we have an addition in a condition down here. So, the thing is A, B and C have explicit types, but either ints are unsigned ints. And the question truly is, if you see this addition, what type does the result of A plus B have? Because if any sign in the comparison, if anything in the condition is unsigned, the comparison will be unsigned. If both sides are signed, it will be signed. So, we have A plus B, we're adding an unsigned value and signed value, and we don't really know what the type is. So, the standard answers is, if an int can represent all values of the original types, the values convert to an int, otherwise it is converted to an unsigned int. These are called integer promotions. Unsigned int B, in the lower example. All right, unsigned int B weird, can represent the values from zero to four billion. Four billion does not fit into a standard int, which can only hold two billion. Thus, A plus B, we have the type unsigned int, which means this side of the comparison is unsigned. Whoops, sorry. Which means this side of the comparison is unsigned and this side is signed. One of the two sides is unsigned, comparison is unsigned. Down here, dammit, that's all right. Down here, we have A plus B, but B is unsigned short. The maximum value unsigned short can have is 65K. At 65K, it fits comfortably into the two billion that an integer can hold. So, the language will dictate that B, A plus B is going to be signed. Both sides of the comparison are signed, comparison ends up signed, 20 is larger than minus one, and yeah. So, love your law and love your C language standard. Anyhow, going back to studying analysis, the big advantages of this stuff is, you'll find vulnerabilities in code that's not normally executed. If you're just fuzzing, you're basically covering the normal trend in code parts, and all the testing, actually, like the testing on the vendor side, will travel the normal trend code parts. You're interested in those parts that are hardly ever reached, because that's where bugs hide. Bugs tend to hide under stones, not in plain sight. It's the same with computer bugs. They hide in places where nobody's looking. So, yeah, there's a lot of things you can find very quickly, but very easily. Format string bugs are very easy to scan for, and you'll find really, really funny and really complex vulnerabilities just by reading the code. But this is the advantage you need to have a lot of time. You need to spend a few years of your life on it, and, yeah, you need to have a kind of a thing for pain as well because reading is something that will make your head hurt, and you've got to like that. So, there are a lot of disadvantages. It's got a lot of time just reconstructing shit, and C++ code is not totally annoying to read, because you have all those virtual method codes, which basically tell you that you can't really tell them there's static disassembly where a certain code is going, unless you spend a lot of time checking it down. And there's certain things that are really, really almost impossible to read, decently, visual basic. If you compile it in a certain manner, it will not be compiled to native code at all, but into an intermediate level in a presentation, which will then be emulated, and you really don't want to read that. All right, so I mentioned before that diffing is a really important concept, and diffing for source code is relatively easy. Now, binary diffing is something that's a lot of fun as well, basically because vendors have gotten to this habit of not telling us exactly where the bug is anymore, and that's quite annoying. So the goal is to take an updated piece of software like the Microsoft security update, and quickly identify where the bug is, because most people take a few days in order to install them, so if you can have an expert the day after the cache was released, you have a few days before they install the cache, which is good. And seriously, diffing takes a lot less time than auditing because somebody else has done the job of auditing and finding the bug, you just have to look for their change things. So it's a bit of a more economic way of breaking the systems. If you just have the time of sitting in front of the system and to wait for a few weeks until a new patch comes out, you diff the patch, you break into the system, you've just saved yourself a lot of work and it's always good. And now, the other thing is that you actually sometimes get all day all of those patches because vendors tend to fuck up those patches in ways that you wouldn't expect, like patch things incorrectly, disregard signings and the added range checks. That's happened to some people at some time. And then there's other things basically because if you have a large company like Microsoft and a bug is in the library and somebody fuzzes an application and the application crashes due to a bug in the library, they will report to the product group for the product that crashed. Now, the product group will try like implement a patch in the product to work around the fixing the bug in the library instead of actually fixing the library. Now, this is quite amusing for us because we see the work around, we see what's broken in the library and then we break our hard disk for whatever other application might be using the library and we get free order. Happens to Microsoft, for example, when they did the ISA server H323 patch, they get everybody net meeting the nodes. That's nice. All right, so first of all, you need to find out what files actually been modified in the patch. You can do it either by, well, kind of depends on what the patch is, but with Microsoft, we usually just create a large directory with a random name. You just abort basically the patching process and load either directly there. Just kill the process once they ask you for something and then you have a directory without the files they're updating. If the application or if it's something more complex with actually modifying executables, you might want to use something like Formon. And then you have to disassemble the patch. Doing this by hand is a really time-consuming process, meaning you have to disassemble all the functions to see which ones are identical when comparing them. That is really, really, really time-consuming. There's ways to do this automatically. You, well, you need a bit of graph theory for it. And I'm not going to explain too much of it. There's a paper at that address there which basically explains how the algorithms underneath it work. And that's quite nice. After the Microsoft release of the S channel, like overflowing PCT parsing patch, takes about an hour when you receive the patch to figure out what the bet is. And it takes about another six hours to overwrite EIP, about 12 hours to have a fully working export. And that is quite nice, especially considering the fact that there seems to be a law in Germany that prohibits financial institutions to install any patch without testing it for seven days first. Right. Coming from the static analysis, you can actually do the same thing with runtime analysis as well. So the idea is here instead of like using Ida as a disassembler and looking at the static stuff, you're actually basically look the process on its fingers while it's doing it in runtime. You do pretty much the same thing as in static analysis, but the thing is it's more a observation type of thing instead of completely reverse engineering stuff. So it's again a manual process. And what you need is a functional version of what you're trying to break. So if it doesn't work on your computer, don't do runtime because yeah, you have nothing to monitor or attach to. Of course you need a debugger. You need again fluent disassembly and you need again at least a clue of what the library functions on your platform do. So get yourself a reference. For the debugging thing, you can do a lot of fun stuff with runtime debugging. And I figured that on Windows there is actually no open source debugger. And I wanted to waste a bit of time of my life and change that. And so here it is. This is dump bug. It is a C++ architecture. So you have actually a debugger including pausing from PE files which is the annoying DL or Nexo files. This is assembly thread handling and all the crap that you don't wanna care about. And you can basically instantly create debuggers in a few lines of code. This is how it looks like. This is a full blown Windows debugger in C. You basically need to tell your compiler that there is a debugger. You need to create a debugger variable and you basically load the program and let it run. This will already deal with everything that can possibly happen to a piece of software on Windows. And from here on you can either register callback functions or overload the class. And the class actually provides a few virtual functions for all interesting things like exceptions happening or setting breakpoints and stuff. And you can overload those and build your own debuggers however you like it. So in long-term analysis you can do one of two things. The first thing is data follow procedure. So basically when stuff is received you break at the receive call, you look where the stuff ends up and you set a memory breakpoint and then you let it run for however long you wanna because it will stop as soon as someone looks at the data. This someone is probably the protocol puzzle and therefore this is the point where you're interested in. Again unfortunately you have to spend a bit more time than once you identify this place and do reverse engineering and find actually out what it's doing. The other thing is basically the same thing that you do in static analysis. You follow code while it's executed. The good thing here is you can actually step over stuff if it looks like it's not interesting and if it was interesting, well do it again. You again check arguments that are passed to functions when they're called and you check arguments if they're user data or not. But actually you wanna do this for stuff like as printf or string copy and the problem is if the programmer was a real brilliant one then he has about 1,000, 2,000 calls of sprintf in his program. You don't wanna stop every time he calls sprintf to print his maiden name or whatever. So you wanna actually just see a few cases where it actually handles user data. So it's one in a hundred calls or something which is kinda boring so it's impractical. Advantages here is that in contrast to static analysis the disassembly you see when the stuff runs is correct because if it's not correct it would be passed to the CPU as incorrect and therefore just crash. So you have basically a guarantee that what you're looking at is actually what the CPU is executing. You have a known state so you actually know everything that the computer knows and you see again advanced vulnerabilities that are really interesting funny combinations of things that people could fuck up. And so it's slightly faster than static analysis because you can skip over stuff and you don't have to find out where indirect calls lead to and stuff. Again it is time, skill and experience required. So you can't just go home and do it. And you often have timeout issues so if you're like inspecting a protocol's pauser it could easily be that at some point in time remember the time that the packet came in and then does a different code path because it's been about 10 seconds on this thing because you were stepping through it instead of just running through it. So you have to watch out if the code path you're seeing is actually the one that's usually executed. The usual findings for static and runtime all kinds of overflows in pretty much every possible location in the code. Format string vulnerabilities are totally easy found and integer vulnerabilities are usually not found in testing methods like fuzzing or manual testing. So it's good for all kinds of stuff especially protocol pausers. Logging is something that many, many servers break on. So every time they wanna lock something they have to copy it in a text buffer and this is where they often fuck up. And you find code using unsafe functions like sprintf or string copy and whatnot. Now I mentioned that you wanna see only the sprintf calls and stuff that are relevant to you and they're with user data. So I actually wanted to prove the point and write a demo for the debugger core and so there is a Ltrace implementation for Windows now. It basically locks calls to functions before and after. So you see what arguments are passed to the functions and what comes out of it, what happens there. Supports a very different call conventions because if you're trying to hack something that's written in Delphi and compiled in Delphi, yeah. I'm not natively yet because I didn't wanna use any Microsoft code like for PDB. So it would require you to use Debug Help DLL which comes with a DDK which means that I'm supposed to spend money on crappy Microsoft code so I can redistribute it and I didn't feel like this. So it does support Debug Information Code View, FPO and stuff like that, not yet PDB but it's coming. Another good thing is as far as I know the only debugger on Windows set is able to follow fork. So if your process is actually responding in child and this one will just create another debugger and follow this child at the same time. So when you're fuzzing something, you don't miss it because it is in a fork child. It does a bit of stack analysis when the call is made and it does of course format string analysis. It works by having trace definitions. So you basically take your arbitrary msdn, information and copy and paste the C prototype into the trace definition file. Here you see you have a function name then you have the different arguments to the function. You can actually specify if this argument is something that is coming into the function that is a input value like the socket in receive or if it's a output thing like in asprintf you wanna see the output buffer, you wanna see what came out of it. Reason for this is you can actually do matching on strings. So you can actually say hex or equals in asprintf and then it will only lock calls that actually produce a output that contained the word hex or which is pretty handy because you can just attach it to a process and let it run for a while and then send all kinds of crap to it and this crap should contain the word hex or and you only see calls to asprintf that actually deal with stuff you send it to. This is the output, here's the good thing about it, you don't need to read disassembly for it because it translates the calls to C again or to something that looks familiar. You see you have different types, you also have the type fntshar which basically says this is a format string and should better contain format specifiers, otherwise it's probably broken and you see the return values here, the addresses of the buffers, stuff like that. Here's another example, this is actually from a server that I found a vulnerability and wrote an exploit for using the tracer which is a good regulation. It turns out it is a server for online game and someone like really screwed me up in the online game and I wanted to pay back. So this is a custom function in the code here. You have a parameter that nobody cares about then there is a format string and a message structure where the login message is written to. What happens here is you see this node that's generated by the tracer saying that there is no format specifier in the fntshar type thing and it turns out that this blah thingy here actually is a message that is coming from the user so here you have your run of the format string bug. Adventures from call tracing, it's extremely fast. You just have to attach the tracer on a process, click around, use the process for a while, terminate the thing, look at the output and basically you can use your text editor and search for the word vulnerability in the tracer output. You might actually find something right away. You can recognize user supply data so you don't have to find out if this data actually reached any point or if this is user data or not because you know what you send to it. If you're tracing a web server and there is an sprintf that contains the word Mozilla it's probably something you send over. And as you have seen it does automatic format string vulnerability detection which is lame, everyone can do that. Disadventures, again it's incomplete so the code stuff is found by static analysis. You see here only the most trodden code pass and it covers only function calls so if someone actually does a pointer I would make a copy or something, you don't see that. So of course you're free and it is recommended to combine those techniques so don't like aim for one and make only this. So one combination that I'm right now fancy is fuzzing using peach and some pretty good script to it and then attach the L trace to the target so in case it forks or in case it throws an exception this is covered. It also see my manual fuzzing and static analysis can help a lot so if you're doing see my manual fuzzing and you have something that looks suspicious then you go load binary up and read the code that's actually doing and learn the stuff that you're wondering about. So a few slides on writing exploits. First of all please don't write exploits in C. I do so. You do so, yeah, please don't. It's really, it's a la prone to the end. I mean we're exploiting C programs all the time. Why do we write exploitable exploits? It is not portable. Just to make a case for writing exploits in C is if you're attacking a system and you want to use an exploit from that system on you might, well of course you can always forward traffic and crap, but you might just not want to have to install Python in the other system. So we see there are different opinions. But don't. Because it's not portable. How often do I see people writing exploits in C that are not even portable between a VINNMS and a VST system? So if I need like five hours to get the exploit working in the first place it doesn't get me anywhere and it's ugly. So go ahead, use parole, use Python. If you're really sick, use Java. Use something that's portable. Export creation process. You start by basically finding a vulnerability. This we covered. Then you find out how to trigger this vulnerability. Then you try to get code execution. Yeah, you're actually aiming at getting your code to execute. Then you try to get somehow shell code over there and then you execute the shell code which basically gives you the root shell. To trigger it, make sure that when you know how to trigger the vulnerability, this works on more than one computer. This would be really good because if it's only working on your computer, you can only exploit your computer. So try it, please, on more than one computer. VMware comes to mind. You can actually have more than one virtual computer on your computer and try it. And observe the environment requirements. Here's an example, which is not too easy to solve, actually. It looks easy because you do a gap request and a lot of A's and send it to the web server and it crashes. This is fine, this is what we want. The problem is that if you look at the vulnerability, it is actually putting the original path, the real path, and the request together and hereby overflowing a buffer. Now the thing is, you don't know the real path. So either you find a way to disclose the path or you find an intelligent way to exploit that. But this is an environment requirement. You don't necessarily know what the path is the server is installed on. Code execution. When you want to basically influence the instruction pointer to point at data that you influenced or sent. First of all, when you try to point the instruction pointer into your buffer, use something that will stop executing. Don't use capital A's. Capital A's are the A increment opcode on Intel. It will just walk over it. So use something else. Use FF, use the debug break. CC is good. C0 is an illegal instruction. And don't limit yourself to the overflow buffer. Don't try to stuff your shellcode into the buffer that you overflowed. Those are two vectors. The one is to trigger the vulnerability. And the other one is to transport your shellcode. Don't limit yourself by putting both vectors in one. Try to get out of the function as soon as possible. So if you can influence the logic somehow, as soon as the overflow happened, try to reach the return as fast as possible. Because there could be reading of local variables or writing to local variables or function arguments, which later screw your shellcode. I got a fancy graphic for that. And this is what you usually have. You have a buffer. This is because the buffer is broken. You have a variable, base pointer, return address. So when it enters the function, it will actually write a return address and a base pointer here. And then you have the buffer overflow. And if this happens, you're screwed. If it tries to use this variable after you overflowed the buffer, this is bad. So try to prevent this. If you can influence the code path, try to prevent reading of the variable to actually then reach this position where it reads the data you overflowed. So what return address should we use? There are different ways of doing it. There is this classic way of following L of 1 during direct return to the buffer. This is bad in operating systems that are used today because they tend to be multi-threading and they tend to have different addresses for stacks all the time. So don't do this because it's not reliable and you will not get anything that works on systems that didn't belong to you and showed. So the best way or the most common way right now is return to a known code location. Find some location, some code location that actually has an instruction like jump to a register, call a register, pop something and return if you have a pointer to your buffer on the stack or at ESP something return is the same case if you have a pointer to your buffer, higher up the stack, use that. Or use the structured exception handler. For those of you who haven't seen SEH, the structured exception handler is, as I mentioned earlier, the way for the program to handle its fuckups himself. So the good thing is the SEH is usually stored on the stack, the address is. So instead of just overflowing up to the return address and then return somewhere, overflow further till you hit the first SEH. What happens here is basically you overflow up to here then NTDL comes along and says exception happened, tries to call the handler and what happens, it executes your code. This is a very reliable way of getting code execution. Useful jump and call register sequences are sometimes really hard to find because if you, for example, do a union code, you only influence two out of four bytes and the other two are mostly zero or something else depending on the translation table. This is really bad. And there's a solution for it, which is pure simple brute force. Yeah, I'm not intelligent enough to do anything else so brute force is my tool of choice. Which is searching the entire net memory address space for something that is addressable and then search from there if it would actually fuck up the exploitation process or not. This gives you all possible return addresses that you could potentially reach. And since I was playing around with return address stuff, I actually wanted to put an end at those return address guessing and by hand finding stuff anyway and also did the same thing for plain ASCII return addresses. The good thing here is you can actually save all the return addresses that you found for one version of the software and go to a different installation and do the same there and then do what the good thing here is it gives you a reliable return address that works on both. If you do that five times, you have something that's probably working all over the world, which is what you're owning with it. The whole thing is put together in a plug-in for OliDBG. OliDBG is a Windows debugger with a fancy graphical user interface and stuff. It actually supports quite a lot of stuff and should be used if you don't have your favorite Windows debugger selected yet. Use Oli. This is a plug-in for Oli and you can basically attach Oli to your target process and have it find return addresses for you. This is how that looks like. Here it is a Unicode return address. This here is a position that one code address with this byte sequence and the undit translation of Unicode. But this obviously is not a call or jump to a register but down here we have one and the plug-in, since it searches from every addressable position, code find out that in between here there's no memory access and we don't care if it fucks up the registers because the registers contain crap anyway, we just overflow the buffer. So this is just playing around and we really don't care what it does and then it's calling ebx and we get code execution. So now we have code execution and we need to get the shellcode there. Shellcode is a general term describing data that became code. Who paid attention on the first slide? This is exactly why Von Neumann is good. This is why we are happy that everyone that's computer is following Von Neumann. Here the data becomes code and now we need to create this data which is useful. So if you're not so good in assembly, you could actually write your shellcode, a small program that opens the listener with a shell in C or something else, compile it, take the assembly out by disassembling it and make it then position independent or written forbidden characters like zero bytes. But I actually recommend writing it in assembly right away. So get network by assembler and yeah, write it in assembly. It's good training anyway. When you write your shellcode, try to make it flexible. Try to not rely on anything on the target as little as you can. Just system calls is the best way to do it. And use for addressing, make sure that you have position independent addressing. So don't rely on addresses because you don't know if your shellcode is going to end up at the same code address all the time. Here's an example for getting the address of the string bin as age here. The way you would do it is you jump from the beginning to the end. From there you call the beginning and then pop EVP or whatever because the call will store in return address which is right here where the string begins on the stack and when you pop it down, you have the address of EVP. The reason to do the call at the bottom and not at the top is this. You have here a negative call because the offset from the call is upwards, not downwards. So it's negative, so it's all f. If you had it the other way around, it would be a zero bytes or the zero bytes because it would, yeah. Just if there is, if the difference is less than like, what, four gigabytes or something, then you have zeros in the known way to transport the shellcode. You can have another buffer in the same request and basically point to this buffer later on where you send data before. Maybe you have to send a username before sending the overflow. Why not put your shellcode in the username? This is a good idea. Shellcode also often gets transformed. So you have zero bytes that get introduced or that you don't have to have in your shellcode for the string operations to work. You have other forbidden characters. For example, if the overflow has something to do with the path, probably dot and slash are not allowed. And so you have to take care, those are not in there. And you have encoding. Again, Unicode can be a real bitch here because it changes like 50% of your code at least, which looks like this. You have a piece of really nice code in the beginning and then it went through Unicode conversation and got like Paul screwed up. And you have to tackle this somehow. The way to tackle this is first published by Chris Enway from Next Generation. They called it a Venetian shellcode because it tries to close the blinds that were generated with the zero bytes in between. And this is why there's called a Venetian shellcode generator. The reason why there's a generator is he doesn't want to do that by hand. This is tiresome and you can actually go and put it for the paper by Chris and then you know why you don't want to do that by hand. So there are actually shellcode generators for this. They fight it out again, maybe one in Python that's public and you can download it and we actually have a code implementation that creates a bit better Unicode, Venetian shellcode and this is also public. So this is what it does. You have your stuff sent over and you have all those zero bytes in here which you don't want to have. So this code over here goes ahead and fixes itself basically. But just go byte by byte and fix them back to what they're supposed to be and then start executing from here. So finalizing this, what we can do now is trigger the vulnerability, point to instruction, point action, some data that we can influence and which at this point in time becomes code. We can get our code or data some home there so we can actually execute it and now we have to try it out and this is where reliability for the exploitation comes in because if you're writing an exploit and it works, let's say on an English version of Windows, make sure that it also works on a Japanese or Chinese version of Windows because you're probably using it more against Chinese web servers than against English ones. So use a debugger, check it with a debugger, check it without a debugger. Use different OS versions and languages and just try if it works. Make sure it is reliable. Do you have anything to add for reliability? No. Sorry to wake you up. Oh, it's all right. So the good thing about this is, this is all there is about writing Oday. So at this point in time, you actually got your own Oday. Now exploiting non-standard stuff is absolutely the same thing. I had a lot of people last year coming up, how can you do this? We'll show it on Cisco. There's no difference. It is, again, a phenomenon machine. It got a CPU, it got Grandimax's memory, it got some permanent storage, it got network connectivity. So why not hack it? So go ahead, hack routers, hack printers, hack cell phones, but help to the pay TV stuff, ATMs, whatever comes to mind. If you see something with an ethernet jack, and you can't get target and start learning it. So what you see then often is non-intel CPUs, something that's not in your PC. There are a few small differences, the major differences, it's nicer. There's almost no CPU out there that is as ugly as Intel. So as soon as you start learning different CPUs, you will find out that there are really nice CPUs out there. They often fix size instruction sets. They often have alignment requirements, which is really important when you're writing shell code. They have different condition codes and different conditional execution, which can be really handy because you can write way cooler shell code than you code on Intel. They have some tricky stuff like delayed branches and they usually have more registers, so you can actually do more stuff with it. Again, the process is absolutely the same, all you want to do on a different OS that might have a difference of year, get a shell first, and then get a debugger running on this shell. And all you need then is basically understand the platform. There are a few things to understand, like process management, memory management, privilege mechanisms, and granularity. It doesn't make sense if you're trying to write a local exploit on a platform and then realize there's only one privilege level on the platform anyway. So you're exploiting to get the same privilege level you had before. This is pointless. Don't do it. So lead up on the stuff, find out how it's working, but don't believe everything. If it tells you it has offer overflow protection against everything, don't believe it. They wouldn't possibly do that. And if they do it, they probably fucked it up. So exploiting backboxes. There are talks here on hardware hacking, but I'm preferring a different approach where it says basically backbox testing. You can look for undocumented debugging and hidden functionality in terms of logging capabilities. This helps you a lot. You can make a lot somehow, a lot of information, which helps you to determine how it works internally. If you have like, how many of you have those little home routers at home that do ADSL and wireless and stuff? There's your hand. Okay, I expect at least half of you next year with an exploit for it. It is really, really easy. Those things are totally broken and you don't even need complex debugging because most of them actually have just an unsoldered serial interface on it. So connect to the serial interface, see what it does. It might actually even come with its own debugger already installed. If you have something more complex, like you wanna hack your neighbor's car, you need a JTEC interface, which is basically a boundary check interface in the first place where you can use it for problem debugging. If you want to. But try and error does as well. It's fine. If you have an overflow, try to find out if you can get code execution the same way you did on Windows or Linux. You have ways to find the cells. Take like an infinite loop in the assembly language of this processor and put this in message code. And when it stops doing anything, but getting really, really hot, it's probably running in circuits in this infinite loop. So you get your code execution done. Self-interpreting results. Find out, not just like try stuff, but also try to think about what happens and why it happened. So basically, this is the summary unless you have anything to add. Do you? You might want to spend less time in front of the screen than you think at the first loss because you need things different on a screen and a paper and a real park. And it's a very common phenomenon, especially in our circuits, to spend all the time in front of the screen and then don't see the first, all the trees. So it's a good idea to step back at times and just look at different things. Good point. So those are great. And I guess I open up for questions because surprisingly, we even have time for that. Come on, please. If you don't have questions, you didn't get it. Okay, so... There is one. Cool. I saw the questions, what's different? The question was differences and opinions on internet versus power. Okay, see, I pass that on. That's not much to say. A park actually doesn't have a nice scenario, but in which, I mean, hopefully we'll be happy if you correct answer on that. So, okay, since everyone is already leaving, I declare this show is closed.