 So, first of all it's important to give the right credit. This research is basically James and Sergey's research. I was just as nicotine while we have like some discussions about the version and how to use that for exploitation. So, because I just as nicotine I decided that I will start first, right? So, yeah, that's really good. So, the motivation behind this presentation is basically the fact that software exploitation is not a generic thing anymore. So, we cannot talk about like a specific technique and this technique to applies to all the software. That's not how it works anymore. Software exploitation actually builds up exploitation primitives that we need to use from the target software. So, exploiting software nowadays completely depends on the software we are exploiting on the state of the software. There is no generic anymore. A modern exploitation technique such as the one that we demonstrating here will need to have some primitives established and these primitives need to exist on the target software and we're going to use that to actually leverage code execution. Also, it's important to say that a target software is more capable of computations than what it was originally intended to. Exploitation, exploiting a software is basically leveraging the potential of a target to do something else. In this case, we're going to use something that is there, but it's not something that the developer of the target software actually included there, which is deception handling mechanism in the case of GCC. If I need to resume this talk in only one minute, basically it says that the GCC, deception handling mechanism is capable of unwinding the stack and it runs a special byte code VM. This VM is to incomplete, which means that if we can manipulate the byte code, we can completely manipulate the VM and thus we can do whatever we want. We went. As Sergey and James previously demonstrated, it's possible to even write your own linker only based on dwarf byte codes, which means that you can do whatever you want, any computations you want. Great. So just to pass the theory of all that behind, how that whole history started, I will pass the talk to Sergey. Hello, I'm Sergey Bratysh and I did very little of this work. Most of it was done by James, of which he will appraise you in due course. What we've discovered is that there is a huge amount of computational power inherent in the GCC, exception handling mechanism. Travis spoke of debugging symbols. Exception handling information is almost the same but slightly different and in this difference this is where the most interesting kind of exploits lie. Think about your normal GCC built executable, be it C++ or be it C that at some point you want to propagate a C++ exception through. What do you see? There has to be a stack unwinding at some point. So there is a section in the L binary where you can hold information on how to do the stack unwinding. What you don't see is that what actually performs the stack unwinding is a virtual machine that runs non-native code that's completely different from anything native that you can find in your runtime. It's always there and it's readily invoked if all you need to do is to cause an exception. Then control will pass to the exception handler and then you can run your own byte code payload in that. In order to make this a part of an exploit, which is the part that Rodrigo helped us with, you need a memory leak or some other way to find this entire subsystem. But when you find it, you get everything. Because this system is meant for working with symbols. It's meant for finding things by name. It's meant for mapping out the structure of function frames, libraries and so on. So you can leverage your single memory leak to make sure that you can run your exception handler into what we call a bring your own linker. And we will show you that it really works and you can write it in Elf byte code alone. Now, did you expect this behavior out of your binary executable? And the answer is probably no. Right? Did you expect a machine inside a machine inside a machine that you bring in every time you run something that's GCC compile? The answer is probably again, no. Which lets me speak of a bit of theory here. Our trust in computers is based, when all is said and done, on what we believe them capable and not capable of doing. Say I trust my dumb phone not to hijack my communications. I trust a chip to have so many states as they are able to do data sheet says. And not two more. And so what we're really doing here is when we exploit, we build execution models that exceed the expectations. And as they exceed the expectations, they perform the unexpected computation that is otherwise known as pilgrimage. I try to summarize much of the existing exploitation folklore in this term called the weird machine. So you're boring pieces of code from the target. And you are guiding them. You're using them as byte code, as implementations of byte code to which your data serves as the op codes and the mediates. And if you look at the history of exploitation that say in 2000 there were first examples of return oriented programming which didn't get called that until eight years later. But so it goes. Things get discovered in this community. Then they become a big academic item in a decade. You find your exploit in the corners of the actual system. And you borrow the functionality that's already there. Only the designers don't know it yet. In the words of Halver Flake who was there when we tried to, when I tried to summarize this heck of folklore, exploitation is setting up instantiating and programming a weird machine. So referring to the non-generic nature of the beast. Well, you have to construct those programs. It's no longer a simple trick. So a part of the target is overwhelmed by say an input you manage to send to it. And it enters an unexpected but reliably manipulable state. So if you're thinking of Turing machines or finite automata or any one of those academic abstractions, you get more state than specified in the model. You get more transitions. You get the path through those states that's not expected to be there. Could I get a glass of water? The reality of this has been that you assembled and lined up bugs that were memory corruption or an unexpected data flow. And you constructed your computation out of that using them as assembly instructions. You know, a two byte over right here, a four byte over right there and then over there. And you have a chest booster. Out of your, thank you, out of your target. Thank you. Thank you. Thank you. Too much water. Too much water. Too much water. Excellent. Thank you. So out of your target, a more powerful programming platform emerges. What we're going to do, what we're going to show you is less clever than your typical zero day exploit technique which borrows pieces of code at locations that the designers don't even know about. Or for functions that the designers even don't know about. What we do is we borrow existing functionality in exactly the state that it is already present in the program's runtime space. In particular, it's one of those things that goes into constructing the process runtime. So you think you run this binary and, you know, your main kicks in, but before that can happen. And all the wild ways happening, there are a whole lot of auxiliary computations, automata, the relicator, the dynamic linker, the exception handler. They're all just standing there waiting for something to do or doing their work when setting up or when shutting down. And you've got all of this auxiliary computation that's driven by normal metadata, by normal L-sections. You don't even have to keep them malformed. You can just put the byte code that is not expected in there and have your way with it. So this isn't rope, which was an enormously clever trick. We traced it to the hacker origins on our web page. We have some history of that. It's borrowing that's already there, that is to say, unique exception handler. James, take us to that wonderland. All right. So Sergey likes lots of high level theory and understanding what's going on. And I like understanding what's going on too. But more than that, I really like code. So let's take a look at what we actually need to do here. So all binary is compiled with GCC that support exception handling. So this is primarily C++ we're talking about. It's also GCJ and any other languages using a GCC back at LLVM and Clang or GCC compatible here. So I'm not going to differentiate that. So dwarf byte code is used for describing the stack frame layout because during an exception handling, you need to unwind the stack. And when the authors of GCC were looking at how they wanted to do that, they noticed that it was already in place for debugging purposes. So they just co-opted the same mechanism. So the process image isn't going to include a dwarf interpreter as part of the GNU C++ runtime in libGCC and libStanded C++. And we can write byte code that forces that interpreter to perform any computation. This was mentioned earlier. So basically James and Sergey, they researched that they did previously. They prove that replacing this byte code they can do any kind of computation. That basically gives them like a complete Trojan as mentioned. That does not change the assembly language of the binary itself. They prove that the dwarf is a complete environment for programming. So it can read memory, it can compute values, and it can influence the flow of execution. Of course, these are all the requirements that we went to replace our shell code with dwarf byte code, right? So this work that we're demonstrating now is intends to actually use dwarf byte codes instead of using our normal shell code or normal payload in the exploitation. So how that all works, okay? How the dwarf works. Basically if you look into the elf and the elf layout, you have like some sessions that actually call for attention, which is like the ones in red on the slide, the EH frame header, the EH frame, the GCC exception table. Those sections are actually based on the debugging format, a format called dwarf, which was created for debugging. It has a standard and it provides information such as types of variables, like back traces, and it was created for debugging and was used by GCC to actually provide the deception handling mechanisms. So the debugging frame defines how the UI process works during an exception. The dwarf was created for debugging, but then it was the same kind of standard was reused for deception handling mechanism GCC. The Linux exception handler is defined by three different like documents. Basically we have GCC itself, the implementation, we have the Linux standard base, which defines how this implementation is supposed to be, and the ABI itself. It's not exactly the same as dwarf because they added some pointer encoding and some language specific data, which is the GCC accept table header. As usual, the documentation is not really perfect. So the biggest problem is we have like one implementation and two different standards, and this is based on a third party standard. So when you look into the three of them all together, you start finding some interesting things such the caches that we exploit to actually leverage code execution use byte codes. So James, give us more details. All right. So theoretically, and this applies for both dwarf and exception handling. So we're just talking about the dwarf metadata for unwinding the stack. You have a big table, and the table has an entry for every single possible address of the program counter. And the line in the table for that program counter address tells you how to restore all the registers if an exception is thrown from that frame. You need to unwind from that point of the program counter to the previous frame. So in most cases, this is based on the canonical frame address, which is essentially the base pointer. And then most of the other registers are restored in terms of this. Well, as we'll see later, this isn't always enough. And that's where we get the real power that we need. There's would be a problem with this huge table. I just said we have a table that has an entry for every single program counter address. That's going to create huge size problems. So we don't actually have that table. That's what the table is conceptually. So the dwarf byte code is essentially a language for specifying this table in a compressed form. So here's a view of the EH frame section as it actually is, not as our giant imaginary table. So there's a call frame information section for the whole binary, and that contains a entry for every function. Now, with optimization and so on, you may end up getting one FDE, a frame description entity for two of them for the same function and so on. But in general, you have one entry per function. And that specifies all the dwarf byte code instructions necessary to compute the table I talked about earlier. One interesting thing here is that there's a language specific data area. Dwarf was made to be very extensible. It wasn't designed by any one company to work on any one software or hardware platform. It was designed to provide very general debugging support. So this language specific data area is a completely undefined area where whatever data the implementation likes can be stored. And GCC uses this to store information about catch handlers. So here's just another view of what's contained in the CIEs. A CIE is just for further compression, so information is not duplicated between FDEs. Each FDE points to one CIE common information entry which stores information that's common to all those FDEs. What we're really interested in is the instructions that are stored. So if you're wondering what the hell all this is and who came up with it and why, you're pretty much as surprised as I was when I started reading this documentation. And it turns out that the exception handling part, unlike the Dwarf debugging part which is well documented, is documented very sparsely. And you ask the community and it says, of course, you know, there was this mailing on this mailing list a couple of years ago. It explains it. But the story is that compilers use a whole lot of optimizations these days. And you don't know where your struct may end up. It may be in memory. It may be partially in registers. It may be on the stack with some of those registers saved for this reason or another. And so the designers of the Dwarf debugging format, and of course the debugging symbols are there for finding the internals of data structures and where things are and at which offsets they live and where they might have been saved, thought, okay, great. Why don't we build a virtual machine? Why don't we build this huge wooden rabbit, right? So we actually have a slide about that coming up in a few slides. Why don't we use all this mechanism to just restore state? So we need to think about what's needed for unwinding a stack frame. This big table isn't just going to have an entry that says, oh, you should set this register to this hard coded value. It's going to need to compute how to restore each register in terms of other registers and memory locations. So we end up with a machine that can read from registers and can read arbitrary memory. And I'll show you why in a minute that this is actually not only able to access data, but it's able to perform computation as well. Now there are some limitations to the techniques that I'm going to show you. We can't write directly to memory, at least not within the confines of properly formed dwarf. I've been looking through the GCC code base a little bit and they don't validate everything. So there are some semi-arbitrary memory writes that you can do if you give it some malformed dwarf data. And none of the demos that I'm going to show use that, but it is something to look at if you're doing this yourself. So isn't this nice? We have this engine inside the runtime which knows where things are, which calls on routines specially written to find them. And oh, by the way, the data it receives is always trusted. Because the compiler put it there. Of course. And it hasn't changed between now and then. Between then and now when we're running this. You know, what could be safer? We do have a few other limitations as well. We can't make, we can't call native code directly. This isn't a big limitation in terms of computation because we can do whatever computation we like ourselves. But it does mean that we can't call functions with side effects as well as not being able to write to memory directly ourselves. And our big giant table does not actually have a column for every register. It only has a column for the registers which are cally saved. So the registers that most of the time you'd really want to write on x8664, the registers that are used for parameter passing, you can't actually write those directly. So you end up having to find code sections that load those registers from things that you can control. And then returning the execution to that code section. So there is some weirdness to this programming. But you know, we had to do something clever. If we only just showed up with an existing, here's your root. And by the way, here's your map of memory. It wouldn't be as much fun. The final limitation sounds like a limitation, but it's not really. Not through any standard process, but just through the implementation, GCC limits the stack of the Dwarf virtual machine, which I'll explain the details of shortly, to 64 words. That doesn't sound like a lot, but I wrote a dynamic linker in Dwarf that uses only 20 of these stack words. So really, you shouldn't need any more. The reason is this opcode is designed to be efficient at memory parsing. This is what your debugger uses to look into, to show you the internals of a struct. This is what an optimizer uses probably to store its state. So these are the basic Dwarf instructions. This is what I talked about earlier, with defining a CFA, a base for the frame, and it can be defined in terms of a register and an offset from that register. And then we can define the restorations for all of our other registers, either to be loaded from registers themselves, or to be loaded at some offset from the CFA. And this is all that's necessary for unwinding the stack most of the time. However, the Dwarf designers, as I said, weren't working on a particular hardware platform. They weren't working on a particular software platform. They said, what if there's some weird architecture out there where we can't just specify an offset from the frame base, and we can't just say the registers are stored from another register, what if we need to do something really weird? And we can't anticipate what that architecture is. So why don't we create a way to restore a register from a complete expression? So expressions have their own instructions. So it's sort of a sub-instruction set inside the basic Dwarf instruction set. And this is where we really get our power. We can push, it's a stack-based machine, similar to an RPM calculator, something like that. The stack is just composed of words. And we can push things on the stack, we can push constants on the stack, then we can perform arithmetic on the stack. Then we can dereference memory and push the result of that on the stack. We can read from registers. We can set up conditionals and then branch based on the output of those conditionals. In other words, we have a complete programming language living there that gets run in a virtual machine every time an exception is thrown. And of course, you ask yourself, well, where is the stack that you speak of? And it's your RAM. And you can have this machine basically work into your RAM at wherever you find the symbols pointing. Isn't that nifty? So just to bring the table up one more time, each architecture register is mapped to a Dwarf register number. These mappings are architecture specific. They're pretty well documented for the most part. There's mapping in the XA64 ABI for XA64. You can find mappings for Spark, etc. So each Dwarf instruction either defines the rules for a particular column or advances to the next line of the table. And in general, table rules inherit from the lines above them as a compression mechanism. So all this dwarf stuff is kind of cool. And when I first started looking at it, I loaded it up in a hex editor and I poked at some bytes. And after a few hours of doing that, that gets really old. I'm sure you've all done similar things. So I wrote a tool called Katana, which was inspired by Eresi, the Elf shell. Now Eresi is a great shell. It's a much better Elf manipulation tool than Katana is. But Eresi is the Elf shell. And Katana is a, it's also an Elf shell, but it's designed specifically for Dwarf manipulation. So this is showing the creation of one of the demos that I'll show later. And we just can load an executable into our shell and then you can form operations on it. We can tell it to write out the dwarf script, write out the dwarf internals into an ASCII script that we can then edit and see what's actually going on without poking around in the hex editor. Dwarf programming, compiling to dwarf. More like assembling. It's a very similar to an assembly language. So once we finished making modifications to the dwarf, we can just load it up and then shove it back in our executable. And now we can have a nice Trojan. Yeah, I should say at this point that Eresi, which started as an Elf shell, has grown to be a fantastic Elf discovery project. And of course, this being the most ubiquitous binary format, well PE is a close competitor, but not all that interesting. It is one of the most basic binary building blocks of anything. And yet there is one book on linkers and loaders that explains something about it. Compare that to the stacks of books become an unleashed PHP programmer in 21 days while being a full dummy or something like that. So this format has so much engineering foresight in it, that Eresi was able to demonstrate in extremely clever ways. So I direct you to the two frack articles on Eresi, the unorthodox debugging tricks that they used. And we actually plan to integrate our tool with it, with the project. But unfortunately that hasn't happened for various logistical reasons. I probably because I got lazy. So with Katana, you can do all these cool things. You can control the unwinding flow. You can change to another exception handler, so on, so on. It's turn complete. So if we've got some function, it's responsible for handling an exception. And we decide, hey wait a minute, we want this other function to handle the exception instead. We can just set up the dwarf instruction in FDE to instead of reading the R16, which is the program counter register. Instead of reading that from on the stack, we can just set it to some hard coded value. And then the unwinding will proceed to the function we wanted it to. Okay. So far I've been avoiding talking about the catch handling blocks. And that's because they aren't part of the dwarf standard. Because dwarf was designed for debugging. And when you're using GDB or whatever, there's no notion of stopping the unwinding. Maybe you don't actually want to view all of the frames when you type BTE and so on. But that's in the realm of output control. The format doesn't have to actually specify how far you're unwinding. So here's where that language specific data that I mentioned earlier comes in. So each FDE, as well as having a language specific data area, also has a pointer to a personality routine, so-called, which interprets the language specific data area. And this is what we need to control if we want to actually redirect execution somewhere that doesn't already have a catch block to find. This is where there's really no documentation. EH frame is mostly derived from dwarf. It's pretty well documented. For GCC accept table, there are a few mailing list posts by Ian Lance Taylor. There's an old document floating around somewhere from, I believe, HP that has an outdated version of the format. In order to really see what's going on, you have to tell GCC to give you some verbose assembly and take a look at what it's actually building in the GCC accept table section. So think about packing your documentation with the source. Well, you pack it with the compiler and then it deals it out piecemeal with the assembler, with the assemble code. I'm not sure this is really the high point of literate programming, however. So once you stared at that for a while and looked at what documentation is available, you can figure out that GCC accept table is a big array of these language specific data areas. Each one has a call site table, an action table, and a type table. And the call site table just defines ranges within the function. The action table specifies a landing pad, which is where the catch block begins. And the type table just specifies which types to match when you're handling the exception. And when you look at this, what do you see? It's a complex data structure with a whole lot of pointers and linkage and walkage around it for the pointer jumping. So what if any of this data is actually corrupted? Hell knows. Because this data is trusted. It's hardly ever checked. There's so much we can do with valid data here that I haven't actually played around with corrupting it. Because really, we don't even need to corrupt the data here. So when an exception is thrown, there's a bunch of stuff that happens. CXI throw in lib standard C++ calls into online, raise exception in lib GCC. That unwinds a frame. It calls the personality routine to process the language specific data area. And that's when it looks and says, okay, where in this call site table MI? Is there an action defined for this call site table? Doesn't the type that I have match the type specified in the type table? And if it does, then it sets up registers and returns to where the program counter was specified to be unwound to. If it doesn't, then it just goes in a loop. It should be noted that DWARF contains data structures and for representing your entire set of data types. So you're starting up from the basic integer type basic blocks. Then you can combine them in structs. Then you can describe arrays of them. Then you can describe arrays of types. It is a full description of every type with its binary representation that your program ever contained. This is really interesting because there is a lot of pattern matching that can happen there, which is again likely an exploitable part of the code. So here we see the code for throwing an exception. It's pretty simple. It's just a couple of function calls. And CXI throws what starts all the magic that then goes and runs the DWARF virtual machine. So I'm going to show a demo that we've shown before. We've shown it at Schmucon a little while ago. And this is the Trojan. We set up a dynamic linker which finds exec VPE. We prepare the stack. And we set up the registers that we can control. We return execution to a location that then loads the parameter passing registers from the registers we can control and calls exec. So let me just show you this in practice. So we have a stupid little program. It doesn't do a lot. We give it something it doesn't expect. Like, let's tell it that we want a shell. OK, it doesn't like what we have to say. So I created a slightly modified version of this program using this DWARF script here that just loads the modified DWARF script, sorry, using this Katana shell script which loads the modified DWARF script, replaces the sections we care about, and saves it again. So the assembly code for our modified demo program is going to be exactly the same as the assembly code for a regular demo. So you read through the assembly code for this Trojan and you're not going to see anything that looks amiss. It never calls exec. It never calls system, anything like that. Now, let's suppose, just for toy purposes, that this is some important service that's going to run as a UID. So I can show the difference when we get a shell. So here's our Trojan program and I'm just running as a normal user. It acts normally most of the time. And this is an important point to bring up about DWARF Trojans or about DWARF exploits is that we're hooking the exception handling mechanism, which means it's usually pretty easy to end up with a program that behaves normally until we want to trigger it because usually you have exception handlers that fire when it receives some input. It doesn't expect and so on. But 99% of the time these exception handlers never fire. They're not necessarily hard to trigger, but they don't come up in the programs being used normally. So this program, which has exactly the same assembly code as before, should just reject us when we tell it we want shell, right? Nope, it gave us a shell. So we showed that before over a year ago at Shmucon and what we'd like to do right now is focus a little bit more on actual exploitation. So Rodrigo, do you want to talk about this? So that's great. So we know that there is a lot of computations being done inside this program. We know if we can control those computations, we win, right? As was demonstrated, if we dominate like the exception handling, we win. We can do whatever we want. That's great. But we also know that the exception handling mechanism was inserted when the program was compiled. We don't have control over that. We also cannot overwrite the program itself. So that's interesting for creating a Trojan, but how do we leverage that in an exploitation so we can actually abuse it in runtime in a target program? So the old GCC used it to include the EH frame and the GCC exception table as plus W, writable section in memory, okay? So basically it was possible to just overwrite it in memory if you managed to locate it. What happens is of course, modern versions of GCC don't do that. So what else can we do? So basically, we know that the Libre GCC that is inserted in the program, in the target program, it needs to find the exception table anyway in memory, right? So we know it's located there. We also know that the EH frame location is included on the frame header. So it used the DL iterate header function to actually locate it and it's located somewhere in the program, in the target program's memory. So assuming that we have an overflow and we managed to overwrite a random value in memory, we can actually overwrite a cache that just GCC actually makes. GCC makes this cache because as Sergey mentioned, the compilers try to speed up things, right? They try to do everything really fast. So there is a cache for the location of this area in memory. If we overwrite the cache location, it points, this cache pointer points to the location in memory. If we overwrite this pointer, we can actually point it to a location in memory that we control, which means that we can actually point the exception handling table and all the exception handling mechanism to actually another location in memory. So when an exception is thrown, it will execute with our rules and we win. The way that it works is basically creating a fake exception handler, right? A fake EH. We create a fake table in memory and we will overwrite the cache pointer to point to that location and then we just trigger the exception. It's important to mention that for this whole attack to work, we need to have some conditions, right? One of the conditions we mentioned is a memory leak. We need to find this pointer in memory. The other condition is we need to be able to overwrite this pointer somehow, like a write for or a partial overwrite or something like that. So those are the primitives that we need to have on the code. Main leaks are pretty common, okay? They happen very often, just two days ago Oracle released a remote full main leak that I found in XORG in Solaris. So it's very normal and that's a very common primitive that nowadays exploitation have, okay? So James, talk more about the caching. All right, so I'm gonna go through this pretty quickly because I think we're starting to run low on time. But there's this cache inside with GCC that has the program header. And as Rodrigo said, we overwrite it. We get control of the data being used for the exception handling. So I'm gonna have a little toy demo here. This isn't an actual exploited real world program. Rodrigo was working on one of those, but time got too short. But in this little toy program, we craft a fake program header and exception handling data. And the dwarf data allows us to use relative pointer encodings so we can actually load this data anywhere and not care too much about where that address is. There are a lot of addresses that we do need to hard code. The code that I have here is specific to particular versions of GCC and libGCC. And there are various hard coded offsets that have to be replaced when you use a different version. But they are merely specific to versions, not the randomized location and memory. Right, so the idea here is that there's somewhere on the stack that points somewhere in libGCC. And we get a memleek, we get that value. We find the base of libGCC from that, from known offsets. We know the database and then we have a known offset for the frame header cache, which is what I wanna actually overwrite. So once we overwrite it, we then point it to our data, which in this example happens to be loaded on the heap. I have a little toy program that lets us load files into memory to make it really easy for us. In the real world, you'd have to jump through a few more hoops. So let me just show this demo before it gets any later. So we have a dictionary program which allows us to load a file that it uses as a dictionary and then we tell it to look up words and it tells us the value that was associated with that key. Really stupid and pointless. It has a format string vulnerability in it. Just for a toy example, that gives us a read primitive and a write primitive. And I have a Python script here which does nothing more than executes our program and gives it a whole bunch of nasty format strings. Does some arithmetic on them and then eventually gives control back to the program. This program, so for this demo, I'm not going to show running a full dynamic linker and actually finding exec and so on. We could do that, there's no reason why not, but I already did it for the Trojan case. So for this I'm just going to show that we can redirect the control flow to somewhere else. I'm sorry. So we have a function that never gets called anywhere in the program and we're going to shove the control flow over to there just through the format string vulnerability without needing a Trojan or anything. This doesn't always work on the first try, largely because of the hacky Python wrapper surrounding it. So this might need a couple of tries, let's see. So we spew out a whole bunch of gobbledygook with all our format strings and then we'll give it some input and now the function that was never called from anywhere ended up handling the exception that was thrown. Whereas normally if it got some input that it wasn't expecting, it would just tell us that it didn't expect the input. So Rodrigo is going to talk a little bit more about possibilities for real world exploitation since that's a subject he knows a great deal about. So it's important to mention as well that there is a complete paper available, okay? So we're going to give the link of the paper pretty soon. And in the paper we explained that, for example, for this little program we liquid a specific address and from this other we started getting and deriving all the addresses that we need. So you don't need to necessarily get directly the address of the pointer because that's usually very hard to locate. But sometimes there is some other registers or memory locations that are easier to leak that are going to give you enough information to derive where is the specific pointer. Everything is on the paper. It's important also to mention that this kind of technique is nowadays like if you get a vulnerability that you need to exploit in a system that is like old or that don't have all the modern protection mechanism it's going to be more difficult to implement and use this technique. Because then you need to actually write all your dwarf payload which is more difficult than just getting a shellcode or running or whatever, right? But the world is evolving and it's becoming more and more difficult to actually exploit software. So those kind of techniques are going to start being more and more important as in the 2000s when first started talking about returning to library C and ROP was first China then people started using ROP actually in 2000 nobody actually gave attention only eight years later people actually paid attention on these other mechanisms that you can actually abuse to get code execution. We showed here like when you have like a write for primitive and you just went to override to the cache pointer and then redirect execution but you can do much more like of course as we mentioned like older versions of GCC if you have a write and which means that you can write anywhere in memory that you went as many bytes as you went in the older versions you can just override to the section itself and throw an exception directly you don't need to locate the pointer or to mess up with like a leaks or whatever. So older systems are even easier to actually use this kind of stuff. You can also use like a two stage payloads. So for example, you can write a minimalistic payload a shellcode which basically will remark the EH frame as writeable again and then you just override it with your dwarf shell payload. That might be helpful to avoid filters if you want to do some complex computation on the target and there are some things being filtered rather than your shellcode you write a minimalistic shellcode that just move things around in memory. Of course you can also use like stage return to library and just basically override and do the same with the EH frame area. So those are other possibilities that are much simpler than the one we're showing here which you have a basic primitive and then you need to leak and get the information of the pointer. So more details are available on the paper. We kind of running late on time and we will be like on the QA room actually for questions. So these are the links. Katana is the tool developed by James and Sergey. It's available in Nonginu and in the GitHub you can download the paper, the presentation, the demo code and I promise I will put like a real life exploit soon over there as well. So what's the takeaway here? Let's think about this carefully. You want to trust the machines that you're forced to these days to interact with every day of your life. And in that trust what figures is how many execution engines are there? How many environments are there? How many machines are there that are programmed in strange ways and are there despite anyone working on one piece knowing that there is another that might interact with it and give you a full exploitation environment. Computer scientists have ignored this because this used to be the domain of engineers. Engineers put things together, compose and make them work. Well, now it turns out that composition can have very, very weird properties and composition can give you a much broader set of powers to leverage from a small exploit in another part. It's almost the time to start computer scientists against computing complexity. You really can't trust a machine that is made of a whole bunch of different parts that you have no idea where they take their input, how they take their input, what they can do with that input all over your memory. And that's the way it goes. You gotta cut down on this grand engineering vision of let's have everything Turing complete. Sometimes it's probably not the best idea, trust-wise. If you enjoy lending your platforms for, shall we say, unexpected computation to neighbors, then you're probably cool with that. Thank you.