 enthusiastic geocacher and a security researcher Assistant professor at Purdue University in Indiana. So let's hear Matthias Pyer on new memory corruption attacks Thank you very much for the introduction Thanks guys wait with applause for until after the talk This is not just my work But it's the work of a whole bunch of students that I want to get give credit to At a whole bunch of different places from ETH Sturick to UC Berkeley to Purdue University and a bunch of other places And I want to especially call out Nicholas Carlini who did a lot of the coding work and This gives me the opportunity to give you a little bit of a peek towards a demo that I will do at the end Of the talk and I we will drop something interesting at the end of the talk so stay tuned But this is not just to talk on on memory corruption, but a Whole journey that happens and it can best be described as dr. Strangelove or how I learned to stop worrying and love the sec fault In the in the last couple of years. I've worked a lot of a lot a lot of time a long time with software and different forms of vulnerabilities memory corruptions and a whole bunch of Bad things that can happen and the world that we are currently living in is that all the software that we are running on our systems Is unsafe and insecure and low-level languages like C and C plus plus trade all the type safety and memory safety For a so-called potential of performance They don't necessarily give you the performance, but they allow you to maybe get the performance But you lose all the type safety and memory safety that you would get otherwise from safer languages and the programmer It's himself or herself is responsible for all the checks that have to be coded explicitly in the in the source code and if any of these checks are missing then a pro program just explodes in your face But it's not just a large set of legacy Applications that we are running on our current systems, but also a whole bunch of new applications They're being written in C and C plus plus that are still prone to different forms of memory corruption books And I just want to call out that even at companies like Google where they Look very closely to coding standards and code reviews They still have a whole bunch of security vulnerabilities if you just look at the at the Google Chrome browser that is being pwned every single year Also while finding and fixing bugs is very important They're just too many bugs out there to find and fix manually So we have to protect the integrity of our systems through some form of additional safety mechanisms that are out there and if you just look back at the Over the last two years or the last couple of months there have been a whole bunch of Security vulnerabilities out there that are calling for stronger and better defenses And just to call out the few that made it to the press there was heart heart bleeds that basically corrupted all the All the crypto keys out there on all the different servers that we were running There was shell shock that allowed us to execute arbitrary commands on different different servers And there was there was ghost which basically allowed us arbitrary memory corruptions In any program that just did a get host by name call so What we are facing as a huge amount of memory unsafety that our current programs Have and that we have to deal with so different forms of additional security mechanisms and security guarantees But let me take a step back and talk to you a little bit about about the different memory safety problems that we are facing So to give you some some additional background At the core of any security vulnerability, there's an Invalid dereference either through a dangling pointer What we call a temporal reference, which is at one point in time our pointer in the low-level language pointed to a valid allocated object, which was later freed by the programmer and Is still accessed after after the fact after it has been de-allocated and the other alternative is an out-of-bounds pointer Which we call spatial memory corruption that is the pointer at one point in time pointed into the into a valid object and then through the Execution of the program started to move outside of the object and is now pointing into an illegal memory region, which is it was a it moved out of the of the correct bounds of the object but It's only a violation or a Invalid dereference if the pointer is read written or freed according to the C language standard You are completely free to have out-of-bounds pointers You're free to have dangling pointers as long as you do not dereference them and this makes finding these vulnerabilities and bugs so hard because It only becomes a bug if we are actually using this invalid pointer But not if we just have them in the program and at any point in time There there are tons and tons of of illegal pointers in the program, which is completely safe until you dereference them And there are two types of attacks that build on these Memory unsafety issues, and there are either control flow high-check attacks where an attacker tries to execute some form of code or Inject new code or just execute some some different behavior that is not inherent to the original program or Alternatively, there are data only attacks where we where the attacker changes some data That is then used along the way of the program to change some internal state of the of the program to achieve a specific specific behavior Today in this talk we focus on executing code only because this is usually the prime Motivation of an attacker an attacker wants to execute code on a on a compute platform to get additional Capabilities on top of it. So how does a control flow high-check attack look like? basically in In high-level terms we can abstract a program into some form of control flow graph and then the execution of the program follows this control flow graph and Passes from one note to the other one thereby Executing the program and in this sample control flow graph We have a small loop and a condition that then breaks out of the loop and the control Flow of the application as the application is executing passes through this control flow graph which is an abstract concept that compiler writers usually use and It moves from one note to the other note as we are executing through the program an Attacker at one point in time might be able to modify a code pointer Code pointers can be either function returns in direct jumps or indirect calls These are all instructions that are used on our hardware to control how The control passes from one basic block to another basic block basically from one node in the graph to another node in the graph And if an attacker can control these code pointers that are then used in the program the attacker can redirect the execution to a different location So after modifying such a code pointer through one of the earlier mentioned memory corruption vulnerabilities The control flow leaves the valid control flow graph and starts executing new new instructions at a different location and basically Leaving the valid control flow graph and the attacker can then reuse different parts of Existing codes that are in the execution image of the process and either through return instructions Where when it is called return or in the programming or through indirect jump or indirect call instructions when it's called jump or in the programming but let's switch the view a little bit and Look at the particular code example So we do have a small vulnerable function of a couple of lines of C code And let's just assume that the attacker controls the two parameters given to this function user and user two We also have a function pointer that is that is being Defined somewhere in this in this program But the attacker can use these parameters that are passed to the function to actually Overwrite part of the the function pointers We will see later and the underlying problem is that We the attacker can force an out-of-bounds memory write through this behavior By forging an out-of-bounds pointer and then De-referencing that pointer to write the specific value to to this memory location if you look at the stack or the memory layout of the program We see the individual variables queue We see the buffer we see the function pointer, and we also have a piece of code at the bottom So initially in a valid execution the pointer queue would point somewhere into the buffer and The value user two would be written into that buffer or it can then be used by the program in a legit way but the attacker can use this This addition to redirect the pointer queue to point to the function pointer to the memory location of the function pointer instead of Into the buffer and as we continue when it is being when Q is being dereferenced the attacker can overwrite this function pointer and then make it point into the code below and as soon as the Program executes or dereferences this function pointer, which has been overwritten by the attacker in steps one and two before The attacker can start executing Codes that is being controlled by the attacker and reuse different forms of gadgets And these are basically the two building blocks That we have in in current attacks and that attackers use to actually get control over the program first We have memory on safety Or memory safety violation that the attacker uses to build up the later stamps of the exploit and then the Control flow of the application leaves the valid control flow graph off the application and then the attacker executes arbitrary code But we do have safety measures, right? There has been a plesora of proposals of different forms of safety mechanisms that have been added over the last couple of years And we've got a whole bunch of different defenses that are active on our current systems and Let's just quickly go through them so that we can see where their limitations are and what other things are possible So we started off with data execution prevention where we removed the writable bit from several locations in memory and the executable bit from other locations Before data execution prevention we could just inject our new code somewhere on the stack or on the heap and The attacker could just redirect the execution to these locations on the stack or on the heap and then Execute this newly injected code But it was the advent of data execution prevention Attackers have to resort to already pre existing code as no new code can be injected into the executing image of the application So this ensures that we can no longer just inject new code like shell code or any Or other pieces of codes that can be used by the attacker to escalate his or her privileges a second step is address-based layout randomization instead of allocating all the different pieces of Often of an application at well-known locations We just scramble these locations around every single time the application is started and this makes it harder for the attacker to guess where the Where the exact locations are Obviously, this is prone to information leaks if the attacker can somehow learn where these individual locations are the attacker can build and exploit that actually Can mitigate these defenses We also have stack canneries that place strategic values on the stack that should not be overwritten and safe exception handlers on top of that that Guaranteed that the exceptions follow a predefined pattern so we've got a whole bunch of defences out there that Protect us from some of the vulnerabilities that are out there, but as we see with all the All the successful exploits on current software these defenses are not complete and are at best Partial and make attacks a little bit harder, but they do in no means stop the attacks also ASLR address-based layout randomization and data execution prevention are only effective in combination if both of them are used together on the on the system at both times because if you break Data execution prevention you can inject new code if you break ASLR You know the addresses of existing code and can just stitch newly new locations and code locations together and then re-execute already existing code as soon as you break ASLR you can reuse these existing code code locations and I just mentioned before that information leaks actually enable you to infer the locations of specific pieces of code and you can then stitch together the well-known code locations to gain full code executions on desktop systems such information leaks are quite common and It is possible to find information leaks in different pieces of software on servers in the last couple of years code reuse attacks have decreased and attacks have gotten harder due to Because they're just aren't that many information leaks on servers. So this summer we actually Worked on an attack that leaks the ASLR address-based layout randomization base addresses of all the concurrently running virtual machines for different Windows versions and different Linux versions by using an memory de-duplication site channel that many cloud infrastructures use and we can basically learn the ASLR base addresses of concurrently running virtual machines by just generating random or Well-known memory pages that are just iterates through the available entropy bits of the ASLR base addresses and then learn these other addresses But for more information, I would like to refer you to the to the paper and to the to the wood talk Also the folks that worked on it the two students or the two other folks that worked on it are here as well And they can be reached to you if you if you have questions. So The status of the deployed defenses is fairly incomplete and we constantly see a lot of attacks Are going on and exploits are possible on current systems But there must be some secret plan somewhere out there that helps us protect against many of these new upcoming attacks We somehow have to defend against all of these new exploits and Academia has come up with with hundreds of different proposals on how we can protect against these These different forms of memory safety vulnerabilities. Many of them are very Unpractical due to a high Either high performance overhead or other forms of overhead or incompatibilities with existing software But two proposals that are gaining more and more momentum are stack integrity and control flow integrity And we are seeing more and more deployment of these these two defense mechanisms, but let's start with stack integrity so We want to enforce an additional set of restrictions on return instructions on the Source code level the return instructions are or the return from a function to the caller is a very Valid fine function and there's you it is usually a one-way function. You can only return to one specific caller at any single time in the During the execution of the program, but at the hardware level. This is not a restricted operation as the Return instruction is implemented on all the existing hardware architectures It basically allows you to read a code pointer from the stack and then redirect the execution flow to an arbitrary Code locations and what second integrity does is it takes the semantics from the programming language from this high-level programming language And enforces these semantics on the overly permissive instruction at the at the hardware level So one example to enforce stack integrity could be to read to protect the return instructions through a shadow stack So for example, if we do have these code snippets here That are where we can call Foo from either function a or function B We can enforce additional integrity on top of these these functions Or on on top of these returns on the hardware level Then as we are executing function foo, we can return to any bytes that is executable or map this executable in the In the image of the executing process But with stack integrity we can enforce that if function a is calling foo We will return only to function a as soon as we return from function foo On the other hand, if you're calling foo from function B, we can ensure that foo will only be able to return to function B at this point in time and thereby we can ensure that the guarantees on the Programming language level and the semantics on the programming language level will be upheld by the underlying Executing program and we can guarantee that the control flow never leaves this This static valley find graph The second security principle that I would like to talk about is control flow integrity and control flow integrity at a Concept level tries to ensure that the execution of the program never leaves the statically determined control flow graph To ensure the security property. We first have to statically construct a control flow graph and for each individual indirect control flow transfer Instruction we have to find the set of allowed targets at at compile time and At runtime we execute an online set check So for each indirect control flow transfer We do have a check if the target is any of the possible targets that were determined at Compile time if it is in this set of allowed targets we allow the check to continue if it is not then we terminate the application so to show this to you symbolically if we execute a A function pointer if you execute a function through a function pointer without control flow integrity we can basically Reach any possible executable byte in memory So there are no restrictions on the machine level same for the return instruction We can basically return to any arbitrary executable instruction with control flow integrity We add additional restrictions So we first check the function pointer if it is in this set of statically determined targets if it is not Then we terminate the application same for return instruction we check The number the the current return instruction is used in At runtime against the statically determined set of allowed instructions or allowed targets at compile time If it is in this set then we allow it to happen So this is slightly different from stack integrity that I talked about before right and we'll see a Larger example in a bit, but what basically happens is that under control flow integrity the attacker may write to memory at any possible time and The underlying memory safety violation is allowed to happen Only at a later point in time this code pointer is verified when it is used and there is some time between the underlying memory corruption that the attacker can can use Until the code pointer is checked later on and there's something that the attacker can influence in that time window But let's go back to the to the example on the two callers that both call foo under a control flow integrity policy on the stack if a calls function foo Then foo can later return to either a or b both are valid targets and the analysis that looked or Searched through the code through the source code found that both a and b are calling function foo So both a and b are allowed return targets when we're returning from foo and maybe the attacker can use this to His or her advantage at one point in time to redirect the control flow to either a or b as both targets are allowed At runtime compared to the statically Determined set of targets and this basically brings me to new forms or novel code reuse attacks And I would like to start with control flow bending which is trying to work with Nicholas Carlini and Antonio Barrezy that did most of the work Both at one of them is at UC Berkeley the other one at ETH Zurich and the underlying idea is that Instead of high-checking the control flow to a totally new location in memory We just bend the control flow a little bit along a valid control flow graph So we are not reaching new locations that are not visible in the program But we are bending the control flow along the way So if we look at an execution of a program each individual control flow transfer is valid by itself but the trace of control flow transfers is Not valid and might not match the non exploit case So if you look at a trace the individual look control flow transfer Might not be possible due to the different constraints that are upheld along the the trace But each individual control flow transfer by itself is valid and This will allow us to circumvent static fully precise control flow integrity As we will see in a in a bit the underlying limitation of CFI is that it is stateless and each individual state is verified without any context and Control flow integrity as a security policy is unaware of all the constraints between the individual states So any bending of the control flow a long possible valuable valid states is Undetectable as according to what the control flow integrity policy can observe So as an attacker we have to search a pass in this abstract control flow graph that matches the desired behavior of the attacker and I'm not talking about some weak form of control flow integrity Weak control flow integrity has been broken and is known to be broken And there's a whole bunch of papers that were published at different security conferences Last year that show that weak forms of control flow integrity is broken And by the way Microsoft Microsoft's control flow guard is an instance of a weak control flow integrity mechanism and can be can be broken by any of these mechanisms that were just cited here It might make the attack a little bit harder or harder in some cases But it does not stop any attack from happening So let's move to strong CFI and see what we can do under a strong control flow integrity policy the assumption that we use is that we define a Very precise or the precise is possible control flow graph so we assume that there is no over approximation and Any possible compiler or compile time analysis will always have an over approximation and thereby have imprecision along the way, but if we assume that there is no Over approximation and we have the most precise possible control flow graph and then show that this control flow graph is broken Then by design we can show that any possible CFI implementation can be broken this way because even with this unrealistically overly precise control flow graph we can still break it We assume that there's some form of stack integrity for some cases And thereby define fully precise static CFI So that a transfer in the control flow graph or in the program is only allowed if there's some benign Execution that uses it so if we have an a witness of an execution trace that actually uses this target only in this case Will we allow the target at runtime? Let's look at two possible Two possible forms of CFI first one control flow integrity form with and then without stack integrity and For simplicity, let's start the CFI without stack integrity So let's assume we have the strongest possible Analysis on the control flow graph and we have very small sets of possible targets for each individual Indirect control for transfer. What is the best attack that we can do under this condition? Ideally we will resort to some form of return-oriented programming and If we don't have stack integrity the goal of an attacker is to find some paths to for example system in this control flow graph that we have and we have to find a set of constraints and Memory locations that we have to write to to divert control flow of the program along this path Which might be constrained through the memory vulnerability that we have and as a second step we have to control the arguments to system and We might ask ourselves. What does such a control flow graph actually look like and for a long time we thought that this control flow graph is this super super complex graph that is very complicated and it will It will be almost impossible for an attacker to find a pass from the actual vulnerability Through this complex graph and thereby controlling all the different arguments and constraints to then actually reach system on the other end So the assumption is that it is very unlikely that an attacker will be able to find such a such a pass but What does a control flow graph really look like? in fact We found that there's a large amount of functions That are basically connecting and densely connecting the control flow graph between all of these individual locations. There are All these functions like mem copy Maloc and a whole bunch of of other functions or printf that basically connect an arbitrary point in the control flow graph With another arbitrary point these functions are called from everywhere and many of these functions Can override their own return address or in a later point call a function that will then be able to override its return addresses and Under the control flow integrity policy that I presented before as soon as we find Such a function we can return to any of the possible callers of this function and thereby connect Two arbitrary points in this control flow graph because both of these points will call this function And Then we can easily find a pass through the control flow graph from the vulnerability to the system and then control the arguments and the attacker wins The dispatcher functions are often frequently called and yet the arguments Can be under the attackers control through the different forms of memory corruptions that we discussed before The dispatcher functions may override their own return address So if you look at the the simple mem copy Call where the attacker can Supply the the three arguments. We just use this mem copy call to overwrite the Its own return target and then redirect and connect these two parts of the control flow graph Basically, we generate a shortcut through this control flow graph and then redirect the execution flow to that other part And can then continue on that other location and We can basically supply the attacker data, which is the other location that called mem copy Overwrite your return address Make a shortcut and then start executing closer to system until we can execute our our code So basically control flow Integrity without any form of stack integrity is broken these stateless defenses are completely insufficient for the stack-based attacks that we present here and Such dispatch dispatcher functions are fairly frequent and we can find different set of dispatcher functions They're basically being called from everywhere both in the in the standard libraries. They're supplied and used everywhere, but also in the actual code of the of the programs The attack Now becomes program dependent So while the defense does not stop the ongoing attack It makes the attack harder and the attacker at least has to find that his pet share functions And then find the shortcuts through the control flow graph, but the attack is still possible so it looks like we are Under under some trouble here, right, but we still make the attack a little bit harder so an other attack that was presented fairly recently is counterfeit object oriented programming and This is not my work Not the work of my collaborators, but I found it a very neat neat attack that that Should be mentioned here as well and the underlying idea is that a function can be a gadget by itself and you can reconnect and tie together different parts of an Of Counterfeit objects to execute interesting behavior so if we look at this the C++ code here where we define a small class and We have this little destructor over there that is of course a virtual the structure and The virtual keyword in C++ tells the compiler. Hey, I want this to be an indirect call and this allows us to Redirect execution because we just heard that redirect call indirect calls are The underlying principle for all these counter flow high-check attacks But then again, there's a in this example in this destruct which is iterates through a list of Students that will all be Deallocated and we we call different forms of virtual functions on all of these All of these objects that we have accumulated now assume that the attacker controls the the list of students that we have or the array of students then the attacker can suddenly generate a set of virtual targets and Can reuse existing virtual virtual tables to execute arbitrary behavior and as a second step Use these different forms of of virtual tables and connect them in a new way and There are different forms of arithmetic gadgets. There are different forms of Other gadgets as well right so for example in this simple update score We've got an arithmetic gadget that we can we can combine and a mem copy gadgets as well Then attacker can just stitch together Also they presented an interesting technique that allows them to overlay objects Remember the memory is under the control of the attacker and instead of just having each object by itself the objects themselves can overlay each other and the memory layout for a simple Exam objects would for example look like as shown on the on the right-hand side on the screen but the attacker can then use existing fields and overlay these fields with other objects and thereby use different forms of arithmetic gadgets to for example update the Virtual pointer table of that other object and redirected to somewhere else and Through this object overlays the attacker can just stitch together individual Individual gadgets and then get different forms of arbitrary execution arbitrary behavior And this looks pretty bad if we combine these two these two attacks both control floor bending and Counter-fight object-oriented programming But well, we do have a large amount of control floor integrity proposals out there So we might want to look at how well they hold up against all of these different forms of attacks and I picked out a bunch of different different control floor integrity mechanisms that have been proposed either by academia or by Google or Microsoft and others so there's there's lockdown that Enforces a dynamic control floor integrity policy on top of binaries by using a form of binary analysis So you don't need source code for that There's MCFI and pie CFI which are two forms of source-based Control-flow integrity that needs a full compile time analysis of all the available code There's Google LLV MCFI that has been recently released and is in the process of being upstreamed into the new New releases of LLVM and there's also Google IFCC which they presented last year But have since redacted and removed from the source base and we'll see in a little bit in a little bit why they removed IFCC and there's also Microsoft's control-flow guard. There are many many others that Implement different sub policies of these CFI mechanisms here so Just to to give you some context when we're talking about CFI the strengths of the defense depends on the size of the equivalence classes that we have and If we have a certain piece of code We have a set of indirect control flow transfers and these are the instructions shown on the left all these Instructions can be used by an attacker to redirect control flow from one location to an arbitrary other location Any control flow integrity analysis Will return a set of equivalence classes an equivalence class Contains a set of targets that are allowed at specific locations and here in this example I'm showing three different equivalence classes and The last two call instructions are using the same equivalence class so multiple indirect control-flow transfers can map to the same equivalence class and The same set of targets are allowed at these different indirect control-flow transfers and then there's the size of of an equivalence class which shows how much Opportunity the attacker has to redirect the the control flow to different locations and Ideally you want to have many equivalence classes with a very small size The smaller the size ideally the size of an Equivalence class would be one in that case the attacker would not have an opportunity to redirect control flow to an alternate locations But at best we want these equivalence classes to be as small as possible So we looked at the Precision on the forward edge so for indirect call and indirect jump Instructions and we looked at the sizes of equivalence classes and we present whisker plots where we show both the median with the red arrow of the Of the sizes of the equivalence classes the 25 percentile and a 75 percentile But we also show with these little pluses on the side different forms of outliers the different CFI policies are on the x-axis and dynamic shows the amount of Possible targets are actually required at runtime So we executed all these these software or all these programs and measured how many indirect control-flow transfers are actually used and how many locations and if we if we look at all these different locations we see that there's Surprisingly high amount of indirect control-flow transfers with a surprisingly high amount of transfers There are targets that is being allowed by all these these different policies and We see that in the under the dynamic policy in many locations only very few targets are allowed and I would like to remind you that this is a logarithmic scale So the higher up you go at the the more transfers are allowed Let me just point out a couple of interesting things for IFCC in many locations The IFCC collapsed all the equivalence classes into a single set So all the possible locations all the indirect control-flow transfers are allowed to use the same set of targets Which basically implements a weak form of of CFI that allows you to reuse all the possible targets And this is likely the reason why IFCC was removed later on Also, we see that for lockdown the spread is usually fairly big So there's an outlier out there that has a very high amount of transfer That are allowed for a specific location and this is due to the Problem with the binary analysis where we require additional symbols and information about the libraries And there's usually one library out there that does not provide symbols and then a large amount of transfers are possible due to Limitations of the binary analysis, so you cannot recover all the information about all the possible targets in in the code There's also MCFI and PI-CFI and we see that for most programs They actually provide roughly the same amount of precision But PI-CFI which is per-input CFI sometimes restricts the amount of transfers that are allowed due to an additional runtime analysis that they do There's also the new LLVM-CFI which is the Basically the second version of IFCC that Google recently released and will be part of newer upcoming LLVM releases and it looks like as if it's much closer to the Limit or the minimum amount of transfers that are being allowed than the old version and it gives you a much tighter security bound So let's look at a summary of all these different CFI mechanisms and in the graph before we've only looked at the forward edge So how good is the precision for indirect calls and indirect jumps? but there's also the backward edge and we've talked at the beginning of Of this talk a little bit about the backward edge and the importance of protecting the backward edge for basic control flow bending and as we see the first three CFI mechanisms IFCC Microsoft control flow guard and LLVM-CFI basically have no protection on the backward edge except the already existing protections out there and this basically Makes control flow bending trivially possible and allows you to reconnect and stitch the control flow in any possible way You would want to do for MCFI and PI-CFI There is some protection on the on the backward edge and it makes control flow bending attacks Harder and we have to find these dispatcher functions and these shortcuts in this control flow graph for lock down the due to the Strong protection on the backward edge that it adds we have to resort to control flow bending on the forward edge Which makes the attack much harder, but due to the limits of the static Binary analysis and the other binary analysis we can still find possible targets So it looks like as if the set of protections that are being proposed and are being integrated in current systems are fairly limited they do make the attacks harder and As soon as we add stack integrity to the mix they will make the attack attacks much harder So if we do have stack integrity we can greatly increase the protection of current systems But as we've shown before many of the systems do not have stack integrity at all So let's just for a for a second assume that we do have stack integrity Then return oriented programming is no longer an option and attack becomes much harder the attacker now needs to find a path through the set of virtual calls that are out there and resort to some form of restricted co-op and the attack becomes really hard and Interpreter would actually makes make attacks much simpler assume that you have some piece of code that you can inject into the Executing image of the application that even under the constraints of full full control flow integrity still allows you to execute different code and You would be surprised by how many touring complete Interpreters there are and I would like to introduce you to printf oriented programming where we'll show that printf is fully touring complete We can translate any arbitrary program to a format string. We can do memory reads We can do memory writes and we can actually implement conditionals as well The program counter becomes the counter in the format string and we can just move the program counter along If you want to do loops we can actually overwrite this program counter or this format string counter as Different parts of the format the string are being printed to the screen And we can reset and jump around in this format string and then readjust our program counter to implement different jumps in this in this abstract language that we that we implement and We are presenting a touring complete domain specific language and to make things interesting for you I wanted to know have you ever heard of brain fuck? It's like this fun touring complete language that only has eight different symbols You can you can move a data data pointer forward. You can move a data pointer backward You can increase the value on the current cell You can decrease the value on the current cell you can write the character to the screen You can get a character and you can jump conditionally back and forth there's a format string statement for the Incrementing the data pointer decrementing the data pointer adding subtracting adding a character and even going back and forth depending on the Implementation of printf you can overwrite this format string The format string counter and reset and jump around in the in the format string Which is what makes it? Dependent on the lip see version that you currently use to make things a little bit easier. We assume that We can we can call printf in a loop So we basically either have to look at the current Implementation of printf in the lip see and overwrite this printf internal data values by using arbitrary memory writes Or we just have to find a loop that loops printf in the current program Which we can usually do by In in many of the programs that we looked at and we can we can break the brainfuck interpreter into a simple fetch execute and retire loop that keeps executing by just calling printf's one after the other So let me Present to you as a short demo on how we can translate arbitrary brainfuck programs into printf statements and then execute brainfuck by just doing a call to a single printf So I have a set of small brainfuck programs and Let's just start with the The Fibonacci thing this this is a small brainfuck program that just calculates Fibonacci programs Let's compile the the program and we do have a small The screen is a little bit small the I've shown the github page so you can download the programs Yourself and try them out So we do run the printf Fibonacci program in a single Printf statement and we are generating different forms of Fibonacci Fibonacci numbers and this is just a printf statement that we keep Executing and we keep executing this this brainfuck program to just generate numbers But there's another fun program that's There's another fun program ever heard of 99 bottles of beer. It's a little bit longer program So let's just Execute this this program see what it does and Interestingly, you see that it gets faster as you're drinking more and more beer as you're counting down the numbers and printing Out the values it gets much much much faster There's also a game of life in there that you can play around with there's a Mandelbrot fractal and Sierpinski triangle that you can print all part in a in a single printf statement So there's there's a lot of fun that you can do with this with this interpreter and It's open source. You can use it. You can download it. You can play around with it and It is available from today on so go around play with it play with the touring completeness and Let's see what happens So let me conclude low-level languages are here to stay and These low-level languages are full of potential and as I've just shown even with a single call to printf that can either be executed in a loop or By looking at the printf internal implementation you can wrap around and show the full potential of these low-level programming languages and weird machines are hiding everywhere out there and with a single memory corruption So if you do have either a format string vulnerability or a memory corruption that allows you to control the first Arguments to printf you can get a touring complete interpreter that allows you to inject your own little program Without stack integrity the defenses that we will add are broken and we have to add stack integrity But even with stack integrity the attacker can still still do a lot of fun stuff and I encourage you to play around with with the code that we've added There's a simple Python script that allows you to translate the any brain fog program into Longer format string and a simple program the format strings are out there go around and have fun. Thank you Thank you, Matias. We have some minutes for questions if any so we've got some microphones here Any questions? So are you? Okay So go home play with with printer Okay, I'll be around Yes. Hello. Hello. It's working. Yeah, it's working. Okay So thank you a very good presentation. I have a small question you use person 10 in your printf Programming but in some implementations. It's disabled. Do you have any workarounds? There's only person and that gives you memory rights So the in any of the lip-sees that we've seen it's it's enabled So did there are obscure in fact in Microsoft visuals here I think it's disabled by default you have to call special function to enable it. Yeah Thank you, please leave or stay silent. Thank you So I was wondering I recently saw some discussion. Can you go closer to the microphone? Hello. Can you hear me? Yeah, so I recently saw some discussions about on the alvea mail list about something called safe stack Where they yeah, I'm one of the authors. Ah, okay. I was going to ask your opinion on it But it's good. It's great There's a we released an earlier version at OS DI So the current version that made it into LLVM is a little bit restricted and it's mostly geared as a debugging tool But the underlying idea is to Give you a tool that allows you to protect stack frames and give you stack integrity without any overhead So I think it's great It's a it's a it's a great tool and it should make it into LLVM without all the additional overheads Yeah, I think it's awesome. So thumbs up. Thanks Okay, thanks again, Matthias See you soon. So we're back here in 14 minutes for console hacking. Thank you