Hi everyone, welcome to our talk, Hacking the Supply Chain, about the Ripple 20 vulnerabilities that haunt hundreds of millions of critical devices. A little bit about us, JSOF. So JSOF is a software security consultancy, we do a lot of security research, penetration testing, we help companies with their secure development processes, as well as some training. My name is Shlomi Overman, I'm a co-founder at JSOF. Together with me today, we'll be speaking Moshe Kohl, a security researcher at JSOF, and the finder of the Ripple 20 vulnerabilities. And Ariel Schon, a security researcher at JSOF, that was also heavily involved in the research. We'll be talking about Ripple 20 in general today, explaining what it is and how it evolved. Then we'll be going into detail about one of the vulnerabilities, the vulnerability that we find the most interesting out of the Ripple 20 vulnerabilities. Short spoiler here, it's one CVE, but we'll actually be talking about several vulnerabilities hiding behind this one CVE. And then we'll be going in-depth into exploitation on a specific vulnerable device, with a specific configuration. So, what is Ripple 20? Ripple 20 is a series of 19 zero-day vulnerabilities in a TCPIP stack called TREC-TCPIP. We say 19 zero-day vulnerabilities, but it sort of depends how you count. There are quite a few more discrete bugs in these 19 vulnerabilities, and two of the vulnerabilities were reported anonymously at the same time as we reported, so it depends how you count. These vulnerabilities were amplified by the supply chain to affect hundreds of millions of devices in all kinds of verticals. Medical, a lot of critical devices, industrial control, enterprise, and a bunch of others. We'll explain how this came to happen. Out of the Ripple 20 vulnerabilities, four of the vulnerabilities are critical remote code execution vulnerabilities, and in addition, eight vulnerabilities, or eight CVEs, are medium to high-severity vulnerabilities, some of which could also potentially lead to remote code execution, pending further research, if anyone wants to go into that. The vulnerabilities affect hundreds of different sub-medium devices. From brand names, you all know. We currently have over 100 different vendors with suspected affected devices, meaning different vendors are looking into these devices to see whether they're affected or not. These include Fortune 500, Global 500 companies, such as those you see on the slide, as well as one-person specialty shops, and quite a large range and diverse range of vendors. You can see the Ripple 20 vulnerabilities in medical devices when you go to the hospital, in your home, in your company, when you turn on the light, when you turn on the water. Transportation devices are affected by Ripple 20. Pretty much everywhere you go, you'll see IoT devices in general, as well as specifically devices affected by the Ripple 20 vulnerabilities. Our current assumption, based on what we know, the suspected devices, as well as the confirmed devices, meaning the different vendors confirmed the devices are affected, we assume every medium to large organization in the US has at least one vulnerable device. Of course, some networks have many more devices, such as data centers or utility companies, but even any other kind of organization might have an affected printer, a hospital might have affected IV pumps, etc. How did this come to happen? So this happened because of the supply chain and security in the supply chain. Not in the sense that somebody put a backdoor into a component built in another country, but in the sense that a piece of code, a library, had a vulnerability, like any software has a vulnerability. And this piece of code was sold to an operating system. It was then sold onwards to a system on module, and the system on module embedded the operating system with our vulnerable library. And from this point, nobody even knows they're using this piece of code, this piece of TREC TCP IP stack. This system on module is then used in different devices, such as IV pumps, and the whole chain becomes vulnerable. Just imagine what happens if one of the companies along the way goes bankrupt or ceases operation, just how difficult it is to track down the vulnerable devices and how complex it is to fix and patch. And so you have a network of devices, a network of different vendors, each selling to each other to create final products, built like Lego from different pieces and different parts. And then when one part at the very beginning of the supply chain, strategically located at the beginning of the supply chain, gets affected by a vulnerability, different devices along the supply chain get affected. This is what happened with Ripple20, and this is why it affects so many devices. So, why did we choose to do this research on TREC TCP IP, and why do we think it's important for the security industry as a whole? Well, for starters, this aspect of the supply chain, of vulnerabilities existing in the supply chain, not as backdoors, but just as regular vulnerabilities, and traveling, rippling from device to device in this ripple effect, this is something that is mostly unexplored. It's been discussed, we've seen some examples, but Ripple20 is really a prime example of what happens when a vulnerability is located this deep in the supply chain and exists for so many years. So, there's one vulnerability, multiple products, huge impact, and this is quite the beginning of a discussion of what should be done to fix these types of vulnerabilities. Another interesting thing is, for us, it was, of course, a good attack surface, and going forward, there's the potential for zombie vulnerabilities. Zombie vulnerabilities, these type of, it's complicated vulnerabilities, we don't know if they're one days, we don't know if they're zero days, quite a few of the vendors we believe won't be patching. Either they don't know they have TREC, the devices can't be patched, companies went bankrupt, and so you have these walking dead vulnerabilities that are supposed to be patched, they've already been reported, but they still exist in the wild. So, this is quite an interesting phenomenon, both for us, as well as for the industry. TREC specifically was chosen because it's an extremely successful TCP IP stack, used widely in the embedded device world, in the IoT world, and it's been available for over 20 years. So, during those 20 years, or 20-something years, it spread throughout the supply chain from device to device, from vendor to vendor, and reached its current scope, current impact. One thing that's very interesting about TREC TCP IP, made our life quite challenging, and made the lives of network operators challenging, is that the TREC stack is extremely configurable. So, every instance of TREC is slightly different. It depends when the vendor stopped support of TREC, what version of TREC they're using. It also depends how they compiled TREC. Specific configuration options, specific features they bought or did not buy, how they're using, what memory manager they're using, etc. And so, every version of the TREC stack looks different, and the vulnerabilities change. So, some vulnerabilities affect different devices differently, some devices are affected by some of the vulnerabilities, some devices are affected by more extreme versions of the vulnerabilities, while the others are affected by lesser versions. So, very complex, very complex to understand. And, of course, at the very, very beginning of a long supply chain. So, TREC doesn't incorporate any other components, which is not something that we can say about any of the other devices affected, which all incorporate different components, including TREC. The research itself, because TREC is so configurable, we had to take a few data points. We used six different devices. We spent a different amount of time on each device in order to understand what we're dealing with, in order to find variations of the vulnerabilities, in order to understand whether certain vendors made changes of their own to the stack or to the compilation. And so we used six different devices with differing architectures in order to understand what's going on, where this stack is, how this stack is used. The research took approximately nine months, with changing, differing intensities throughout this time. Started September 19th, we reported the vulnerabilities June 20th, June 2020, of course. And then we discovered some things after the fact as well. For example, while trying to bypass our company firewall in order to see if the vulnerabilities route over the Internet, we discovered that because TREC TCPIP stack allows for encapsulation, you could also encapsulate these vulnerabilities attempting to bypass firewalls. So we were able to bypass our firewalls, and at least some of these vulnerabilities will route over the Internet. As with any research involved in IoT, especially with so many different devices, we saw some pretty strange architectures, pretty strange firmwares and firmware structures. The device we'll be talking about today has one of these unique architectures. We can't talk about everything in the scope of 45 minutes. Of course, these are very complex, detailed things. The exploitation of all the vulnerabilities is something that we won't be able to go into in depth. We did release two white papers with full details, as well as pseudocode and specific references to the devices and the memory configurations that we exploited. Feel free to look them up on our site if you want to go into further technical detail. A little bit about the vulnerability, or the CV, that we'll be talking about today. CV-2020-11901 is a critical vulnerability in the TREC DNS resolver component, so client-side DNS vulnerability. Once successfully exploited, it allows remote code execution, as we've demonstrated and as you'll see today. The reason we think it's so interesting and the reason we chose to talk about it, besides being remote code execution, is the fact that DNS traverses NAT boundaries. A device within your network will issue a DNS request which will potentially travel over the internet, allowing sophisticated high-end attackers to perform an attack from outside the network into the network itself, which is different from most vulnerabilities we see in TCP-IP stacks. Behind the scenes, four vulnerabilities. One, what we call an artifact. That's a bug that made it easier for us to exploit. And these vulnerabilities vary over time. So, along the years, TREC changed the stack and the vulnerabilities changed, so we see different versions of the vulnerabilities in different devices, depending on when they stopped using TREC. They also change between vendor configuration. One of the interesting things that we realized during this research, as well as during the disclosure process, we realized that the supply chain complexities are a real problem for information security, they're a real problem for the vendors, they're a real problem for the network operators, and they create a challenge for the whole industry. And we realized this is bound to happen again. We didn't research the whole TCP-IP stacks, there are other TCP-IP stacks, there are other pieces of code, libraries, that exist in the supply chains that have been lurking around for years. And so, this would also be the beginning of a conversation about what the vendors and what the network operators can do to both reduce the impact of vulnerabilities like this when they happen, using things like exploit mitigations, as well as how we perform such complex disclosures and how we make sure that, as vendors, how the vendors make sure that their suppliers are performing secure development, pen testing their code, etc. So, this is sort of a wake-up call to some of the vendors and some of the network operators that this will happen probably again and again, and this can be prevented, or at least the impact can be reduced, using the right techniques. Now, I'm going to hand over the microphone to Moshe Kol. We'll be talking about the different vulnerabilities, where they are in the code, and what they look like. Thanks, Shlomi. Hi, I'm Moshe. I'm a security researcher at JSOF, and I will talk about the vulnerabilities that comprise CVE-2020-11901, also known as the DNS bugs. So, first, we need to refresh our memory about the DNS protocol. So, DNS is a core internet protocol designed to map between domain names and IP addresses. It's a query-response protocol, client-server architecture. So, the client resolves the name by issuing a query to a DNS server. So, for example, if you browse to www.example.com, your browser issues a DNS query of type A to one of the configured DNS servers, and the servers look up the name and returns a response. In this case of type A, the value of the response is an IPv4 address. So, a little bit about the record types. So, DNS servers can return multiple answers in the same DNS response. An answer is specified as a resource record. Each resource record is associated with a name. We'll talk about the type and class fields shortly. The TTL field specifies the number of seconds. This record is valid. The value of the record is specified in the rdata field, whose length is specified in the rdlength field. So, both questions and answers have a type in the DNS protocol. Some of the common types include type A, which specifies an IPv4 address for the query domain. Type CNAME, which defines an alias, a canonical name for the query domain. This provides a level of indirection. And type MX, which defines the domain name of a mail server for the query domain. So, if, for example, you send an email for gmail.com, your mail client issues a DNS query for gmail.com, it gets back a domain name of a mail server for gmail.com. And because this is a name and not an IP address, this name needs to be resolved further into an IP address. So, the resolver issues a DNS query of type A in this case, in order to resolve the domain name of a mail server. In practice, most DNS servers simply hand in the IP address along with the name, but nonetheless, this functionality needs to be supported by the DNS resolvers. So, a little bit about the domain names encoding, the binary format. So, domain names are encoded as a sequence of labels. So, www is a label, example is a label. Each label is preceded by a length byte, specifying the number of characters this label occupies. And the domain name is terminated by a zero length byte. And according to the RFC, the maximum label length is 63. This will come up later. So, what the designers of the DNS protocols notice is that there is a lot of repetition inside the DNS packet itself. So, in an effort to reduce the size of the DNS messages, they employ a simple compression scheme. In this scheme, compression is achieved by replacing a sequence of labels with a pointer to prior occurrence of the same sequence. So, here you can see a sample DNS response packet. You can see gmail.com is specified as an offset 0xc from the start of the packet. And it so happens that gmail.com needs to be specified multiple times inside the packet. So, instead of specifying gmail.com literally in the packet, again, we simply use the compression feature. We point to the previous occurrence of gmail.com. So, if we want to write smtp.gmail.com, we need to write only the first label literally, smtp in this case, and the next two labels are specified using the compression offset. So, in the compression schemes, compression pointers are encoded in two bytes. The first byte begins with 11 as the most significant bit, and the other 14 bits specify an offset from the start of the header. So, as Slomi said, the vulnerabilities reside in the DNS resolver of TrackTCPAP, and we find them in the DNS parsing logic in the stack, specifically in a function called tf.dns.callback. Here you can see a snippet of pseudocode from this function, specifically the parsing logic handling MX resource records. So, what we can see here is that the function computes the length of the MX hostname. Based on that length, a buffer on the heap is allocated, and then the MX hostname is copied as ASCII into the just-allocated buffer. So, what we can see from this snippet is that tf.dns.label.to.ascii, the function that is responsible for the copy, is not aware of the length of the buffer being allocated. What it does is simply copies bytes from the encoded name until a null byte is reached. So, this means that if, for some reason, expand.label.length returns a length value which is too small, we will have a heap-based buffer overflow vulnerability. So, this motivates us to look further into expand.label.length and examine its operation. So, you can see a pseudocode of expand.label.length. What this function is basically doing is sums up all the length bytes while honoring compression. So, in more detail, it reads the current label length, then checks to see if there is compression or not. If there is no compression, which is the common case, it adds the current label length plus one to the total length variable, which is later returned from this function, and advance the input add. And if there is compression, it reads the compression offset, it computes a new label pointer based on that offset, and then it checks to see that the new label pointer points before the initial label pointer. So, this means we can only jump backwards from where we were, and the process continues from this new label pointer. So, as you can see, there is no bound checks on the packet buffer here, so this led us to the first vulnerability, a read-out-of-bounds vulnerability. This could result in a denial of service if, for example, while iterating over the length byte, we read from an unmapped page. But more interestingly, we can cause an information leakage vulnerability. So, tf.dns.label.to.ascii, the function that does the copies, has no bound checks either. This means that data from the heap could be interpreted as an MX hostname. This MX hostname is later resolved by the client in an attempt to get an IP address. So, this means that we can leak data from the heap inside the MX hostname itself in the query. This vulnerability affects track version at least 4.7, and it was fixed in later versions, as we will see. We don't know the exact version of the fix, but nonetheless, the vulnerability still affects devices in the wild due to the complex supply chain effect and the nature of the embedded devices, so some don't receive updates. So, this is nice and all, but we are looking for an RCE, so let's go back to the function that computes the length and examine its operator further. So, there are more issues with expandLabelLength. So, per the RFC, there is a limitation on the maximum domain name length. The limitation is 255 characters, and this limitation is not enforced in expandLabelLength. Further, it does not validate the characters of the domain name. They should be all alphanumeric and hyphen, but it doesn't validate it, so we can embed bytes within the name itself. And, last but not least, the totalLength variable is stored as an unsigned short, 16 bits width, and recall that the totalLength is the variable that is returned from the expandLabelLength. So, what we try to do is we try to get our RCE by overflowing the totalLength variable. So, what we need in order to pull this thing off is to construct a name whose length is larger than 64k. So, we ask ourselves, is it really possible? This is not trivial, this is possible over UDP. So, can we overflow the totalLength variable within a single DNS response packet? The answer is yes, and we use the DNS compression feature to achieve this. The idea was to nest compression pointers within themselves, so recall that expandLabelLength does not validate the bytes of the domain name itself. This means we can embed there any bytes we want, and in this case we chose to embed compression pointers. You will see in the example shortly. Keep in mind during the example that we have two challenges to overcome. First, there is a limitation on the DNS response packet size. The maximum size allowed over UDP by the network stack is 1460 bytes. And keep in mind that we can only jump backwards from a current label pointer, so we will need to overcome this challenge also. Here you can see on the slide the basic construction we used. This is a name, arranged in a matrix-like form. Each row in this matrix has length 16. The bluish cells represent compression pointers, and the pink cells represent branch bytes. We will talk about those shortly. Let's assume we start expanding the name from this offset, offset 0xf in the first row. We can actually achieve this in practice by using another compression pointer that will land us exactly in that spot, but for now take it as a given. If we start expanding from this offset, this byte is interpreted as a length byte. In this case there is no compression, so the function skips 0xf plus 1 bytes and moves to the next row, as well as adding 16 to the total length variable. The process continues. Notice that we stay in the same column because of the special matrix shape until we reach branch bytes. The purpose of the branch byte is simply to lead us to the next compression pointer. In this case the branch byte is 0e, so we land in this compression pointer. We know that this is a compression pointer because the high nibble is c. What the function does is reads the compression offset. The compression offset in this case is 0e. It then checks to see that we point backwards from our initial position. Our initial position was 0xf, so 0xe is less than 0xf. We continue expanding the name from here. The process continues. We reach a branch byte and another compression pointer. You can see that with this compression trick the total length variable nearly doubled itself. If we continue until we reach a null byte, in this toy example we reach a total length value of 1500 bytes, which is pretty neat if you consider that the name itself only occupies 128 bytes. Of course, this doesn't overflow the total length variable yet, but in order to overflow it we use the maximum label length allowed, 63x3f, instead of xf shown in the example. Using exactly this construction we reach a name of length 64k greater than 64k, thus overflowing the total length variable itself. And remember that if expandLabelLength returns a value which is too small, we have an heap-based buffer overflow which is a good RCE candidate. Also know that this vulnerability can be triggered in response to every query type supported by the network stack by using CNAME resource records which must be parsed regardless of the original query type. So this vulnerability affects the latest track version at the time of disclosure, and that's considered dangerous from our point of view. So at this point, we decide to purchase another device that runs track in this case Schneider UPS device, and we want to know if the vulnerability affects him or not. And what we found is that track fixes the read-out-of-bank vulnerability, but they fix it badly. So you can see that the RDLength value from the resource record itself checks again the remaining size of the packet. And now after the fix, expandLabelLength accepts a third argument, labelEndPtr which is computed based on the RDLength value. And what expandLabelLength does when it reaches this endpoint, it simply stops processing without any error and returns the current total length. So this is perfect from an attacker's standpoint because RDLength is attacker-controlled. So if, for example, we specify an RDLength value which is too small than the actual value, we will have an AWS buffer overflow. So here you can see a resource record, and instead of specifying an RDLength value of 20, we will specify 7. So the labelEndPtr will point here expandLabelLength returns 5 in this case but tf.dnsLabelToAraski will copy the entire MXSourceName, thus overflowing our buffer. So this is the second heap-based buffer overflow vulnerability we found. And we also found during the MXParsing logic a memory leak. We found that we can leak another infrastructure. So here you can see another infrastructure is allocated and in these two error flows, the other info is not freed. So this means we can leak another infrastructure by specifying an RDLength value which is strictly less than 2, like 1, or by causing expandLabelLength to return a 0 length for example by using a bad compression pointer. So the size of the leak is 0x3c and these artifacts come in handy when exploiting vulnerabilities and in fact we use the exact same memory leak in our exploit, as you will see later. So to summarize, we saw three vulnerabilities that comprise CVE 2020.11.9.1 and an artifact. So the first vulnerability, the read-out-of-bounds affects other versions of the network stack and was fixed in later versions. The integer overflow vulnerabilities, as far as we've seen, affects both old and versions of the TrackTCP IP stack and the bad RDLength vulnerability is a result of a bad fix for the read-out-of-bounds vulnerability and thus affects only newer versions of the stack. The artifact is present in both old and newer versions of the stack. And the main takeaway from this part is that a device can be affected by one or more vulnerabilities depending on the exact version of the track they're using and this fragmentation makes the life of the IT security personnel more challenging to know whether the devices are affected or not. So now I'll hand over the mic to Ariel Schon. He will talk about exploitation. Thanks, Moshe. Hi, I'm Ariel. I'm also a security researcher at JSOF and today I'm going to talk about exploiting the vulnerability, the CVE, on a Schneider Electric UPS device. So a UPS device is basically a big battery. UPS stands for uninterruptible power supply and you connect devices to it instead of directly to the wall, to the outlet to protect them from power outages or power fluctuations of any sort. So we're going to exploit on a UPS made by Schneider Electric specifically on the network card. This network card houses a Turbo 186 processor. It's an x86 based processor. All code runs in 16-bit real mode meaning also there are no modern mitigations at all so no depth, no SLR. This processor is x86 based. It's not strictly x86 as seen in this processor as a weird segmentation scheme. Instead of shifting the segment register by 4 bits like on x86, it does it by 8 bits and we'll see this feature come into play later. During this research, we had essentially no debugging capabilities so no JTAG, no GDB, nothing of the sort so we relied mainly on static analysis and reverse engineering using only limited crash dumps as assistance. These crash dumps as visible feature a basic stack trace and some registers but nothing much. So just to recap the vulnerability our primitive is a heap overflow through DNS response parsing and this is a rather new track implementation so we can only overflow with alphanumeric characters and hyphens and periods. And we're going to use the bad rdelect vulnerability variant that Moshe talked about earlier. So when exploiting heap overflows, generally there are two methods either through metadata corruption so overflowing free list pointers, block sizes stuff of the sort or by overflowing application specific data structures allocated on the heap. Generally metadata is considered a more generic exploitation method as it doesn't rely on extensive shaping and this is the exploitation method we used in the earlier CVE on the DigiConnect device that we have a white paper about this and you can find on our website and we wondered if we can use the same technique on this device as well. So the track heap as implemented on the Schneider Electric QPS is slightly different. We have a free list that looks like this it has a size field, next pointer to the next free list block some free data usually containing garbage if it's a free block and another post size field. So this heap features a tight fit preference it will always allocate the smallest free block available for the allocation size requested it also features free block coalescing so there are never two adjacent free list blocks and it has all sorts of verifications and asserts that we didn't have in the previous implementation on the Digi device so for example on every heap operation the entire free list is checked one of the checks is that the first size field and the last size field really do match allocated blocks however are only checked when free so this is a bit easier corrupting the heap in a way that will not cause a premature crash due to these checks on heap operations Using only alphanumeric characters is rather hard, so we chose to go in a different way by overflowing data structures this time. So we know we can overflow through all DNS response types, however we chose to overflow through MX requests specifically because when the device boots up, it will send out three MX requests. So that is a good exploitation primitive. Also, three requests is very good for us for the heap exploit, since interactivity is always advantageous. It allows us a bit more flexibility in shaping our heap exactly the way we want. And we don't really mind that this happens only on device boot, as we probably would have to crash the device anyway. As said, we had limited debugging capabilities during this research, so not much insight into the heap and how it is shaped. So we would like to get the heap into a state that is as deterministic as possible. So crashing is good in this manner. And crashing the UPS network card has a very low penalty, as it doesn't affect the actual UPS operation in any way. It doesn't affect the power supply. And the network card will automatically boot right up after a few seconds, so the penalty is very low. So overflowing data structures. We chose to overflow a structure called tsDNSCacheEntry. This structure holds information about the DNS request-response pair. It has all sorts of interesting fields. For example, the dnseAdderInfoPtr field that holds a list of AdderInfo structs. These structs hold information about a certain DNS response. So for example, if you resolve the name, this structure has a field that will hold a pointer to the name that was resolved. Other than that, it has other pointers and interesting fields, such as, it's a doubly-linked list, as you can see, so it has an X and previous entry pointers. And these are always interesting from an exploiter point of view. And this structure is referenced often in DNS response parsing, which is a logic we can easily trigger. So this was a natural candidate for us. So assuming we can overflow this structure, what can we do with it? This is a pseudocode snippet from the parsing logic. It specifically shows how CNAME records are parsed. So we can see that, first thing, a pointer is taken from the DNS cache entry into a stack variable. Then the CNAME is allocated on the heap and the data is copied. This is, of course, data we control as we provide the DNS response. And subsequently, the pointer to the new CNAME allocated on the heap is placed into what the stack variable points to. Again, this pointer was taken from the DNS cache entry. So if we overflow the cache entry, we can control to where this heap pointer to our CNAME is written to memory. This is a control pointer, right, essentially? We can write a four-byte pointer to data we control on the heap. In x86 16-bit, this is a two-byte offset and two-byte segment. We can write this to any alphanumeric address as our overflow is alphanumeric. This is a relatively strong exploitation primitive. Writing data into places you're not supposed to write into is always good when you try to exploit. So this is the primitive we chose to continue with. Our overflow is a simple heap overflow, meaning it is from the end of our buffer with no offset into the next, what lies in the heap next. So we would like, naturally, the cache entry to be placed after the MXNAME buffer. Because of the heap structure in this specific trick implementation, we have all sorts of limitations. For example, the cache entry is allocated on request creation. However, the MXNAME buffer is allocated on DNS response parsing. So chronologically, it is allocated later when we want it on the heap to be allocated before so we can overflow into the cache entry. Also, because free blocks are checked very often in this heap implementation for corruption, we cannot write free data. We must overwrite only allocated data. It will be best if we can overflow directly into the structure without corrupting any heap data in the way. So we need to shape the heap in some way to get this to happen. A specific call pattern is preferable as we would like to overcome the chronological problem. We can do this because of type fit preference. So if we create, for example, hole two, as seen in this diagram, this is the size of the DNS cache entry structure. And before it, we have another hole the size of the MXNAME buffer we're going to allocate. We can overflow from the MXNAME buffer into the cache entry. We do need to separate them with some allocated separator to prevent the two free blocks from being coalesced together and ruining our shape. So we need some allocation primitives to create this. We need an allocation to create holes and an allocation to create the separator. So to create the holes, we can use a temporary allocation primitive. And this is relatively easy to achieve as every DNS answer has all sorts of names in it or we can cause it to have names in it. So either MX or PDR or CNAMES, they all have name fields. And these names cause allocation on the heap. So the allocation is the size of the name we provide and its data is, of course, alphanumeric and controlled. This allocation will also cause a small other infrastructure to be allocated as well. We need to take this into account when shaping. This allocation is freed when DNS parsing fails or when the record TTL expires. So this allocation is perfect for creating free regions of arbitrary size, which is what we need. However, we need to separate our free regions and we can do this using the AdderInfo memory leak, the memory leak artifact Moshe talked about earlier. And just to recap shortly, we can see that an AdderInfo structure is allocated and only then the record validity is checked. And if it's not valid, we will exit through an error flow and that will not free the allocation. So we basically have a memory leak of a known size and we can use this as a separator between our two free buffers. So we managed to shape the heap into the specific shape we want. And this allows us to reliably overflow the DNS cache entry every time. So assuming we can do this, and we just showed we can, what can we do with the CNAME pointer override primitive? What can we override? So this pointer write has all sorts of limitations. It's written to an address we overflowed into the cache entry, so naturally only alphanumeric addresses are allowed. However, this is a string overflow vulnerability. After all, we're overflowing with a name, so we do have a trailing null byte. In x86, little-endian architecture, this trailing null byte can be the most significant byte of the segment, allowing us a bit more flexibility in the addresses we can reach. However, nothing in this specific firmware is placed in a strictly alphanumeric memory address, so no code, no heap, no stack, no globals, nothing interesting we can override. Luckily, due to the weird segmentation of this Turbo 186 processor, we can use a little trick and easily combine two alphanumeric bytes to reach a non-alphanumeric byte or a non-alphanumeric segment, and this will look something like this. For example, we choose the segment to be null byte 4b, which is allowed. It will then be shifted by eight bytes and an alphanumeric offset will be added to it, resulting in a linear address containing a non-alphanumeric byte, which corresponds directly to a non-alphanumeric segment. So using this technique due to the weird segmentation feature, we can reach the heap utility code section, which has all sorts of functions, such as free and malloc, which we can overflow. Again, this is a 16-bit real mode, so we don't have DEP or ASLR, allowing us to overwrite code with our primitive. So when overwriting code in x86, one of the interesting destinations to overwrite with a pointer is a far call opcode, as a far call has an absolute address encoded directly in it. So if we can overwrite this destination address with our pointer, we can cause the execution flow to move into our CNAME buffer. So we can patch the far call using our primitive and execute our controlled payload. So we do this exactly, we patch a far call in the free air flow, which is called when metadata corruption is detected. Naturally, we do corrupt the heap when overflowing, so this air flow will take place when our allocated blocks are freed. So let's shortly recap what we did here. First, we shaped the heap in order to get the MXNAME buffer to be directly before the DNS cache entry with a separator in between that's not shown here. We then overflow to write the DNAC other info PTR to point into a far call in the free function. We then process a CNAME record, the parsing logic will process a CNAME record containing some evil alphanumeric payload, which will be allocated on the heap and a pointer to this payload will be placed into the address we put in the DNAC other info PTR, overwriting a far call destination, directing it to our evil payload, which can be, for example, some alphanumeric shellcode. Triggering this payload is rather easy, the free air flow will be triggered when the MXNAME record we overflowed from is free. And the CNAME buffer, specifically what we overflowed with, contains a two-stage alphanumeric shellcode, which will decode itself and allow us to run essentially arbitrary payloads, achieving arbitrary payload execution. Specifically, our payload just turns off power to all UPS outlets, turning off any critical device that was supposed to stay on. And we will now see a short demo of this payload execution. Thanks for listening and tuning into our presentation. We'll be glad if you join us in the Q&A.