"One font vulnerability to rule them all" A story of cross-software ownage, shared codebases and advanced exploitation
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Jurczyk, Mateusz "j00ru"
"Font rasterization software is clearly among the most desirable attack vectors of all time, due to multiple reasons: the wide variety of font file formats, their significant structural and logical complexity, typical programming language of choice (C/C++), average age of the code, ease of exploit delivery and internal scripting capabilities provided by the most commonly used formats (TrueType and OpenType). As every modern widespread browser, document viewer and operating system is exposed to processing external, potentially untrusted fonts, this area of security has a long history of research. As a result, nearly every major vendor releases font-related security advisories several times a year, yet we can still hear news about more 0-days floating in the wild. Over the course of the last few months, we performed a detailed security audit of the implementation of OpenType font handling present in popular libraries, client-side applications and operating systems, which appears to have received much less attention in comparison to e.g. TrueType. During that time, we discovered a number of critical vulnerabilities, which could be used to achieve 100% reliable arbitrary code execution, bypassing all currently deployed exploit mitigations such as ASLR, DEP or SSP. More interestingly, a number of those vulnerabilities were found to be common across various products, enabling an attacker to create chains of exploits consisting of a very limited number of distinct security bugs. In this presentation, we will outline the current state of the art with regards to font security research, followed by an in-depth analysis of the root cause and reliable exploitation process of a number of recently discovered vulnerabilities, including several full exploit chains. In particular, we will demonstrate how a universal PDF file could be crafted to fully compromise the security of a Windows 8.1 x86/x64 operating system via just a single vulnerability found in both Adobe Reader and the Adobe Type Manager Font Driver used by the Windows kernel."
 Okay, thank you. So welcome to my talk. Today we will be talking about fonts again. This is the second font stock at recon. But it's not about GTF, but it's about post-tripped fonts and specifically about the research that I performed regarding post-tripped font security. So about myself, I also work on project zero, similar to Steve. I am passionate about low-level software security, as you will see. I also play CDFs. I am the vice captain of Dragon Sector. And I have a blog and Twitter. I don't post there very often. But I will definitely put the slides and some additional details about the research, which is by the way quite extensive. So in this talk, I am only actually talking about the exploitation of one epic bug that I found. But there are more bugs. And I think all of them are quite interesting. So I will be following up with some additional information about all of those findings later on. So if you are interested in the stock and the details, then be sure to check these things. So today we will talk about, first we will talk about the general structure of type one and open type fonts. Then we will talk about what Adobe type manager font driver is in the Windows kernel and what other code bases also share the same code, because this is actually going to be quite an important part of the talk. And then we will talk about exploitation of one specific vulnerability, which has two CVs assigned to it, but it is actually one bug. First we are going to own Adobe reader 11 on Windows 8.1 30-bit. And then we are going to do the same thing on 64-bits with the help of another vulnerability. And then I will provide some final thoughts. So let's start with a very short primer of post script fonts. Basically, they now have over 30 years. Adobe introduced them in 1984. Actually, they introduced two types of post script fonts, type one and type three fonts. The first type makes it only possible to use a subset of the post script functionality and type three fonts can use all of it. And type one fonts are actually the common formats that we are going to talk about. And they were all originally closed formats. Adobe only released a specification after a few years after Apple started working on a competition format called TrueType. So the general structure of the type one fonts is very simple. It's basically like a post script file containing dictionaries and primitive types, such as integers, numbers, real numbers, and some other stuff. And they also have some nested dictionaries. And overall, it just basically specifies it's just several numbers specifying the properties of the font. And apart from this, in type one fonts, you also have something called char strings. This is going to be a word that I'm going to be using a lot today. And char strings are basically like post script programs that are used to actually draw all of the outlines of a glyph. So here you can see an example of char string, which consists of immediate values that are pushed on the post script stack and also some operations that are being performed using those values. So overall, this char string draws the add character in one of the fonts that I took. So overall, the execution context of char strings in type one consists of three main parts. We have the instruction stream, which is basically like a buffer with all of the instructions that are encoded inside of the type one file. Then we have something called the operant stack, which is basically the stack that we also have in post script. In type one, it is able to store up to 24 numbers, which are 32 bits. And interestingly, char strings instructions actually take different parts of those values. So some of those use the full 32 bit and some of those use only 16 bits of those. And we also have something called the transient array, which is also going to be very useful to us today. And it's just a helper value that is not a stack. It's just an addressable array that we can control to some degree. So we can control the length of it using a numeric value inside of the dictionary. And we also can initialize it using a built array in the private dictionary of the font. So it's not really documented anywhere that I know of, but still most interpreters implement it. So this means that probably some font started using it and then the projects were forced to implement it. So when it comes to the operators themselves, so the instructions, we have several types of instructions. They are mostly about just drawing the glyphs. But there are also some arithmetic commands and subroutine commands because there are subroutines in type one. And also we can just push values to the stack using the byte range 32 to 255, which varies in the length of the instruction in order to be able to encode the full 32 bit range. So here we can see a short excerpt of the operators that are available in type one fonts taken from the official specification. So there isn't too many of them. As you can see, most of them are related to just drawing lines and other stuff like that. We have one interesting instruction which is called diff. So this is the arithmetic instruction. And yeah, in general, we have an escape instruction which can encode some more interesting ones that didn't fit into the original encoding. And as you could see, some of those ideas were missing. And it wasn't really, it's kind of weird to look at this list, but the reason for this is that basically between the time that the specification was released and today it was actually evolving very dynamically. So the specification changed, introducing some operands and operations and removing some others. So that is basically the reason for this. And another interesting property of type one fonts is they have to be, there have to be at least two files in order to load them at least in the Windows system. So we need a PFB file which is basically the core of the font. And we have the PFM file which is the matrix file. And there is also a third file that is optional which is related to multiple master fonts. So just a brief introduction into multiple master fonts. It's basically an extension of type one fonts that Adobe introduced several days, several years after type one fonts. And it just makes it possible to specify two or more masters that you can interpolate between in fonts to be able to get different results and not be dependent on the specific shape of the glyph, but have it more dynamic. So this is an example of how this would seem. But yeah, multiple master fonts were only supported in Adobe type manager released in 1990. And it was the first program by the way to properly also rasterize type one fonts which is kind of obvious because Adobe was the inventor of the format. And multiple master fonts weren't really adopted worldwide. Partially due to the advent of open type. So there are only a couple of multiple master fonts existing in the world anywhere. Mostly created by Adobe itself. However, the interesting thing is that they are supported by Microsoft GDI. So ATMFD in the kernel and also Adobe reader. When it comes to open type, it was released by both Microsoft and Adobe which works together to create something that would supersede both through type and type one fonts. And it's basically more of the same. There are several major differences such as there's only one file required instead of two in order to have the font loaded. Previously textual data such as the dictionaries that were at the beginning of the file were converted to compact binary form. So they consume less space. And the charge stream specification which is the most important part was significantly extended. So they introduced a lot, lot, lot new instructions and also deprecated or removed some of them. So here you can see the list of operations or operators from the open type specification. You can see that there are a lot more. So specifically they introduced a lot of new instructions for drawing glyphs and for hinting so that the fonts would be rendered nicely at small point sizes. But there is also several new instructions that are very interesting possibly from the exploitation standpoint because there are like new arithmetic and lodging instructions such as and or not absolute value, addition, subtraction and stuff like that. We also have some new instructions for managing the stack so we can duplicate the top stack value, we can exchange values on the stack, get a specific, get a value from a specific index on the stack and stuff like that. There are also some very weird instructions such as for example the random one which is supposed to insert a random number into the operand stack. I don't really know why a font would like to have any nondeterministic step inside of charge stream but it was in the documentation. And yeah, so that's mostly the interesting stuff. And if we also look at the open type specification at the very end of it, there is a very interesting table which specifies all of the limits for all of the structures that should be used for implementing open type and charge strings. So that is actually a very good starting point for vulnerability hunting so we can verify that all of those limits are actually followed by the implementation that we are looking into. So now let's look into the Adobe type manager software. Basically, it was created by Adobe to render type one fonts and it was ported to very early versions of Windows by patching into very low level of the operating system in order to provide kind of native support for it. But Windows and its architecture made it kind of impossible to do it anymore. So first Microsoft would allow converting type one fonts into true type during system installation and later on in Windows NT4. They added the ATM as a third party font driver called ATMD.dll and it's there until today. It's still in Windows 8.1 rasterizing our post script fonts. So why am I talking about all of this? Well, basically due to the vast collaboration of vendors during the early days of font development, if one vendor created an implementation for a specific format, they would often share it with the other vendors in order to make sure that it would be widely adopted. So that's also what happened with Adobe. So they licensed the code to Microsoft as ATMD and then Microsoft used the code in Windows GDI. So the Windows kernel used to does this to render the fonts. There is also the direct write library which is a user-land library which renders fonts and it is used in, for example, web browsers such as Internet Explorer, Chrome, Firefox, et cetera. There is also Windows presentation foundation which uses the code and there's obviously Adobe Reader by Adobe itself which also has the same common ancestor as all of those other products. And the important part is that if we find a single bug in the post script font implementation, OTF or type one, then it's very likely that this bug or some other bugs might be also common across the other products. So this is kind of scary from a security standpoint. But there is some good news. If you think about it, it's only the common ancestor of those different code bases. It's not really the same branch. So it's been living in different branches maintained by different groups of people since like many, many years. So it also received a very degree of attention from the security community because some people are only looking bugs in the Windows kernel and aren't probably checking if the same bugs apply to Adobe Reader, for example, because they are not interested in that. So the good news would be that they don't have to be really affected by the exact same set of bugs, right? But this is also bad news and I think it's more bad news than good news because you could actually take all of those different code bases and cross-div them and check what sanity checks exist in some of them and don't in the others and derive real zero days from that. So it's not a very good situation. So what I decided to do is to manually out it the TrimState machine that was implemented in Adobe type manager and see if I could find anything there and maybe something that would also reproduce in other software. So let's start with a quick primer into reverse engineering at the MFD. The first thing you see once you load it in IDA is that it doesn't have the symbols available from the Microsoft symbol server because it's basically not Microsoft Output code so they don't make the symbols available. We unfortunately have to stick with unnamed names, sub and address or whatever. So this is actually quite a bit of interesting information that we're missing. And I suspect that this might be a reason why it was less thoroughly audited compared to Win32k which has all of the symbols available because the entry bar is actually quite higher. But if we think about it and if we recall that there are shared code bases, maybe we can actually make an advantage of the fact that DirectWrite and Windows Presentation Foundation are shared the same code. So maybe we could see what the symbols are in those libraries and then in some way put them to work with ATMFD. And it's definitely possible. We can do it with some functions but there's another very interesting way which is something that Halvar Flake noticed. And the thing is that Adobe some long time ago actually released some builds of Reader with debug symbols enabled. It was Reader4 or AIX and Reader5 for Windows. So this also included the Fontengin cool type. And since the code, even though it was very, very old, it is very, very old right now, it hasn't really changed fundamentally. So there are many things that you can take from the old cool type, many symbols that you can actually just take and put into ATMFD and they will work because the functions are very similar. So yeah. This is basically like several function names that are quite useful from the old version of cool type. And there is also a bright side. There are some things in ATMFD that help us, reverse engineering it. First of all, there are a lot of debug messages. They are not really printed out. So there aren't any debug print function calls but there are some function calls that are actually like stops that could have been enabled and probably are enabled in some debug builds but they are not enabled in the release builds but the debug strings are still there. So we have things like variable names both local and global. We have some function names, the conditions that the ATMFD wanted to be met but haven't been met in case of some assertions. We also have some source file paths and stuff like that. So it's a lot of useful information. And there are also like type 1 font string literals. So the names of the fields that are expected in the dictionaries of type 1 fonts. You can see some of them here. There are really a lot of them and they are very useful. So if we wanted to find the single function, the interpreter that does all of the char string processing, it's really not a very difficult task because first of all it has a lot of cross references to debug messages which are directly related to char strings. As you can see here, for example, such as messages saying operand staggo underflow or argument counter at and the name of operator from char string. And second of all, if you look at the list of functions, the function is actually the largest one in the file. It has over 20 kilobytes and it's like five times larger than the second largest function in the file. So it's really enormous. It looks like this in IDA and I actually had to increase the maximum limit of nodes that IDA would be willing to render as a graph because 1,000 wasn't enough. So it's really huge. And if we want to have a confirmation that this is actually the right function, we can take a look at direct write or Windows presentation foundation and see that this, the name of the function is actually type 1 interpreter char string or in the symbolize school type, it's do type 1 interpreter char string. And what the function is, you could also see it in the graph before, is that it's a giant switch case statement handling all of those different instructions in line inside of this function. So it's quite simple in general. It's just, it basically reads a byte of the char string and then performs the switch case expression. And one other interesting or important thing is that the post script operand stack, which in open type is 48 elements long, is also on the local stack of the interpreter function. It's called OP SDK or OP operand stack. And this is the name that was used in debug messages from ATMFD. So I know for sure that it was like that in the source code. And we also have a pointer to that called OPSP, which at the beginning of the function obviously points at the beginning of the array. And the array is in the same context as the return address and the other stack frames and stuff like that. So the question is, like very important question is, why is the function so large, right? Because there aren't that many instructions. And so first of all, the function is responsible for both executing type 1 and type 2, which type 2 are open type char strings. So it is used for both of them. And this could be useful because type 1 fonts now have access to all of the open type instructions and vice versa. And the other thing is that ATMFD actually implements every single feature that was ever part of any of type 1 or type 1 or open type specifications. So even if the given feature was only there for like a year or two and it was long forgotten, ATMFD still supports it. So even the most obsolete, deprecated or forgotten ones. And yeah, so I was really enthusiastic when I learned about all of this because it's like basically the perfect starting point for a vulnerability researcher. And I just wanted to sum up the findings that I had during this research because I'm not going to be talking about all of them. So there were some quite low severity bugs that were just like doses in ATMFD or small memory disclosures. There was also very interesting memory disclosure that affected all of those code bases. So you could use the one bug to actually disclose memory from the heap of Internet Explorer and Adobe Reader and Microsoft Windows Kernel. So it's another one bug to rule them all. And there were also quite a few bugs that had high severity so they could lead to remote code execution or elevation of privileges in the Windows Kernel. And the three most important one were found in both Adobe Reader and the Windows Kernel, including the one that I am going to talk about. So the bottom most one. So yeah, let's look into the bug. Basically, it allowed remote code execution in Adobe Reader and elevation of privileges in the Windows Kernel or perhaps also remote code execution in the Windows Kernel as well. Unfortunately, it only affected 32-bit platforms, but as we will see, that might not really be a huge problem. And it was reproducible with just Type 1 fonts. But Adobe Reader and Windows Kernel supports Type 1 fonts so that it's not a problem as well. And in order to understand the vulnerability, we have to look into the operator that it is in. It's in the blend operator. That's why the bug itself is called blend vulnerability by myself. And it's related to the forgotten multiple master fonts. The blend operator itself was introduced in 1998. And it was only there for like two years. Then it was deleted. But obviously, ATMFD still has support for it. And I'm not really going to be talking about the details of what it does on a logical level. But for us, it's important that it pops K times N arguments from the operand stack, where K is the number of master designs which is equivalent to the length of a wide vector table specified in the private dictionary. And N is a controlled signed 16-bit value which is loaded from the operand stack. So it pops a semi-controlled number of arguments from the stack and then pushes N values back. So if you look at the code, you can actually see that the interpreter or maybe the developer rather had a very good intention to verify that the specified number of arguments are present on the stack and that everything will go fine. So there is a check whether the stack pointer is within the bounds of the stack buffer itself and there is a check whether there is at least one item on the stack which is the N value that we're going to pop. It also checked whether there are enough items on the stack and if there is enough space to push their values onto the stack later on. And you could also see that what some of those conditions actually were inverbating because they were in the debug messages as well. But it turns out that even though they made a lot of effort to actually check all of those conditions, they missed one corner case which is a negative value of N. So a negative value of N passed all of those checks and it reached the doblend function which did three things which I described before. So it loaded the input parameters from the stack that did some computation called blending operation and pushed the resulting value back. So from a technical point of view, what happened is just OPSP is decremented by this expression. The times 4 obviously comes from the fact that 4 is the size of operands on the stack and yeah, it's all pretty trivial. So what happens for a negative N is that no actual popping or pushing takes place in the doblend function but the operands stack pointer is actually adjusted accordingly to the formula that I've shown. So with a controlled 16 bit N, we can actually arbitrarily control the stack pointer beyond the operands stack array. And it is a security boundary because normally the stack should always be within that local array. So it turns out we're quite lucky because even though we can do it, at the beginning of the main interpreter loop, there is a check whether OPSP is not smaller than OPSP stack and if it is, then basically execution is aborted. But there is no similar check for whether OPSP is larger than operands stack so whether it hasn't gone beyond it from the other side. And thanks to this, we can actually have this inconsistent state with the out of bounds pointer and still execute further instructions, which is very good. So what we can do with this is that if we set a very large and the maximum length of wide vector is 16, so if we set it to 16 values, then we can increase the operands stack by as much as almost two megabytes on the stack. And this is well beyond the stack area itself. So we could maybe point it to somewhere else such as like the heap or pool or executable images, etc. But if we set it, the wide vector to something to an array, which is length, which is of length two, then we can have very great granularity of the access of the control of the pointer. So if we just issue a two command minus X blend sequence, then we can set OPSP to any offset relative to operands stack with the granularity of four bytes. For example, if we look at the stack frame of the interpreter function, what happens is that if we have the operands stack and then the difference between the beginning of the operands stack array and the return address is 349 dWords, then if we push minus 349, then perform the blend operation, the pointer will go out of bounds, and then if we perform the exchange operation, exchanging the two top values on the stack, what we will do is we'll just flip the return address and the saved ABP value. And if we then issue an enter command, we will force the return from the interpreter function, and obviously everything will go bad. We will get a blue screen of death showing that the processor tried to execute non-executable memory, which is the stack. So this is obviously quite bad. But it gets even better because it turns out that we can use all of the supported operators from the char strings, such as addition, subtraction, the other arithmetic stuff. And this is pretty much sufficient to create a full ROP chain using just char strings itself. And yeah, so as a result, the bug enables the creation of 100% reliable char string on the exploit, which subverts basically all of modern exploit mitigations. Stack cook is obviously because you can control where on the stack you modify data, depth, ASLR, because you can just build a ROP chain for yourself, SMAP, et cetera. Both Adobe Reader and Windows kernel were affected, so this means that you can get RCE in Adobe Reader and then EOP in the Windows kernel using this single bug. Yeah, that's pretty interesting, I guess. But yeah, 64 bits is kind of a problem, as I said, because if you look at the code, then it turns out that the end times master design's expression is cast to unsident before it is added to the 64-bit pointer of operand stack. And then if you specify a negative end value, then the whole, the full expression will fail. So the if will execute and we will just be aborted. But the fact is that there are no 64-bits version of Adobe Reader to my current knowledge, so we can still own all of Adobe Reader. And for 64-bit Windows kernel, we have still some other font bugs that we can exploit to get EOP. So some actual exploitation. Okay. So the overall goal would be to prepare a PDF which pops out calculator in the latest Adobe Reader that is affected by the vulnerability on Windows 8.1 for both 32-bit and 64-bit versions of Windows, make it 100% reliable, achieve high integrity level, obviously, or anti-authority system security, token for the calculators of this means that we want the full system compromise, subverting all available exploit mitigations because the vulnerability allows us to do this. And since there are now 64-bits of Adobe Reader, we can just create a single PDF that will exploit the vulnerability. And then inside of the second stage payload, we can differentiate between 32-bit and 64-bit kernels. And then we can just add and attack them accordingly. So let's start with the userland exploit. So it's really not that great, as I said before. So even though we can set the OPSP pointer well outside of the local stack array, not really all operators will work then. And specifically, all operators that actually increase OPSP check if it's still within bounds in order to follow the rule that the pointer should always point within the local array. So this, for example, makes it impossible to write constants in the normal way by just pushing values there because we will be aborted immediately. And some other instructions that us duplicate, pop, call, subroutine, random, etc., are forbidden as well. So here is an example. Basically, you can see that the first thing that the random implementation does is that it checks whether the OPSP is not larger than the end of the stack. And if it is, it just aborts. However, we're not really lost in this situation because there are some comments which write to the stack but do not increase the stack pointer because it also pops some values from the stack before that. And if that happens, so in case of those instructions, they actually don't check the OPSP pointer at all because they don't really have a reason to do it because if all of the instructions that do increase it check for the condition that, in theory, it should also be fine in cases of those instructions that don't increase it. So the lack of this safety net basically makes this vulnerability exploitable. And there aren't too many trust string instructions which makes as possible to write to the stack. But it's a few of them. So we have arithmetic instructions, addition, negation, division, blah, blah. We also have some other interesting ones such as get value from the stack and get value from the transit array and we can exchange values from the stack. So maybe we can try to do something with this. So, well, if we think about what we can do in this condition, we could maybe try to use the index instruction. That was my first thought because it replaces the top stack item with the one X items below the top where X is the original value from the stack. However, it couldn't really work because we don't really control the X under our operand stack pointer, right? And also, the arithmetic instructions don't really help us either because we don't control the original instructions and they do require controlled operands for us. But it's not really hopeless. There is this one other instruction that caught my attention which is get and it replaces the index with the value from the corresponding index from the transit array. So the first idea was that since the index is only 16 bits, maybe we could put the value that we want to put into the stack at a specific place into all of those transit array entries and, of course, make it sufficiently long so that it is full of those values and then we are sure that the index will work in the array and we will get the right value. But there are obviously some problems with this. First of all, over 65,000 of instructions just to put a single constant at a single specific place in the stack. It's not really very efficient. And the index is also a 16 bit value. So if the original value was a negative number that get would also bail out. But the absolute instruction maybe could fix this but I haven't checked. However, there is a much more smarter way to do it because we actually can control the value somehow which is basically directly under OPSP and this is the square root instruction. So what the square root instruction does is that it just puts a square root of the previous value on the stack in place of the old one. So the idea is that after five subsequent invocation of the instruction, whatever was originally on the stack, we end up with either zero if the original value was zero or one if the value was nonzero after five invocation of square root. This is kind of obvious. And then if we perform these operations and we use the get operation, that the operand for the get operation will either be zero or one. So let's see an example of how we could write some data to the stack. Let's say we want to put the value 31337. So we first put it on the operand stack, duplicate it, then put it into the transient array indexes zero and one using the put operations. Then we shift the operand stack pointer using the blend bug. So we put a minus 100 and then blend. Okay. Then we just execute five square root instructions so we can see that the original value is basically decreasing until the point where the highest 16 bits which are the argument for get become one and then we just issue the get instruction which fetches the data from the transient array. And we have the control value on the stack wherever we want. So the other thing that we would like to want to create a full ROP chain is read existing data from the stack because we cannot really create a ROP chain just based on inserting constants. So we have to perform some operations based on existing data from stack for example to create to calculate the addresses of ROP gadgets. So we can use a similar trick. We can just use the square root instruction to either also get a zero or one and then use the put instruction to put the value which is before the zero or the one into the transient array. And then if we pre-initialize the transient array, the first two entries to zeroes and then after we reach the value, we sum both entries, we will basically end up with the value that we want. And another thing that we would like to do is have the operand stack pointer reset back to the original address of the beginning of the operand stack so that we can perform further operations there. And it turns out that there is a set current point instruction which does exactly that with no side effects. So let's see another example. We basically just put a zero, duplicate it on the operand stack, then put the two zeroes inside of the transient array to allocate, sorry to initialize those two entries. Then we again shift the operand stack pointer, sum five square roots. Then we put the value that we want into the transient array. It will be index one because the original data was not zero. Then we reset the stack pointer. We get both transient array indexes zero and one into the operand stack with two get instructions. We then sum both of those numbers. And at this point, we have read our number, our value from the stack. So at this point, we can perform any operations we like. For example, we can subtract a specific value from that address in order to get the base address of some library that we want to use for rope chaining. And so this gives us all we need to actually create a reliable rope chain. And we can now think about what the rope chain could do to make it as elegant for us, make it as elegant as possible for us to continue the exploitation. So one thing would be to call a library with the path to the exploit PDF itself. Because the PDF magic doesn't really have to appear at the beginning of the file. It can be somewhere later on, I think in the first kilobyte or something. So we could just create a binary polyglot which would be both a PE and a PDF file. And the PE file would be a DLL that has the second stage code that we can execute. And Anzha Albertini has already done it as a proof of concept in 2012. However, there are two problems that we have to overcome. First of all, the problem is that there is no pointer to the path of our file on the threads stack. So we cannot just copy the address of the string and provide it as parameter to load library. And the other thing is that for whatever reason, I don't know really what it is, is that Adobe Reader recently began rejecting PDF files that start specifically with the MZ signature. So that won't work so easily. So we have to settle for a less elegant solution that is quite standard. So we would like basically to call virtual protect over stack in order to make it both readable, writable and executable. And then also put the first stage payload there. And that's it. And then have it executed. And the first frame of the Rop is just cool type internal implementation of the get proc address which takes a value of a function inside of kernel 3332 and then just jumps to it directly. So it's pretty simple. It's just several d-words of the Rop. And you can see here in the debugger that it works. We have like arbitrary code execution inside of Adobe Reader from the stack. That is very good. Okay. So even though that works, I wasn't really convinced to writing a second stage font related Win32k exploit in assembly in order to attack ATMFD. It's definitely possible, but it's a bit of a pain, I guess. So I still wanted to have a control DLL loaded via load library after all so that I can write the second stage payload in C++ preferably. And to our advantage, first of all, the renderer process at the time of the exploitation holds an active handle to the exploit PDF file with read access. And secondly, even though the rendered, the sandbox process is quite limited when it comes to writing capabilities to the file system, it can write to a temporary directory in update add of your CROBAT 11. So the idea would be to compile a second stage DLL with the exploit PDF file specified in the tab linked linker option in Visual Studio in order to create the polyglot. Then replace, of course, the two first magic bytes with something else, for example, small emz letters. That way we create the PDFP polyglot. And then in the assembly payload, what I decided to do would be to iterate over all possible handle values within some sensible range, and get the name of each of the objects of the handles and see if it ends with.pdf if it is. Then I just assume it's our PDF file because it's the only one currently handled by the renderer process. And I would write back the original emz signature after copying the file into the temp temporary directory and then invoke load library over that file within the temporary directory. And you can see that after I did it, it worked pretty well. I could actually write C++ second stage payload and it would show as a message box inside of Adobe Reader. So I could now write C++ code that could elevate privileges pretty conveniently. So as I said before, we can only, we can have a single second stage DLL because there is only one bit of Adobe Reader. And it can basically exploit both 32 and 64 bit kernels. We must only recognize the underlying system using a single API function and then we can drive exploitation accordingly. In both cases, we have to create a window and then the only difference is in the window procedure. So about rendering the font because in order to attack ATMFD to trigger the vulnerability, you actually have to render the font or at least try to get it rendered. So this requires several API calls to achieve. So that's creating the window and then loading the exploit font into the system, creating a handle to the font, selecting it and then finally drawing some text using that font. And turns out that all of them actually work fine inside of the sandbox because Win32K access is not locked in Adobe Reader except one function and yeah, the most important one, loading the font. So what I saw is that Win32K doesn't really want to load any fonts via the function, which by the way takes the file name of the font that we want to load under the Adobe Reader sandbox. So that's a bit of a problem. And if we look at the documentation, we can see that there is another function for loading fonts in Windows. It's called add font memory source x, which installs fonts directly from memory. So that would be convenient because I assume the problem was and it actually was in file system access. However, it didn't really provide any means of loading a font consisting of two files. So it could very well load a TTF file or an OTF file, but we needed to load a type one font and we couldn't really do it with this function and people on the Internet have also been wondering and they didn't find the solution. And I also confirmed that it was actually a problem by reverse engineering Win32K. And there are no other official or documented functions that we could use to actually load type one fonts. But obviously we are reverse engineers, so we can open up Aida and see what our system calls there might be that reference the font loading code. And it turns out that there is one font related system call as well that is not documented anywhere called anti GDI add remote font to DC. And if you type that into Google, it will result in zero results basically, either officially or unofficially. And if we strip the anti GDI prefix and just Google for the rest, then we will see that there is one result which is the description of Microsoft's patent. So if we look at the description of the patent, it basically just says that the function can be used to load fonts from memory. Similarly to other functions. Doesn't really give us too much information. We have to dig by ourselves. And fortunately, it's not similar to the other function that I mentioned. It's not just raw buffer with font data, but it's font files that are preceded by a header that specifies what the partitioning of the memory is and where in memory each of the files that we want to load is. So I had to reverse engineer the structure and it contains like a number of fields that are pretty easy to figure out how to initialize them. So we just have to initialize the is type one font to one that will make Win32k to assume that we want to load a type one file. And we can just say that the number of files is zero because Win32k will anyway know that we need to load two files and then we specify the offsets of the files. And then after that, we just put the data of the files. And after we do this, I confirm that Win32k will successfully load the files, the type one font from memory, and we will reach all of the relevant atmfd code paths so we can actually attack atmfd. So one thing we have to consider is that where do we put all of those kernel exploit fonts? Because since we want to create a single PDF of Doom, which is just a single file that attacks everything and pops cocks everywhere, we have to embed a lot of information there. So we can also either put the kernel exploits in PE resources because it's a DLL or we can just append it at the end of the file. So this is the structure of my proof of concept file. You can just see all of the information that I mentioned before. So you have a polyglot of PDF and PE which exploits Adobe Reader. And then after that, we have some padding and the exploits for 30 to bit and 64 bit exploits of the Windows kernel. So let's write a kernel exploit. 30 to bit. Let's start with 30 to bit because there is this is the same vulnerability that we can use with Adobe Reader. So if you know a little bit about Windows kernel and how it stores objects in memory, you know that elevation of privileges is fairly easy. You have to just find like a process that you know that is very privileged and you have to copy the security token of the process and copy that inside of the structure that describes other processes that you want to elevate and it can be really easily implemented in a short snippet of 86 assembly. So our ROPS goal in ATMFD would be to first allocate writeable executable memory, copy the EOP shellcode there, jump to the shellcode and have it do its job and then what is also important, we have to cleanly recover from this whole condition because we don't really want the operating system to crash at that point. So it should be fairly easy overall because the trashing exploitation process is exactly the same as with Adobe Reader. The blend instruction works in the same way. What is also convenient is that the addresses of ATMFD.dll, Win3rd2k.sys and NTOS kernel are all on the stack so we can make use of all of ROP gadgets from all of those three modules. And starting with Windows 8, most kernel memory allocations are allocated from a pool that is non-executable so we cannot reuse some other pool allocation that was allocated from such pools. We have to create an allocation by ourselves specifying the non-paged pool flag which still allocates normal executable non-pageable memory that we can use to store the payload. So this is how the ATMFD ROP payload looks like. It's also very, very simple. So we just allocate some memory and then we adjust the registers like ESI, EDI and ECX to point to the right memory regions. We perform a copy operations to copy like 128 bytes or maybe some more into the new allocation and then we just jump into it. And of course we have to have the EOP payload on the stack as well to be able to copy it to the new allocation. And after we do this, we can see that it works as well, pretty well. We have the kernel mode execution in ATMFD. So what we have to do now is just write the EOP shell code itself and it's just about, as I said, just traversing several kernel mode structures and replacing some pointers. The only interesting thing here that I didn't mention is that we have to also, if we want to spawn calculator, we also have to change the active process limit assigned to the job to more than one because otherwise we will not be able to create another process within the job. So yeah, so since we haven't spawned our calculator yet because we are not able to do it, I decided that my shell code is just going to elevate the privileges of the Adobe Reader 30-bit process and then after we have this, we can do anything from our second stage DLL. And then at the end, we are just jumping to other zero. So yeah, this is not really obvious why that would work because it shouldn't really recover cleanly, but it turns it does because ATMFD provides a rather performance very aggressive exception handling and it handles all invalid user mode memory references and just ignores them as if nothing happened. So it's actually not very nice for people that are fuzzing, for example, open type fonts because then if there was ever an exception that resulted in invalid user mode memory, the fuzzer would never know about it. But on the other hand, it's quite convenient for us here because we can just jump to other zero and ATMFD will just take care of the rest and return to user mode as if nothing happened. So we can see that it worked as well. We can see that the payload once executed actually elevated the privileges of the two processes of Adobe Reader. And the final step would be just to pop up a calculator using create process. But after several minutes of trying to make this happen, I realized that Adobe Reader hooks the current base create process a function. So we just have to restore it to make it work. And yeah, this is very simple. We just put the original five byte function prologue inside of the memory. So I have a live demo that I would like to show you. I have two virtual machines prepared. The resolution is pretty low, but I think it should work anyway. So we have the Poc.pdf file. Let's first see that the version of Adobe Reader is what we want. It's working really slowly. But yeah, it's 11, 0, 10. And we can also see that the operating system is Windows 8. Well, it's kind of obvious. So we can just open the file. Okay, so the calculator popped up. And then we can also see that it has elevated privileges. Maybe I will do a magnifier to make it better visible. So there is Calc. That is, yeah, it's not working very nice, but you can see that calculator has elevated privileges. So yeah, that's the 32-bit full system compromise using one vulnerability. Okay, so I think I still have a little bit of time so I can discuss the 64-bit exploitation of Windows kernel. So obviously, as I said before, we cannot make use of the previous bug because it doesn't exist or rather it's mitigated by how the code was written. So we have to use some other bug. And I had three options when thinking about it. There is a write-with-wear which works via an uninshiced pointer from the kernel pools. There was a control pool-based buffer overflow and a limited pool-based buffer underflow. So I thought that the first vulnerability would actually provide the best primitives for us to perform the exploitation itself. So let's learn about the vulnerability. It also makes it possible to do elevation of privileges as I will show. And it reproduces on both architectures and with type 1 and open type phones. So in order to understand the vulnerability, we also have to look back at a very old specification from 1998 where the registry object was defined. So a registry object is also related to multiple masters which by the way were being tried. We introduced to the open type format for a while but since it failed, they dropped the idea. And it was also subsequently removed from the specification in 2000 with all of the other multiple master related things but of course, ATMFD supports it. So we have to learn about two new instructions in charge strings called store and load. And these two instructions basically copy memory between the registry object and the transient array. So the registry object is just an array of say three pointers and these pointers hold the addresses of some allocations. So we can address the registry object using index 0, 1 or 2 as set in the specification. And yeah, this is the nice part. So it says that if we try to do an out of bounds index, the result is undefined which is also always a good sign in a specification because they might have got it wrong. And internally, the registry items are basically stored inside of a, they are stored as a three item array of registry item structures. We just store the size of the item and pointer to the item. And the verification of the registry index does of course exist because the bugs are not that trivial. But can you spot the bug here in the listing? Obviously, they are checking with the index that we specified is larger than three but they really should specify, should check for whether it is larger or equal than three because the only valid indexes are 0, 1 and 2. So this is a one of, of by one vulnerability in accessing the registry array. And as a result, using the load and store operators, we can actually trigger the following mem copy calls. We have controls transit array and size. So we can either, we can either copy into the pointer that is specified by the third entry in the registry array or we can read from it. Of course, provided that the size of registry item three is larger than zero and we have to remember that the variable is of type signed. So the registry array is part of the overall, like, very large font state structure. And what is very convenient for us is that it is uninitialized during the interpreter runtime. So it holds whatever values were there when the structure was allocated. So this means that if we can spray the kernel pools such that we can control the whole structure, both the size and the data, then we can have, like, unlimited read and write capabilities in the Windows kernel inside of the char string program. So this is how we can, how we can reproduce this vulnerability provided that we have sprayed the kernel pool. It's just a matter of five instructions. Four of them are just providing arguments to restore instruction. So it's the registry index and then offsets within the transit array and the registry items, item and then the number of d words that we want to copy. After that, we just call the store instruction and, yeah, the vulnerability occurs. So if we think about how we can, how we can spray the pool, knowing that the allocation that we want to spray is within session page pool, we can look up some existing research and it turns out that Tarjee Mant was actually doing stuff like that back in 2011 for Windows 7. And what he did is he just called a single function called set class long pointer, which is responsible for setting a Unicode menu name of arbitrary length. So this causes Win32k to basically create an allocation of an arbitrary size and content, which should be a Unicode string. And it still works today in Windows 8.1. So I was experimenting for a little while trying to see what, how many calls would suffice to have this memory region sprayed in the right way. And I came up with this like nested loop to allocate, create allocations between a thousand and four thousand bytes for a hundred times. And then on all of my test machines, it would, the memory that we had to spray would be sprayed reliably with the data that we had provided. In this case, it would be a 01010101 value for the size and then an invalid kernel pointer for the data pointer. And after we have this and we spray the memory pool and actually invoke all of those instructions shown in the title that we can see that the memory is actually being referenced and we have a write operation here. So that was quite easy, I guess. We have now the, we now have a write and read and write with work condition. The question is what shall you read and write? Because we're on Windows 8.1 trying to subvert all exploit mitigations and as it turns out, Microsoft actually went into great lengths to make it impossible to get any information about the kernel address space from within user mode and specifically from within low integrity processes. And of course, we would like to use whatever resources we have right now and not burn another zero day on this. So there are still things that Windows doesn't prevent us from acquiring about the kernel address space. We have two instructions that are quite useful called as SIDT and SGDT, which just give us into user mode the addresses of the IDT and GDT CPU structures inside of the kernel address space. So they are available in user mode by default and they are really impossible to disable or restrict as an operating system without using virtualization technology. So they provide us a very convenient NTA, ASLR primitive in the world of Windows 8.1 kernel exploitation. So another interesting thing is that on CPU zero in Windows, the structure of GDT and IDT and how they are placed in memory is quite peculiar because we have GDT and for directly after that we have IDT, the first structure is of 80 hex bytes, the other one is of 1,000 bytes, storing 256 structures of size 16 bytes and then because of the fact that those two structures don't align with the size of the page of the small 4K page, we have a lot of unused memory after that. And of course IDT is responsible for storing function pointers. It's full of function pointers and some of those function pointers are user facing such as the CPU exception handlers which are the low entries of IDT. But if we thought about overwriting it, it's not really the safest choice because other processes may reference it as well. And the kernel may do it too, so something unexpected could happen but there are also some interrupts that are designed specifically for user mode usage such as the three shown below. One other problem that we might have to consider is that the function pointers are actually partitioned inside of the structure, so it's not just a 64-bit function pointer but it's actually spread inside of the structure. We could deal with this using the arithmetic instructions of the Truster program because, yeah, why not? It makes it possible for us to do this but we also could keep things simple and just use kind of a trampoline of the form jump register and we could find such a gadget within the same memory page as the function that we are overwriting and in that way we could just overwrite the low 16 bits of the address instead of the full one and this would be fully reliable. So the other important thing is that ADIDT has read, write and execute access on CPU zero. So it both has function pointers and is RWE so we couldn't imagine a better situation for us. We could just use the unused bytes after the ADIDT to store our shellcode. Of course we have to care about things such as that SIDT only provides 32 bits of IDTR in the compatibility mode that we are executing in so we have to switch temporarily to 64 bits in order to get the address. We can do it using this simple Macros in Visual Studio. I'm not going to delve into this but it's pretty straightforward to get the IDTR registry. So what we should do in the second stage DLL is just make sure that we are running on CPU zero using a single API function that spray the session page spool as I shown before and just load the kernel exploit font and let it do its work. And inside of the font char string, inside of the loaded font, we first copy the entire IDTR into the transient array then we adjust entry 29 which is the QRIS security check failure function so that it points instead of the function it points to a jump R11 gadget which resides in the same memory page and then write that back to the IDT. So yeah, I just wanted to make it a little ironic that we are using the security interrupt to elevate our privileges. And then we just save the modified part of the IDT entry somewhere after IDT and we write the kernel mode EOP shell code later on. So this is how it looks like during the execution of the char string. We have the IDT and we have the transient array. So first we copy all of IDT inside of the transient array then we make a backup of the IDT entry that we're overwriting. Later on we're just subtracting a value from the IDT entry inside of the transient array or maybe add some value to point it to the jump R11 gadget and after we have this we just put it inside of the IDT itself then put the X64 shell code into the transient array and copy it into the IDT or rather after the IDT inside of the unused memory region. And once this is done the only thing left for us to do is that we have to trigger the function pointer that we have overwritten with R11 set to the address of the shell code that we have put in kernel memory. And what the shell code does is the same thing as it did for 32-bit versions of Windows so it just elevates the privileges of the Adobe reader process and increases the active process limit. After that we also unhook create process A and spawn calculator. So I will show you the demo of this in my other virtual machine. Here I will just show you that this is 64-bit version of Windows. Yeah, as you can see here it is. Maybe I will run Magnifier as well. So we just double click on the Pock.PDF and yeah, okay there is another calculator and also let's see that it's elevated. Maybe not. But you can see that all of the Adobe reader processes and calculator are indeed elevated to anti-authority system. So we have our mission accomplished. We ended up with a single PDF file that attacks both Windows 32-bit and 64-bit, the latest one, Windows 8.1 and Adobe reader 11. And yeah, so we bypassed all of the mitigations that we wanted. Stack hook is by the design of the vulnerability ASLR because we only used addresses that we either leaked or requested from the CPU depth because we run all of the stages in executable memory, sandboxing with the vulnerability in the Windows kernel which was the same for 32-bit and a little bit different one for 64-bit and SMAP also because we run the shell code from executable memory in the kernel address space. And we maintained complete reliability because there was no brute forcing or guessing involved. All stages were deterministic, maybe except for the full spraying one and 64-bit but I think it should be pretty reliable as well. So the final thoughts are that even though fonts received a lot of attention from the security community, apparently they are not dead and this was shown by this presentation and the previous one as well, I'd rather say that there seem to be more of them being found each year. So there are also some more bugs from me coming out and some blog posts detailing the details of the other ones that I mentioned today. And it's also quite doubtful that they ever will completely cease to exist. So it's a good thing that some companies are actually putting some mitigations or some design changes in place to make those bugs unexploitable or just not worth exploiting because they are not in privileged contexts. And yeah, we should also be aware of the fact that shared native code bases still exist and this is obviously very scary in the context of software security and especially in the context of file formats that were developed 20 or 30 years ago and the code really hasn't changed that much since then. So even in 2015, with all of those mitigations that we have, it turns out that it's still a matter of just a single good bug to get a full system compromise. And with that, I thank you very much and I'm happy to answer any questions. When you threw that bug the second time in the kernel, you jumped to zero and that was your safe return. Did you have a cool way to safe return in Reader? No, I didn't because there was no reason to do it in the proof of concept, right? Because yeah, it's not really important for me to make Adobe Reader responsive after the exploitation stage, but it could definitely be done because there are several functions up the stack frames that can be returned to. So it's not really a problem, but I didn't do it for the proof of concept. Okay, no more questions. Thank you.
 Okay, thank you. So welcome to my talk. Today we will be talking about fonts again. This  is the second font stock at recon. But it's not about GTF, but it's about post-tripped  fonts and specifically about the research that I performed regarding post-tripped font  security. So about myself, I also work on project zero, similar to Steve. I am passionate  about low-level software security, as you will see. I also play CDFs. I am the vice  captain of Dragon Sector. And I have a blog and Twitter. I don't post there very often.  But I will definitely put the slides and some additional details about the research, which  is by the way quite extensive. So in this talk, I am only actually talking about the  exploitation of one epic bug that I found. But there are more bugs. And I think all of  them are quite interesting. So I will be following up with some additional information about  all of those findings later on. So if you are interested in the stock and the details,  then be sure to check these things. So today we will talk about, first we will talk about  the general structure of type one and open type fonts. Then we will talk about what Adobe  type manager font driver is in the Windows kernel and what other code bases also share  the same code, because this is actually going to be quite an important part of the talk.  And then we will talk about exploitation of one specific vulnerability, which has two  CVs assigned to it, but it is actually one bug. First we are going to own Adobe reader  11 on Windows 8.1 30-bit. And then we are going to do the same thing on 64-bits with  the help of another vulnerability. And then I will provide some final thoughts.  So let's start with a very short primer of post script fonts. Basically, they now have  over 30 years. Adobe introduced them in 1984. Actually, they introduced two types of post  script fonts, type one and type three fonts. The first type makes it only possible to use  a subset of the post script functionality and type three fonts can use all of it. And  type one fonts are actually the common formats that we are going to talk about. And they  were all originally closed formats. Adobe only released a specification after a few  years after Apple started working on a competition format called TrueType. So the general structure  of the type one fonts is very simple. It's basically like a post script file containing  dictionaries and primitive types, such as integers, numbers, real numbers, and some  other stuff. And they also have some nested dictionaries. And overall, it just basically  specifies it's just several numbers specifying the properties of the font.  And apart from this, in type one fonts, you also have something called char strings. This  is going to be a word that I'm going to be using a lot today. And char strings are basically  like post script programs that are used to actually draw all of the outlines of a glyph.  So here you can see an example of char string, which consists of immediate values that are  pushed on the post script stack and also some operations that are being performed using  those values. So overall, this char string draws the add character in one of the fonts  that I took. So overall, the execution context of char strings in type one consists of three  main parts. We have the instruction stream, which is basically like a buffer with all  of the instructions that are encoded inside of the type one file. Then we have something  called the operant stack, which is basically the stack that we also have in post script.  In type one, it is able to store up to 24 numbers, which are 32 bits. And interestingly,  char strings instructions actually take different parts of those values. So some of those use  the full 32 bit and some of those use only 16 bits of those. And we also have something  called the transient array, which is also going to be very useful to us today. And it's  just a helper value that is not a stack. It's just an addressable array that we can control  to some degree. So we can control the length of it using a numeric value inside of the  dictionary. And we also can initialize it using a built array in the private dictionary  of the font. So it's not really documented anywhere that I know of, but still most interpreters  implement it. So this means that probably some font started using it and then the projects  were forced to implement it. So when it comes to the operators themselves, so the instructions,  we have several types of instructions. They are mostly about just drawing the glyphs.  But there are also some arithmetic commands and subroutine commands because there are  subroutines in type one. And also we can just push values to the stack using the byte range  32 to 255, which varies in the length of the instruction in order to be able to encode  the full 32 bit range. So here we can see a short excerpt of the operators that are  available in type one fonts taken from the official specification. So there isn't too  many of them. As you can see, most of them are related to just drawing lines and other  stuff like that. We have one interesting instruction which is called diff. So this is the arithmetic  instruction. And yeah, in general, we have an escape instruction which can encode some  more interesting ones that didn't fit into the original encoding. And as you could see,  some of those ideas were missing. And it wasn't really, it's kind of weird to look at this  list, but the reason for this is that basically between the time that the specification was  released and today it was actually evolving very dynamically. So the specification changed,  introducing some operands and operations and removing some others. So that is basically  the reason for this. And another interesting property of type one fonts is they have to  be, there have to be at least two files in order to load them at least in the Windows  system. So we need a PFB file which is basically the core of the font. And we have the PFM  file which is the matrix file. And there is also a third file that is optional which is  related to multiple master fonts. So just a brief introduction into multiple master  fonts. It's basically an extension of type one fonts that Adobe introduced several days,  several years after type one fonts. And it just makes it possible to specify two or more  masters that you can interpolate between in fonts to be able to get different results  and not be dependent on the specific shape of the glyph, but have it more dynamic. So  this is an example of how this would seem. But yeah, multiple master fonts were only  supported in Adobe type manager released in 1990. And it was the first program by the  way to properly also rasterize type one fonts which is kind of obvious because Adobe was  the inventor of the format. And multiple master fonts weren't really adopted worldwide.  Partially due to the advent of open type. So there are only a couple of multiple master  fonts existing in the world anywhere. Mostly created by Adobe itself. However, the interesting  thing is that they are supported by Microsoft GDI. So ATMFD in the kernel and also Adobe  reader. When it comes to open type, it was released by both Microsoft and Adobe which  works together to create something that would supersede both through type and type one fonts.  And it's basically more of the same. There are several major differences such as there's  only one file required instead of two in order to have the font loaded. Previously textual  data such as the dictionaries that were at the beginning of the file were converted to  compact binary form. So they consume less space. And the charge stream specification  which is the most important part was significantly extended. So they introduced a lot, lot, lot  new instructions and also deprecated or removed some of them. So here you can see the list  of operations or operators from the open type specification. You can see that there are  a lot more. So specifically they introduced a lot of new instructions for drawing glyphs  and for hinting so that the fonts would be rendered nicely at small point sizes. But  there is also several new instructions that are very interesting possibly from the exploitation  standpoint because there are like new arithmetic and lodging instructions such as and or not  absolute value, addition, subtraction and stuff like that. We also have some new instructions  for managing the stack so we can duplicate the top stack value, we can exchange values  on the stack, get a specific, get a value from a specific index on the stack and stuff  like that. There are also some very weird instructions such as for example the random  one which is supposed to insert a random number into the operand stack. I don't really know  why a font would like to have any nondeterministic step inside of charge stream but it was in  the documentation. And yeah, so that's mostly the interesting stuff. And if we also look  at the open type specification at the very end of it, there is a very interesting table  which specifies all of the limits for all of the structures that should be used for  implementing open type and charge strings. So that is actually a very good starting  point for vulnerability hunting so we can verify that all of those limits are actually  followed by the implementation that we are looking into. So now let's look into the Adobe  type manager software. Basically, it was created by Adobe to render type one fonts and it was  ported to very early versions of Windows by patching into very low level of the operating  system in order to provide kind of native support for it. But Windows and its architecture  made it kind of impossible to do it anymore. So first Microsoft would allow converting  type one fonts into true type during system installation and later on in Windows NT4.  They added the ATM as a third party font driver called ATMD.dll and it's there until today.  It's still in Windows 8.1 rasterizing our post script fonts. So why am I talking about  all of this? Well, basically due to the vast collaboration of vendors during the early days  of font development, if one vendor created an implementation for a specific format, they  would often share it with the other vendors in order to make sure that it would be widely  adopted. So that's also what happened with Adobe. So they licensed the code to Microsoft  as ATMD and then Microsoft used the code in Windows GDI. So the Windows kernel used to  does this to render the fonts. There is also the direct write library which is a user-land  library which renders fonts and it is used in, for example, web browsers such as Internet  Explorer, Chrome, Firefox, et cetera. There is also Windows presentation foundation which  uses the code and there's obviously Adobe Reader by Adobe itself which also has the  same common ancestor as all of those other products. And the important part is that if  we find a single bug in the post script font implementation, OTF or type one, then it's  very likely that this bug or some other bugs might be also common across the other products.  So this is kind of scary from a security standpoint. But there is some good news. If you think  about it, it's only the common ancestor of those different code bases. It's not really  the same branch. So it's been living in different branches maintained by different groups of  people since like many, many years. So it also received a very degree of attention from  the security community because some people are only looking bugs in the Windows kernel  and aren't probably checking if the same bugs apply to Adobe Reader, for example, because  they are not interested in that. So the good news would be that they don't have to be really  affected by the exact same set of bugs, right? But this is also bad news and I think it's  more bad news than good news because you could actually take all of those different code bases  and cross-div them and check what sanity checks exist in some of them and don't in the others  and derive real zero days from that. So it's not a very good situation.  So what I decided to do is to manually out it the TrimState machine that was implemented  in Adobe type manager and see if I could find anything there and maybe something that would  also reproduce in other software. So let's start with a quick primer into reverse engineering  at the MFD. The first thing you see once you load it in IDA is that it doesn't have the  symbols available from the Microsoft symbol server because it's basically not Microsoft  Output code so they don't make the symbols available. We unfortunately have to stick  with unnamed names, sub and address or whatever. So this is actually quite a bit of interesting  information that we're missing. And I suspect that this might be a reason why it was less  thoroughly audited compared to Win32k which has all of the symbols available because the  entry bar is actually quite higher. But if we think about it and if we recall that there  are shared code bases, maybe we can actually make an advantage of the fact that DirectWrite  and Windows Presentation Foundation are shared the same code. So maybe we could see what  the symbols are in those libraries and then in some way put them to work with ATMFD. And  it's definitely possible. We can do it with some functions but there's another very interesting  way which is something that Halvar Flake noticed. And the thing is that Adobe some long time  ago actually released some builds of Reader with debug symbols enabled. It was Reader4  or AIX and Reader5 for Windows. So this also included the Fontengin cool type. And since  the code, even though it was very, very old, it is very, very old right now, it hasn't  really changed fundamentally. So there are many things that you can take from the old  cool type, many symbols that you can actually just take and put into ATMFD and they will  work because the functions are very similar. So yeah. This is basically like several function  names that are quite useful from the old version of cool type. And there is also a bright side.  There are some things in ATMFD that help us, reverse engineering it. First of all, there  are a lot of debug messages. They are not really printed out. So there aren't any debug  print function calls but there are some function calls that are actually like stops that could  have been enabled and probably are enabled in some debug builds but they are not enabled  in the release builds but the debug strings are still there. So we have things like variable  names both local and global. We have some function names, the conditions that the ATMFD  wanted to be met but haven't been met in case of some assertions. We also have some source  file paths and stuff like that. So it's a lot of useful information. And there are also  like type 1 font string literals. So the names of the fields that are expected in the dictionaries  of type 1 fonts. You can see some of them here. There are really a lot of them and they  are very useful. So if we wanted to find the single function, the interpreter that does  all of the char string processing, it's really not a very difficult task because first of  all it has a lot of cross references to debug messages which are directly related to char  strings. As you can see here, for example, such as messages saying operand staggo underflow  or argument counter at and the name of operator from char string. And second of all, if you  look at the list of functions, the function is actually the largest one in the file. It  has over 20 kilobytes and it's like five times larger than the second largest function in  the file. So it's really enormous. It looks like this in IDA and I actually had to increase  the maximum limit of nodes that IDA would be willing to render as a graph because 1,000  wasn't enough. So it's really huge. And if we want to have a confirmation that this  is actually the right function, we can take a look at direct write or Windows presentation  foundation and see that this, the name of the function is actually type 1 interpreter  char string or in the symbolize school type, it's do type 1 interpreter char string. And  what the function is, you could also see it in the graph before, is that it's a giant  switch case statement handling all of those different instructions in line inside of this  function. So it's quite simple in general. It's just, it basically reads a byte of the  char string and then performs the switch case expression. And one other interesting or important  thing is that the post script operand stack, which in open type is 48 elements long, is  also on the local stack of the interpreter function. It's called OP SDK or OP operand  stack. And this is the name that was used in debug messages from ATMFD. So I know for  sure that it was like that in the source code. And we also have a pointer to that called  OPSP, which at the beginning of the function obviously points at the beginning of the array.  And the array is in the same context as the return address and the other stack frames  and stuff like that. So the question is, like very important question is, why is the function  so large, right? Because there aren't that many instructions. And so first of all, the  function is responsible for both executing type 1 and type 2, which type 2 are open type  char strings. So it is used for both of them. And this could be useful because type 1 fonts  now have access to all of the open type instructions and vice versa. And the other thing is that  ATMFD actually implements every single feature that was ever part of any of type 1 or type  1 or open type specifications. So even if the given feature was only there for like a year  or two and it was long forgotten, ATMFD still supports it. So even the most obsolete, deprecated  or forgotten ones. And yeah, so I was really enthusiastic when I learned about all of this  because it's like basically the perfect starting point for a vulnerability researcher. And  I just wanted to sum up the findings that I had during this research because I'm not  going to be talking about all of them. So there were some quite low severity bugs that  were just like doses in ATMFD or small memory disclosures. There was also very interesting  memory disclosure that affected all of those code bases. So you could use the one bug to  actually disclose memory from the heap of Internet Explorer and Adobe Reader and Microsoft  Windows Kernel. So it's another one bug to rule them all. And there were also quite a  few bugs that had high severity so they could lead to remote code execution or elevation  of privileges in the Windows Kernel. And the three most important one were found in both  Adobe Reader and the Windows Kernel, including the one that I am going to talk about. So the  bottom most one. So yeah, let's look into the bug. Basically, it allowed remote code  execution in Adobe Reader and elevation of privileges in the Windows Kernel or perhaps  also remote code execution in the Windows Kernel as well. Unfortunately, it only affected  32-bit platforms, but as we will see, that might not really be a huge problem. And it  was reproducible with just Type 1 fonts. But Adobe Reader and Windows Kernel supports  Type 1 fonts so that it's not a problem as well. And in order to understand the vulnerability,  we have to look into the operator that it is in. It's in the blend operator. That's  why the bug itself is called blend vulnerability by myself. And it's related to the forgotten  multiple master fonts. The blend operator itself was introduced in 1998. And it was only  there for like two years. Then it was deleted. But obviously, ATMFD still has support for  it. And I'm not really going to be talking about the details of what it does on a logical  level. But for us, it's important that it pops K times N arguments from the operand  stack, where K is the number of master designs which is equivalent to the length of a wide  vector table specified in the private dictionary. And N is a controlled signed 16-bit value  which is loaded from the operand stack. So it pops a semi-controlled number of arguments  from the stack and then pushes N values back. So if you look at the code, you can actually  see that the interpreter or maybe the developer rather had a very good intention to verify  that the specified number of arguments are present on the stack and that everything will  go fine. So there is a check whether the stack pointer is within the bounds of the stack  buffer itself and there is a check whether there is at least one item on the stack which  is the N value that we're going to pop. It also checked whether there are enough items  on the stack and if there is enough space to push their values onto the stack later  on. And you could also see that what some of those conditions actually were inverbating  because they were in the debug messages as well. But it turns out that even though they  made a lot of effort to actually check all of those conditions, they missed one corner  case which is a negative value of N. So a negative value of N passed all of those checks  and it reached the doblend function which did three things which I described before.  So it loaded the input parameters from the stack that did some computation called blending  operation and pushed the resulting value back. So from a technical point of view, what happened  is just OPSP is decremented by this expression. The times 4 obviously comes from the fact  that 4 is the size of operands on the stack and yeah, it's all pretty trivial. So what  happens for a negative N is that no actual popping or pushing takes place in the doblend  function but the operands stack pointer is actually adjusted accordingly to the formula  that I've shown. So with a controlled 16 bit N, we can actually arbitrarily control the  stack pointer beyond the operands stack array. And it is a security boundary because normally  the stack should always be within that local array.  So it turns out we're quite lucky because even though we can do it, at the beginning  of the main interpreter loop, there is a check whether OPSP is not smaller than OPSP stack  and if it is, then basically execution is aborted. But there is no similar check for  whether OPSP is larger than operands stack so whether it hasn't gone beyond it from the  other side. And thanks to this, we can actually have this inconsistent state with the out of  bounds pointer and still execute further instructions, which is very good.  So what we can do with this is that if we set a very large and the maximum length of  wide vector is 16, so if we set it to 16 values, then we can increase the operands stack by  as much as almost two megabytes on the stack. And this is well beyond the stack area itself.  So we could maybe point it to somewhere else such as like the heap or pool or executable  images, etc. But if we set it, the wide vector to something to an array, which is length,  which is of length two, then we can have very great granularity of the access of the control  of the pointer. So if we just issue a two command minus X blend sequence, then we can  set OPSP to any offset relative to operands stack with the granularity of four bytes.  For example, if we look at the stack frame of the interpreter function, what happens  is that if we have the operands stack and then the difference between the beginning  of the operands stack array and the return address is 349 dWords, then if we push minus  349, then perform the blend operation, the pointer will go out of bounds, and then if  we perform the exchange operation, exchanging the two top values on the stack, what we will  do is we'll just flip the return address and the saved ABP value. And if we then issue  an enter command, we will force the return from the interpreter function, and obviously  everything will go bad. We will get a blue screen of death showing that the processor  tried to execute non-executable memory, which is the stack. So this is obviously quite bad.  But it gets even better because it turns out that we can use all of the supported operators  from the char strings, such as addition, subtraction, the other arithmetic stuff. And this is pretty  much sufficient to create a full ROP chain using just char strings itself. And yeah,  so as a result, the bug enables the creation of 100% reliable char string on the exploit,  which subverts basically all of modern exploit mitigations. Stack cook is obviously because  you can control where on the stack you modify data, depth, ASLR, because you can just build  a ROP chain for yourself, SMAP, et cetera. Both Adobe Reader and Windows kernel were  affected, so this means that you can get RCE in Adobe Reader and then EOP in the Windows  kernel using this single bug. Yeah, that's pretty interesting, I guess. But yeah, 64  bits is kind of a problem, as I said, because if you look at the code, then it turns out  that the end times master design's expression is cast to unsident before it is added to  the 64-bit pointer of operand stack. And then if you specify a negative end value, then  the whole, the full expression will fail. So the if will execute and we will just be  aborted. But the fact is that there are no 64-bits version of Adobe Reader to my current  knowledge, so we can still own all of Adobe Reader. And for 64-bit Windows kernel, we  have still some other font bugs that we can exploit to get EOP. So some actual exploitation.  Okay. So the overall goal would be to prepare a PDF which pops out calculator in the latest  Adobe Reader that is affected by the vulnerability on Windows 8.1 for both 32-bit and 64-bit  versions of Windows, make it 100% reliable, achieve high integrity level, obviously, or  anti-authority system security, token for the calculators of this means that we want  the full system compromise, subverting all available exploit mitigations because the  vulnerability allows us to do this. And since there are now 64-bits of Adobe Reader, we  can just create a single PDF that will exploit the vulnerability. And then inside of the  second stage payload, we can differentiate between 32-bit and 64-bit kernels. And then  we can just add and attack them accordingly. So let's start with the userland exploit.  So it's really not that great, as I said before. So even though we can set the OPSP  pointer well outside of the local stack array, not really all operators will work then. And  specifically, all operators that actually increase OPSP check if it's still within bounds in  order to follow the rule that the pointer should always point within the local array.  So this, for example, makes it impossible to write constants in the normal way by just  pushing values there because we will be aborted immediately. And some other instructions that  us duplicate, pop, call, subroutine, random, etc., are forbidden as well. So here is an  example. Basically, you can see that the first thing that the random implementation does  is that it checks whether the OPSP is not larger than the end of the stack. And if it  is, it just aborts. However, we're not really lost in this situation because there are some  comments which write to the stack but do not increase the stack pointer because it also  pops some values from the stack before that. And if that happens, so in case of those instructions,  they actually don't check the OPSP pointer at all because they don't really have a reason  to do it because if all of the instructions that do increase it check for the condition  that, in theory, it should also be fine in cases of those instructions that don't increase  it. So the lack of this safety net basically makes this vulnerability exploitable. And  there aren't too many trust string instructions which makes as possible to write to the stack.  But it's a few of them. So we have arithmetic instructions, addition, negation, division,  blah, blah. We also have some other interesting ones such as get value from the stack and  get value from the transit array and we can exchange values from the stack. So maybe we  can try to do something with this. So, well, if we think about what we can do in this condition,  we could maybe try to use the index instruction. That was my first thought because it replaces  the top stack item with the one X items below the top where X is the original value from  the stack. However, it couldn't really work because we don't really control the X under  our operand stack pointer, right? And also, the arithmetic instructions don't really help  us either because we don't control the original instructions and they do require controlled  operands for us. But it's not really hopeless. There is this one other instruction that caught  my attention which is get and it replaces the index with the value from the corresponding  index from the transit array. So the first idea was that since the index is only 16 bits,  maybe we could put the value that we want to put into the stack at a specific place  into all of those transit array entries and, of course, make it sufficiently long so that  it is full of those values and then we are sure that the index will work in the array  and we will get the right value. But there are obviously some problems with this. First  of all, over 65,000 of instructions just to put a single constant at a single specific  place in the stack. It's not really very efficient. And the index is also a 16 bit value. So if  the original value was a negative number that get would also bail out. But the absolute  instruction maybe could fix this but I haven't checked. However, there is a much more smarter  way to do it because we actually can control the value somehow which is basically directly  under OPSP and this is the square root instruction. So what the square root instruction does is  that it just puts a square root of the previous value on the stack in place of the old one.  So the idea is that after five subsequent invocation of the instruction, whatever was originally  on the stack, we end up with either zero if the original value was zero or one if the  value was nonzero after five invocation of square root. This is kind of obvious. And  then if we perform these operations and we use the get operation, that the operand for  the get operation will either be zero or one. So let's see an example of how we could write  some data to the stack. Let's say we want to put the value 31337. So we first put it  on the operand stack, duplicate it, then put it into the transient array indexes zero and  one using the put operations. Then we shift the operand stack pointer using the blend  bug. So we put a minus 100 and then blend. Okay. Then we just execute five square root  instructions so we can see that the original value is basically decreasing until the point  where the highest 16 bits which are the argument for get become one and then we just issue  the get instruction which fetches the data from the transient array. And we have the  control value on the stack wherever we want. So the other thing that we would like to want  to create a full ROP chain is read existing data from the stack because we cannot really  create a ROP chain just based on inserting constants. So we have to perform some operations  based on existing data from stack for example to create to calculate the addresses of ROP  gadgets. So we can use a similar trick. We can just use the square root instruction to  either also get a zero or one and then use the put instruction to put the value which  is before the zero or the one into the transient array. And then if we pre-initialize the transient  array, the first two entries to zeroes and then after we reach the value, we sum both  entries, we will basically end up with the value that we want. And another thing that  we would like to do is have the operand stack pointer reset back to the original address  of the beginning of the operand stack so that we can perform further operations there. And  it turns out that there is a set current point instruction which does exactly that with no  side effects. So let's see another example. We basically just put a zero, duplicate it  on the operand stack, then put the two zeroes inside of the transient array to allocate,  sorry to initialize those two entries. Then we again shift the operand stack pointer,  sum five square roots. Then we put the value that we want into the transient array. It  will be index one because the original data was not zero. Then we reset the stack pointer.  We get both transient array indexes zero and one into the operand stack with two get instructions.  We then sum both of those numbers. And at this point, we have read our number, our value  from the stack. So at this point, we can perform any operations we like. For example, we can  subtract a specific value from that address in order to get the base address of some library  that we want to use for rope chaining. And so this gives us all we need to actually create  a reliable rope chain. And we can now think about what the rope chain could do to make  it as elegant for us, make it as elegant as possible for us to continue the exploitation.  So one thing would be to call a library with the path to the exploit PDF itself. Because  the PDF magic doesn't really have to appear at the beginning of the file. It can be somewhere  later on, I think in the first kilobyte or something. So we could just create a binary  polyglot which would be both a PE and a PDF file. And the PE file would be a DLL that  has the second stage code that we can execute. And Anzha Albertini has already done it as  a proof of concept in 2012. However, there are two problems that we have to overcome.  First of all, the problem is that there is no pointer to the path of our file on the  threads stack. So we cannot just copy the address of the string and provide it as parameter  to load library. And the other thing is that for whatever reason, I don't know really what  it is, is that Adobe Reader recently began rejecting PDF files that start specifically  with the MZ signature. So that won't work so easily. So we have to settle for a less  elegant solution that is quite standard. So we would like basically to call virtual  protect over stack in order to make it both readable, writable and executable. And then  also put the first stage payload there. And that's it. And then have it executed. And  the first frame of the Rop is just cool type internal implementation of the get proc address  which takes a value of a function inside of kernel 3332 and then just jumps to it directly.  So it's pretty simple. It's just several d-words of the Rop. And you can see here in the debugger  that it works. We have like arbitrary code execution inside of Adobe Reader from the  stack. That is very good. Okay. So even though that works, I wasn't really convinced to writing  a second stage font related Win32k exploit in assembly in order to attack ATMFD. It's  definitely possible, but it's a bit of a pain, I guess. So I still wanted to have a control  DLL loaded via load library after all so that I can write the second stage payload in C++  preferably. And to our advantage, first of all, the renderer process at the time of the  exploitation holds an active handle to the exploit PDF file with read access. And secondly,  even though the rendered, the sandbox process is quite limited when it comes to writing  capabilities to the file system, it can write to a temporary directory in update add of  your CROBAT 11. So the idea would be to compile a second stage DLL with the exploit PDF file  specified in the tab linked linker option in Visual Studio in order to create the polyglot.  Then replace, of course, the two first magic bytes with something else, for example, small  emz letters. That way we create the PDFP polyglot. And then in the assembly payload, what I decided  to do would be to iterate over all possible handle values within some sensible range,  and get the name of each of the objects of the handles and see if it ends with.pdf if  it is. Then I just assume it's our PDF file because it's the only one currently handled  by the renderer process. And I would write back the original emz signature after copying  the file into the temp temporary directory and then invoke load library over that file  within the temporary directory. And you can see that after I did it, it worked pretty  well. I could actually write C++ second stage payload and it would show as a message box  inside of Adobe Reader. So I could now write C++ code that could elevate privileges pretty  conveniently. So as I said before, we can only, we can have a single second stage DLL  because there is only one bit of Adobe Reader. And it can basically exploit both 32 and 64  bit kernels. We must only recognize the underlying system using a single API function and then  we can drive exploitation accordingly. In both cases, we have to create a window and  then the only difference is in the window procedure. So about rendering the font because  in order to attack ATMFD to trigger the vulnerability, you actually have to render the font or at  least try to get it rendered. So this requires several API calls to achieve. So that's creating  the window and then loading the exploit font into the system, creating a handle to the  font, selecting it and then finally drawing some text using that font. And turns out that  all of them actually work fine inside of the sandbox because Win32K access is not locked  in Adobe Reader except one function and yeah, the most important one, loading the font.  So what I saw is that Win32K doesn't really want to load any fonts via the function, which  by the way takes the file name of the font that we want to load under the Adobe Reader  sandbox. So that's a bit of a problem. And if we look at the documentation, we can see  that there is another function for loading fonts in Windows. It's called add font memory  source x, which installs fonts directly from memory. So that would be convenient because  I assume the problem was and it actually was in file system access. However, it didn't  really provide any means of loading a font consisting of two files. So it could very  well load a TTF file or an OTF file, but we needed to load a type one font and we couldn't  really do it with this function and people on the Internet have also been wondering  and they didn't find the solution. And I also confirmed that it was actually a problem by  reverse engineering Win32K. And there are no other official or documented functions that  we could use to actually load type one fonts. But obviously we are reverse engineers, so  we can open up Aida and see what our system calls there might be that reference the font  loading code. And it turns out that there is one font related system call as well that  is not documented anywhere called anti GDI add remote font to DC. And if you type that  into Google, it will result in zero results basically, either officially or unofficially.  And if we strip the anti GDI prefix and just Google for the rest, then we will see that  there is one result which is the description of Microsoft's patent. So if we look at the  description of the patent, it basically just says that the function can be used to load  fonts from memory. Similarly to other functions. Doesn't really give us too much information.  We have to dig by ourselves. And fortunately, it's not similar to the other function that  I mentioned. It's not just raw buffer with font data, but it's font files that are preceded  by a header that specifies what the partitioning of the memory is and where in memory each  of the files that we want to load is. So I had to reverse engineer the structure and  it contains like a number of fields that are pretty easy to figure out how to initialize  them. So we just have to initialize the is type one font to one that will make Win32k  to assume that we want to load a type one file. And we can just say that the number  of files is zero because Win32k will anyway know that we need to load two files and then  we specify the offsets of the files. And then after that, we just put the data of the files.  And after we do this, I confirm that Win32k will successfully load the files, the type  one font from memory, and we will reach all of the relevant atmfd code paths so we can  actually attack atmfd. So one thing we have to consider is that where do we put all of  those kernel exploit fonts? Because since we want to create a single PDF of Doom, which  is just a single file that attacks everything and pops cocks everywhere, we have to embed  a lot of information there. So we can also either put the kernel exploits in PE resources  because it's a DLL or we can just append it at the end of the file. So this is the structure  of my proof of concept file. You can just see all of the information that I mentioned  before. So you have a polyglot of PDF and PE which exploits Adobe Reader. And then after  that, we have some padding and the exploits for 30 to bit and 64 bit exploits of the Windows  kernel. So let's write a kernel exploit. 30 to bit. Let's start with 30 to bit because  there is this is the same vulnerability that we can use with Adobe Reader. So if you know  a little bit about Windows kernel and how it stores objects in memory, you know that  elevation of privileges is fairly easy. You have to just find like a process that you  know that is very privileged and you have to copy the security token of the process  and copy that inside of the structure that describes other processes that you want to  elevate and it can be really easily implemented in a short snippet of 86 assembly. So our  ROPS goal in ATMFD would be to first allocate writeable executable memory, copy the EOP  shellcode there, jump to the shellcode and have it do its job and then what is also important,  we have to cleanly recover from this whole condition because we don't really want the  operating system to crash at that point. So it should be fairly easy overall because  the trashing exploitation process is exactly the same as with Adobe Reader. The blend instruction  works in the same way. What is also convenient is that the addresses of ATMFD.dll, Win3rd2k.sys  and NTOS kernel are all on the stack so we can make use of all of ROP gadgets from all  of those three modules. And starting with Windows 8, most kernel memory allocations  are allocated from a pool that is non-executable so we cannot reuse some other pool allocation  that was allocated from such pools. We have to create an allocation by ourselves specifying  the non-paged pool flag which still allocates normal executable non-pageable memory that  we can use to store the payload. So this is how the ATMFD ROP payload looks like. It's  also very, very simple. So we just allocate some memory and then we adjust the registers  like ESI, EDI and ECX to point to the right memory regions. We perform a copy operations  to copy like 128 bytes or maybe some more into the new allocation and then we just jump  into it. And of course we have to have the EOP payload on the stack as well to be able  to copy it to the new allocation. And after we do this, we can see that it works as well,  pretty well. We have the kernel mode execution in ATMFD. So what we have to do now is just  write the EOP shell code itself and it's just about, as I said, just traversing several  kernel mode structures and replacing some pointers. The only interesting thing here  that I didn't mention is that we have to also, if we want to spawn calculator, we also have  to change the active process limit assigned to the job to more than one because otherwise  we will not be able to create another process within the job. So yeah, so since we haven't  spawned our calculator yet because we are not able to do it, I decided that my shell  code is just going to elevate the privileges of the Adobe Reader 30-bit process and then  after we have this, we can do anything from our second stage DLL. And then at the end,  we are just jumping to other zero. So yeah, this is not really obvious why that would  work because it shouldn't really recover cleanly, but it turns it does because ATMFD provides  a rather performance very aggressive exception handling and it handles all invalid user mode  memory references and just ignores them as if nothing happened. So it's actually not  very nice for people that are fuzzing, for example, open type fonts because then if there  was ever an exception that resulted in invalid user mode memory, the fuzzer would never know  about it. But on the other hand, it's quite convenient for us here because we can just  jump to other zero and ATMFD will just take care of the rest and return to user mode as  if nothing happened. So we can see that it worked as well. We can see that the payload  once executed actually elevated the privileges of the two processes of Adobe Reader. And  the final step would be just to pop up a calculator using create process. But after several minutes  of trying to make this happen, I realized that Adobe Reader hooks the current base create  process a function. So we just have to restore it to make it work. And yeah, this is very  simple. We just put the original five byte function prologue inside of the memory. So  I have a live demo that I would like to show you. I have two virtual machines prepared.  The resolution is pretty low, but I think it should work anyway. So we have the Poc.pdf  file. Let's first see that the version of Adobe Reader is what we want. It's working  really slowly. But yeah, it's 11, 0, 10. And we can also see that the operating system  is Windows 8. Well, it's kind of obvious. So we can just open the file. Okay, so the  calculator popped up. And then we can also see that it has elevated privileges. Maybe  I will do a magnifier to make it better visible. So there is Calc. That is, yeah, it's not  working very nice, but you can see that calculator has elevated privileges. So yeah, that's the  32-bit full system compromise using one vulnerability. Okay, so I think I still have a little bit  of time so I can discuss the 64-bit exploitation of Windows kernel. So obviously, as I said  before, we cannot make use of the previous bug because it doesn't exist or rather it's  mitigated by how the code was written. So we have to use some other bug. And I had three  options when thinking about it. There is a write-with-wear which works via an uninshiced  pointer from the kernel pools. There was a control pool-based buffer overflow and a limited  pool-based buffer underflow. So I thought that the first vulnerability would actually  provide the best primitives for us to perform the exploitation itself. So let's learn about  the vulnerability. It also makes it possible to do elevation of privileges as I will show.  And it reproduces on both architectures and with type 1 and open type phones. So in order  to understand the vulnerability, we also have to look back at a very old specification from  1998 where the registry object was defined. So a registry object is also related to multiple  masters which by the way were being tried. We introduced to the open type format for  a while but since it failed, they dropped the idea. And it was also subsequently removed  from the specification in 2000 with all of the other multiple master related things but  of course, ATMFD supports it. So we have to learn about two new instructions in charge  strings called store and load. And these two instructions basically copy memory between  the registry object and the transient array. So the registry object is just an array of  say three pointers and these pointers hold the addresses of some allocations. So we can  address the registry object using index 0, 1 or 2 as set in the specification. And yeah,  this is the nice part. So it says that if we try to do an out of bounds index, the result  is undefined which is also always a good sign in a specification because they might have  got it wrong. And internally, the registry items are basically stored inside of a, they  are stored as a three item array of registry item structures. We just store the size of  the item and pointer to the item. And the verification of the registry index does of  course exist because the bugs are not that trivial. But can you spot the bug here in  the listing? Obviously, they are checking with the index that we specified is larger  than three but they really should specify, should check for whether it is larger or equal  than three because the only valid indexes are 0, 1 and 2. So this is a one of, of by  one vulnerability in accessing the registry array. And as a result, using the load and  store operators, we can actually trigger the following mem copy calls. We have controls  transit array and size. So we can either, we can either copy into the pointer that is  specified by the third entry in the registry array or we can read from it. Of course, provided  that the size of registry item three is larger than zero and we have to remember that the  variable is of type signed. So the registry array is part of the overall, like, very large  font state structure. And what is very convenient for us is that it is uninitialized during  the interpreter runtime. So it holds whatever values were there when the structure was allocated.  So this means that if we can spray the kernel pools such that we can control the whole structure,  both the size and the data, then we can have, like, unlimited read and write capabilities  in the Windows kernel inside of the char string program. So this is how we can, how we can  reproduce this vulnerability provided that we have sprayed the kernel pool. It's just  a matter of five instructions. Four of them are just providing arguments to restore instruction.  So it's the registry index and then offsets within the transit array and the registry  items, item and then the number of d words that we want to copy. After that, we just  call the store instruction and, yeah, the vulnerability occurs. So if we think about  how we can, how we can spray the pool, knowing that the allocation that we want to spray  is within session page pool, we can look up some existing research and it turns out that  Tarjee Mant was actually doing stuff like that back in 2011 for Windows 7. And what  he did is he just called a single function called set class long pointer, which is responsible  for setting a Unicode menu name of arbitrary length. So this causes Win32k to basically  create an allocation of an arbitrary size and content, which should be a Unicode string.  And it still works today in Windows 8.1. So I was experimenting for a little while trying  to see what, how many calls would suffice to have this memory region sprayed in the right  way. And I came up with this like nested loop to allocate, create allocations between a  thousand and four thousand bytes for a hundred times. And then on all of my test machines,  it would, the memory that we had to spray would be sprayed reliably with the data that  we had provided. In this case, it would be a 01010101 value for the size and then an invalid  kernel pointer for the data pointer. And after we have this and we spray the memory pool  and actually invoke all of those instructions shown in the title that we can see that the  memory is actually being referenced and we have a write operation here. So that was quite  easy, I guess. We have now the, we now have a write and read and write with work condition.  The question is what shall you read and write? Because we're on Windows 8.1 trying to subvert  all exploit mitigations and as it turns out, Microsoft actually went into great lengths  to make it impossible to get any information about the kernel address space from within  user mode and specifically from within low integrity processes. And of course, we would  like to use whatever resources we have right now and not burn another zero day on this.  So there are still things that Windows doesn't prevent us from acquiring about the kernel  address space. We have two instructions that are quite useful called as SIDT and SGDT,  which just give us into user mode the addresses of the IDT and GDT CPU structures inside of  the kernel address space. So they are available in user mode by default and they are really  impossible to disable or restrict as an operating system without using virtualization technology.  So they provide us a very convenient NTA, ASLR primitive in the world of Windows 8.1 kernel  exploitation. So another interesting thing is that on CPU zero in Windows, the structure  of GDT and IDT and how they are placed in memory is quite peculiar because we have GDT  and for directly after that we have IDT, the first structure is of 80 hex bytes, the other  one is of 1,000 bytes, storing 256 structures of size 16 bytes and then because of the fact  that those two structures don't align with the size of the page of the small 4K page,  we have a lot of unused memory after that. And of course IDT is responsible for storing  function pointers. It's full of function pointers and some of those function pointers are user  facing such as the CPU exception handlers which are the low entries of IDT. But if we  thought about overwriting it, it's not really the safest choice because other processes  may reference it as well. And the kernel may do it too, so something unexpected could happen  but there are also some interrupts that are designed specifically for user mode usage such  as the three shown below. One other problem that we might have to consider is that the  function pointers are actually partitioned inside of the structure, so it's not just  a 64-bit function pointer but it's actually spread inside of the structure. We could deal  with this using the arithmetic instructions of the Truster program because, yeah, why  not? It makes it possible for us to do this but we also could keep things simple and just  use kind of a trampoline of the form jump register and we could find such a gadget within the  same memory page as the function that we are overwriting and in that way we could just  overwrite the low 16 bits of the address instead of the full one and this would be fully reliable.  So the other important thing is that ADIDT has read, write and execute access on CPU  zero. So it both has function pointers and is RWE so we couldn't imagine a better situation  for us. We could just use the unused bytes after the ADIDT to store our shellcode. Of  course we have to care about things such as that SIDT only provides 32 bits of IDTR in  the compatibility mode that we are executing in so we have to switch temporarily to 64  bits in order to get the address. We can do it using this simple Macros in Visual Studio.  I'm not going to delve into this but it's pretty straightforward to get the IDTR registry.  So what we should do in the second stage DLL is just make sure that we are running on CPU  zero using a single API function that spray the session page spool as I shown before and  just load the kernel exploit font and let it do its work. And inside of the font char string,  inside of the loaded font, we first copy the entire IDTR into the transient array then we  adjust entry 29 which is the QRIS security check failure function so that it points instead  of the function it points to a jump R11 gadget which resides in the same memory page and then  write that back to the IDT. So yeah, I just wanted to make it a little ironic that we are  using the security interrupt to elevate our privileges. And then we just save the modified  part of the IDT entry somewhere after IDT and we write the kernel mode EOP shell code later on.  So this is how it looks like during the execution of the char string. We have the IDT and we have  the transient array. So first we copy all of IDT inside of the transient array then we make a  backup of the IDT entry that we're overwriting. Later on we're just subtracting a value from  the IDT entry inside of the transient array or maybe add some value to point it to the jump  R11 gadget and after we have this we just put it inside of the IDT itself then put the X64 shell  code into the transient array and copy it into the IDT or rather after the IDT inside of the  unused memory region. And once this is done the only thing left for us to do is that we have to  trigger the function pointer that we have overwritten with R11 set to the address of the  shell code that we have put in kernel memory. And what the shell code does is the same thing as  it did for 32-bit versions of Windows so it just elevates the privileges of the Adobe reader  process and increases the active process limit. After that we also unhook create process A and  spawn calculator. So I will show you the demo of this in my other virtual machine.  Here I will just show you that this is 64-bit version of Windows. Yeah, as you can see here  it is. Maybe I will run Magnifier as well. So we just double click on the Pock.PDF and yeah,  okay there is another calculator and also let's see that it's elevated. Maybe not. But you can  see that all of the Adobe reader processes and calculator are indeed elevated to anti-authority  system. So we have our mission accomplished. We ended up with a single PDF file that attacks both  Windows 32-bit and 64-bit, the latest one, Windows 8.1 and Adobe reader 11. And yeah,  so we bypassed all of the mitigations that we wanted. Stack hook is by the design of the  vulnerability ASLR because we only used addresses that we either leaked or requested from the CPU  depth because we run all of the stages in executable memory, sandboxing with the vulnerability in  the Windows kernel which was the same for 32-bit and a little bit different one for 64-bit and  SMAP also because we run the shell code from executable memory in the kernel address space.  And we maintained complete reliability because there was no brute forcing or guessing involved.  All stages were deterministic, maybe except for the full spraying one and 64-bit but I think it  should be pretty reliable as well. So the final thoughts are that even though fonts received a  lot of attention from the security community, apparently they are not dead and this was  shown by this presentation and the previous one as well, I'd rather say that there seem to be  more of them being found each year. So there are also some more bugs from me coming out and some  blog posts detailing the details of the other ones that I mentioned today. And it's also quite  doubtful that they ever will completely cease to exist. So it's a good thing that some companies  are actually putting some mitigations or some design changes in place to make those bugs  unexploitable or just not worth exploiting because they are not in privileged contexts. And yeah,  we should also be aware of the fact that shared native code bases still exist and this is obviously  very scary in the context of software security and especially in the context of file formats  that were developed 20 or 30 years ago and the code really hasn't changed that much since then.  So even in 2015, with all of those mitigations that we have, it turns out that it's still a  matter of just a single good bug to get a full system compromise. And with that, I thank you very  much and I'm happy to answer any questions. When you threw that bug the second time in  the kernel, you jumped to zero and that was your safe return. Did you have a cool way to safe  return in Reader? No, I didn't because there was no reason to do it in the proof of concept,  right? Because yeah, it's not really important for me to make Adobe Reader responsive after the  exploitation stage, but it could definitely be done because there are several functions up the  stack frames that can be returned to. So it's not really a problem, but I didn't do it for the  proof of concept. Okay, no more questions. Thank you.
recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_0_239.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_240_335.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_336_383.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_384_587.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_588_767.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_768_911.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_912_8867.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_8868_9599.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_9600_10139.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_10140_11879.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_11880_13067.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_13068_16127.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_16128_19271.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_19272_19607.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_19608_21215.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_21216_22295.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_22296_23759.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_23760_23903.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_23904_24575.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_24576_25235.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_25236_28667.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_28668_30743.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_30744_33251.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_33252_33851.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_33852_35711.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_35712_36839.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_36840_38075.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_38076_46859.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_46860_49163.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_49164_50699.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_50700_53327.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_53328_55067.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_55068_55247.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_55248_56831.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_56832_58175.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_58176_59807.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_59808_62291.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_62292_65375.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_65376_66731.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_66732_68759.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_68760_72083.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72084_72251.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72252_72323.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72324_72671.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72672_72779.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72780_72887.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72888_72983.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_72984_73067.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_73068_73211.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_73212_73283.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_73284_73379.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_73380_73991.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_73992_75047.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_75048_78875.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_78876_82103.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_82104_83615.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_83616_84323.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_84324_86147.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_86148_87323.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_87324_88175.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_88176_88295.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_88296_88367.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_88368_89807.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_89808_90419.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_90420_91523.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_91524_91667.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_91668_91967.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_91968_92123.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_92124_92315.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_92316_92531.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_92532_92999.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_93000_93095.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_93096_96575.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_96576_96923.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_96924_98435.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_98436_98639.png recon2015-21-j00ru-One-font-vulnerability-to-rule-them-all_98640_98674.png