WEBVTT

00:00.820 --> 00:08.320
Hi everyone, welcome to our talk, Hacking the Supply Chain, about the Ripple 20 vulnerabilities that haunt hundreds of millions of critical devices.

00:08.440 --> 00:10.720
A little bit about us, JSOF.

00:10.720 --> 00:20.640
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.

00:21.220 --> 00:24.200
My name is Shlomi Overman, I'm a co-founder at JSOF.

00:24.200 --> 00:30.580
Together with me today, we'll be speaking Moshe Kohl, a security researcher at JSOF, and the finder of the Ripple 20 vulnerabilities.

00:30.720 --> 00:36.580
And Ariel Schon, a security researcher at JSOF, that was also heavily involved in the research.

00:37.100 --> 00:42.640
We'll be talking about Ripple 20 in general today, explaining what it is and how it evolved.

00:42.640 --> 00:50.600
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.

00:50.740 --> 00:58.420
Short spoiler here, it's one CVE, but we'll actually be talking about several vulnerabilities hiding behind this one CVE.

00:58.560 --> 01:05.580
And then we'll be going in-depth into exploitation on a specific vulnerable device, with a specific configuration.

01:06.220 --> 01:08.220
So, what is Ripple 20?

01:08.220 --> 01:14.340
Ripple 20 is a series of 19 zero-day vulnerabilities in a TCPIP stack called TREC-TCPIP.

01:14.440 --> 01:18.780
We say 19 zero-day vulnerabilities, but it sort of depends how you count.

01:18.780 --> 01:30.320
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.

01:30.320 --> 01:37.200
These vulnerabilities were amplified by the supply chain to affect hundreds of millions of devices in all kinds of verticals.

01:37.280 --> 01:43.280
Medical, a lot of critical devices, industrial control, enterprise, and a bunch of others.

01:43.280 --> 01:45.500
We'll explain how this came to happen.

01:46.180 --> 02:02.520
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,

02:02.520 --> 02:07.500
pending further research, if anyone wants to go into that.

02:08.560 --> 02:11.760
The vulnerabilities affect hundreds of different sub-medium devices.

02:11.760 --> 02:14.340
From brand names, you all know.

02:14.820 --> 02:27.100
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.

02:27.100 --> 02:41.220
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.

02:42.240 --> 02:53.540
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.

02:54.060 --> 02:56.740
Transportation devices are affected by Ripple 20.

02:56.740 --> 03:05.060
Pretty much everywhere you go, you'll see IoT devices in general, as well as specifically devices affected by the Ripple 20 vulnerabilities.

03:06.160 --> 03:21.880
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.

03:21.880 --> 03:37.760
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.

03:38.060 --> 03:39.700
How did this come to happen?

03:39.700 --> 03:44.000
So this happened because of the supply chain and security in the supply chain.

03:44.000 --> 03:55.660
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.

03:55.720 --> 03:58.020
And this piece of code was sold to an operating system.

03:58.020 --> 04:06.200
It was then sold onwards to a system on module, and the system on module embedded the operating system with our vulnerable library.

04:06.260 --> 04:12.360
And from this point, nobody even knows they're using this piece of code, this piece of TREC TCP IP stack.

04:12.360 --> 04:19.520
This system on module is then used in different devices, such as IV pumps, and the whole chain becomes vulnerable.

04:19.520 --> 04:32.820
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.

04:33.120 --> 04:44.400
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.

04:44.400 --> 04:56.500
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.

04:56.680 --> 05:00.760
This is what happened with Ripple20, and this is why it affects so many devices.

05:02.080 --> 05:09.840
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?

05:10.120 --> 05:26.660
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.

05:26.660 --> 05:38.280
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.

05:38.280 --> 05:50.140
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.

05:50.920 --> 06:00.000
Another interesting thing is, for us, it was, of course, a good attack surface, and going forward, there's the potential for zombie vulnerabilities.

06:00.060 --> 06:09.160
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.

06:10.840 --> 06:24.580
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.

06:24.580 --> 06:29.300
So, this is quite an interesting phenomenon, both for us, as well as for the industry.

06:30.260 --> 06:43.020
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.

06:43.020 --> 06:54.620
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.

06:54.740 --> 07:06.880
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.

07:07.140 --> 07:09.760
So, every instance of TREC is slightly different.

07:09.760 --> 07:14.660
It depends when the vendor stopped support of TREC, what version of TREC they're using.

07:14.660 --> 07:16.760
It also depends how they compiled TREC.

07:18.040 --> 07:25.280
Specific configuration options, specific features they bought or did not buy, how they're using, what memory manager they're using, etc.

07:25.540 --> 07:31.800
And so, every version of the TREC stack looks different, and the vulnerabilities change.

07:31.800 --> 07:44.660
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.

07:44.660 --> 07:48.160
So, very complex, very complex to understand.

07:48.180 --> 07:52.240
And, of course, at the very, very beginning of a long supply chain.

07:52.240 --> 08:01.640
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.

08:02.640 --> 08:08.260
The research itself, because TREC is so configurable, we had to take a few data points.

08:08.260 --> 08:09.980
We used six different devices.

08:09.980 --> 08:26.980
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.

08:26.980 --> 08:39.520
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.

08:39.520 --> 08:45.100
The research took approximately nine months, with changing, differing intensities throughout this time.

08:45.100 --> 08:51.740
Started September 19th, we reported the vulnerabilities June 20th, June 2020, of course.

08:52.360 --> 08:56.320
And then we discovered some things after the fact as well.

08:56.320 --> 09:15.280
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.

09:15.280 --> 09:23.220
So we were able to bypass our firewalls, and at least some of these vulnerabilities will route over the Internet.

09:24.180 --> 09:36.700
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.

09:37.100 --> 09:42.240
The device we'll be talking about today has one of these unique architectures.

09:42.240 --> 09:45.400
We can't talk about everything in the scope of 45 minutes.

09:45.400 --> 09:50.820
Of course, these are very complex, detailed things.

09:50.820 --> 09:56.120
The exploitation of all the vulnerabilities is something that we won't be able to go into in depth.

09:56.540 --> 10:08.680
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.

10:08.700 --> 10:13.320
Feel free to look them up on our site if you want to go into further technical detail.

10:14.440 --> 10:18.440
A little bit about the vulnerability, or the CV, that we'll be talking about today.

10:18.440 --> 10:27.280
CV-2020-11901 is a critical vulnerability in the TREC DNS resolver component, so client-side DNS vulnerability.

10:27.280 --> 10:34.320
Once successfully exploited, it allows remote code execution, as we've demonstrated and as you'll see today.

10:34.320 --> 10:42.780
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.

10:43.160 --> 11:01.560
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.

11:01.560 --> 11:04.440
Behind the scenes, four vulnerabilities.

11:04.460 --> 11:06.200
One, what we call an artifact.

11:06.200 --> 11:08.920
That's a bug that made it easier for us to exploit.

11:09.080 --> 11:12.600
And these vulnerabilities vary over time.

11:12.600 --> 11:23.460
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.

11:23.460 --> 11:26.520
They also change between vendor configuration.

11:26.680 --> 11:42.920
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,

11:42.920 --> 11:45.980
and they create a challenge for the whole industry.

11:45.980 --> 11:49.580
And we realized this is bound to happen again.

11:49.580 --> 11:59.920
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.

11:59.920 --> 12:19.880
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,

12:19.880 --> 12:28.940
as vendors, how the vendors make sure that their suppliers are performing secure development, pen testing their code, etc.

12:28.940 --> 12:42.380
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.

12:43.200 --> 12:46.280
Now, I'm going to hand over the microphone to Moshe Kol.

12:46.280 --> 12:51.860
We'll be talking about the different vulnerabilities, where they are in the code, and what they look like.

12:52.480 --> 12:53.720
Thanks, Shlomi.

12:53.740 --> 12:55.080
Hi, I'm Moshe.

12:55.080 --> 13:05.620
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.

13:06.240 --> 13:10.060
So, first, we need to refresh our memory about the DNS protocol.

13:10.060 --> 13:16.480
So, DNS is a core internet protocol designed to map between domain names and IP addresses.

13:16.620 --> 13:20.420
It's a query-response protocol, client-server architecture.

13:20.560 --> 13:24.700
So, the client resolves the name by issuing a query to a DNS server.

13:25.080 --> 13:39.820
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.

13:39.820 --> 13:45.300
In this case of type A, the value of the response is an IPv4 address.

13:45.580 --> 13:48.080
So, a little bit about the record types.

13:48.080 --> 13:53.060
So, DNS servers can return multiple answers in the same DNS response.

13:53.060 --> 13:55.920
An answer is specified as a resource record.

13:55.920 --> 13:58.600
Each resource record is associated with a name.

13:58.600 --> 14:01.700
We'll talk about the type and class fields shortly.

14:02.020 --> 14:05.500
The TTL field specifies the number of seconds.

14:05.500 --> 14:07.380
This record is valid.

14:07.380 --> 14:15.540
The value of the record is specified in the rdata field, whose length is specified in the rdlength field.

14:15.660 --> 14:20.680
So, both questions and answers have a type in the DNS protocol.

14:20.940 --> 14:26.780
Some of the common types include type A, which specifies an IPv4 address for the query domain.

14:26.780 --> 14:32.320
Type CNAME, which defines an alias, a canonical name for the query domain.

14:32.400 --> 14:35.160
This provides a level of indirection.

14:35.160 --> 14:41.180
And type MX, which defines the domain name of a mail server for the query domain.

14:41.180 --> 14:54.400
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.

14:54.400 --> 15:00.600
And because this is a name and not an IP address, this name needs to be resolved further into an IP address.

15:00.600 --> 15:08.620
So, the resolver issues a DNS query of type A in this case, in order to resolve the domain name of a mail server.

15:08.840 --> 15:19.340
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.

15:20.180 --> 15:25.860
So, a little bit about the domain names encoding, the binary format.

15:26.200 --> 15:30.020
So, domain names are encoded as a sequence of labels.

15:30.020 --> 15:34.240
So, www is a label, example is a label.

15:34.280 --> 15:40.680
Each label is preceded by a length byte, specifying the number of characters this label occupies.

15:40.680 --> 15:44.480
And the domain name is terminated by a zero length byte.

15:44.600 --> 15:49.460
And according to the RFC, the maximum label length is 63.

15:49.460 --> 15:51.020
This will come up later.

15:51.520 --> 16:00.680
So, what the designers of the DNS protocols notice is that there is a lot of repetition inside the DNS packet itself.

16:00.920 --> 16:07.380
So, in an effort to reduce the size of the DNS messages, they employ a simple compression scheme.

16:07.380 --> 16:14.800
In this scheme, compression is achieved by replacing a sequence of labels with a pointer to prior occurrence of the same sequence.

16:14.800 --> 16:17.580
So, here you can see a sample DNS response packet.

16:17.580 --> 16:23.880
You can see gmail.com is specified as an offset 0xc from the start of the packet.

16:23.880 --> 16:30.000
And it so happens that gmail.com needs to be specified multiple times inside the packet.

16:30.000 --> 16:36.960
So, instead of specifying gmail.com literally in the packet, again, we simply use the compression feature.

16:36.960 --> 16:42.280
We point to the previous occurrence of gmail.com.

16:42.280 --> 16:55.280
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.

16:55.820 --> 17:00.620
So, in the compression schemes, compression pointers are encoded in two bytes.

17:00.620 --> 17:08.900
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.

17:10.320 --> 17:23.700
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.

17:23.700 --> 17:33.440
Here you can see a snippet of pseudocode from this function, specifically the parsing logic handling MX resource records.

17:33.440 --> 17:39.880
So, what we can see here is that the function computes the length of the MX hostname.

17:39.880 --> 17:49.920
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.

17:49.920 --> 18:00.880
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.

18:00.940 --> 18:06.560
What it does is simply copies bytes from the encoded name until a null byte is reached.

18:06.800 --> 18:17.160
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.

18:17.160 --> 18:22.380
So, this motivates us to look further into expand.label.length and examine its operation.

18:22.760 --> 18:25.680
So, you can see a pseudocode of expand.label.length.

18:25.680 --> 18:30.880
What this function is basically doing is sums up all the length bytes while honoring compression.

18:30.880 --> 18:37.660
So, in more detail, it reads the current label length, then checks to see if there is compression or not.

18:37.660 --> 18:47.980
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.

18:48.320 --> 19:00.200
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.

19:00.200 --> 19:09.520
So, this means we can only jump backwards from where we were, and the process continues from this new label pointer.

19:10.320 --> 19:18.840
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.

19:19.140 --> 19:27.040
This could result in a denial of service if, for example, while iterating over the length byte, we read from an unmapped page.

19:27.240 --> 19:32.400
But more interestingly, we can cause an information leakage vulnerability.

19:32.620 --> 19:38.700
So, tf.dns.label.to.ascii, the function that does the copies, has no bound checks either.

19:38.700 --> 19:42.640
This means that data from the heap could be interpreted as an MX hostname.

19:42.640 --> 19:48.380
This MX hostname is later resolved by the client in an attempt to get an IP address.

19:48.420 --> 19:53.920
So, this means that we can leak data from the heap inside the MX hostname itself in the query.

19:54.320 --> 20:01.820
This vulnerability affects track version at least 4.7, and it was fixed in later versions, as we will see.

20:02.200 --> 20:19.040
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.

20:19.560 --> 20:28.100
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.

20:28.100 --> 20:30.780
So, there are more issues with expandLabelLength.

20:30.780 --> 20:38.680
So, per the RFC, there is a limitation on the maximum domain name length.

20:38.680 --> 20:44.860
The limitation is 255 characters, and this limitation is not enforced in expandLabelLength.

20:44.860 --> 20:48.780
Further, it does not validate the characters of the domain name.

20:48.780 --> 20:57.600
They should be all alphanumeric and hyphen, but it doesn't validate it, so we can embed bytes within the name itself.

20:57.600 --> 21:11.980
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.

21:11.980 --> 21:19.080
So, what we try to do is we try to get our RCE by overflowing the totalLength variable.

21:19.600 --> 21:27.020
So, what we need in order to pull this thing off is to construct a name whose length is larger than 64k.

21:27.020 --> 21:30.160
So, we ask ourselves, is it really possible?

21:30.160 --> 21:34.660
This is not trivial, this is possible over UDP.

21:34.740 --> 21:40.940
So, can we overflow the totalLength variable within a single DNS response packet?

21:41.000 --> 21:46.240
The answer is yes, and we use the DNS compression feature to achieve this.

21:46.440 --> 21:56.440
The idea was to nest compression pointers within themselves, so recall that expandLabelLength does not validate the bytes of the domain name itself.

21:56.440 --> 22:02.020
This means we can embed there any bytes we want, and in this case we chose to embed compression pointers.

22:02.020 --> 22:04.480
You will see in the example shortly.

22:04.480 --> 22:08.320
Keep in mind during the example that we have two challenges to overcome.

22:08.620 --> 22:12.140
First, there is a limitation on the DNS response packet size.

22:12.140 --> 22:19.480
The maximum size allowed over UDP by the network stack is 1460 bytes.

22:19.480 --> 22:27.040
And keep in mind that we can only jump backwards from a current label pointer, so we will need to overcome this challenge also.

22:28.600 --> 22:32.040
Here you can see on the slide the basic construction we used.

22:32.040 --> 22:35.000
This is a name, arranged in a matrix-like form.

22:35.000 --> 22:38.120
Each row in this matrix has length 16.

22:38.360 --> 22:44.740
The bluish cells represent compression pointers, and the pink cells represent branch bytes.

22:44.740 --> 22:46.960
We will talk about those shortly.

22:48.100 --> 22:54.500
Let's assume we start expanding the name from this offset, offset 0xf in the first row.

22:54.640 --> 23:03.160
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.

23:03.300 --> 23:08.800
If we start expanding from this offset, this byte is interpreted as a length byte.

23:09.000 --> 23:22.220
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.

23:22.220 --> 23:23.700
The process continues.

23:23.700 --> 23:34.660
Notice that we stay in the same column because of the special matrix shape until we reach branch bytes.

23:34.660 --> 23:39.480
The purpose of the branch byte is simply to lead us to the next compression pointer.

23:39.480 --> 23:44.440
In this case the branch byte is 0e, so we land in this compression pointer.

23:44.620 --> 23:49.100
We know that this is a compression pointer because the high nibble is c.

23:49.100 --> 23:54.200
What the function does is reads the compression offset.

23:54.480 --> 23:57.320
The compression offset in this case is 0e.

23:57.320 --> 24:01.460
It then checks to see that we point backwards from our initial position.

24:01.460 --> 24:06.060
Our initial position was 0xf, so 0xe is less than 0xf.

24:06.060 --> 24:08.600
We continue expanding the name from here.

24:08.620 --> 24:10.160
The process continues.

24:10.620 --> 24:14.260
We reach a branch byte and another compression pointer.

24:14.500 --> 24:20.920
You can see that with this compression trick the total length variable nearly doubled itself.

24:21.040 --> 24:35.920
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.

24:36.160 --> 24:51.920
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.

24:51.920 --> 25:02.580
Using exactly this construction we reach a name of length 64k greater than 64k, thus overflowing the total length variable itself.

25:02.580 --> 25:11.640
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.

25:12.500 --> 25:26.200
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.

25:26.200 --> 25:35.040
So this vulnerability affects the latest track version at the time of disclosure, and that's considered dangerous from our point of view.

25:35.040 --> 25:47.520
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.

25:47.520 --> 25:52.700
And what we found is that track fixes the read-out-of-bank vulnerability, but they fix it badly.

25:52.700 --> 26:00.980
So you can see that the RDLength value from the resource record itself checks again the remaining size of the packet.

26:00.980 --> 26:11.640
And now after the fix, expandLabelLength accepts a third argument, labelEndPtr which is computed based on the RDLength value.

26:11.640 --> 26:20.920
And what expandLabelLength does when it reaches this endpoint, it simply stops processing without any error and returns the current total length.

26:20.920 --> 26:26.600
So this is perfect from an attacker's standpoint because RDLength is attacker-controlled.

26:26.600 --> 26:35.280
So if, for example, we specify an RDLength value which is too small than the actual value, we will have an AWS buffer overflow.

26:35.580 --> 26:42.180
So here you can see a resource record, and instead of specifying an RDLength value of 20, we will specify 7.

26:42.440 --> 26:54.160
So the labelEndPtr will point here expandLabelLength returns 5 in this case but tf.dnsLabelToAraski will copy the entire MXSourceName, thus overflowing our buffer.

26:54.940 --> 27:00.300
So this is the second heap-based buffer overflow vulnerability we found.

27:00.300 --> 27:04.900
And we also found during the MXParsing logic a memory leak.

27:04.900 --> 27:07.540
We found that we can leak another infrastructure.

27:07.740 --> 27:16.300
So here you can see another infrastructure is allocated and in these two error flows, the other info is not freed.

27:16.340 --> 27:31.000
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.

27:31.360 --> 27:45.960
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.

27:46.900 --> 27:55.540
So to summarize, we saw three vulnerabilities that comprise CVE 2020.11.9.1 and an artifact.

27:55.540 --> 28:02.580
So the first vulnerability, the read-out-of-bounds affects other versions of the network stack and was fixed in later versions.

28:02.580 --> 28:18.940
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.

28:18.940 --> 28:24.080
The artifact is present in both old and newer versions of the stack.

28:24.080 --> 28:41.080
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.

28:41.360 --> 28:44.740
So now I'll hand over the mic to Ariel Schon.

28:44.740 --> 28:47.080
He will talk about exploitation.

28:48.040 --> 28:49.160
Thanks, Moshe.

28:49.160 --> 28:50.220
Hi, I'm Ariel.

28:50.220 --> 28:59.980
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.

29:01.880 --> 29:07.380
So a UPS device is basically a big battery.

29:07.380 --> 29:21.380
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.

29:22.220 --> 29:27.580
So we're going to exploit on a UPS made by Schneider Electric specifically on the network card.

29:28.240 --> 29:32.300
This network card houses a Turbo 186 processor.

29:32.300 --> 29:34.200
It's an x86 based processor.

29:34.320 --> 29:42.280
All code runs in 16-bit real mode meaning also there are no modern mitigations at all so no depth, no SLR.

29:42.580 --> 29:45.320
This processor is x86 based.

29:45.320 --> 29:51.480
It's not strictly x86 as seen in this processor as a weird segmentation scheme.

29:51.480 --> 30:00.480
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.

30:01.560 --> 30:19.380
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.

30:19.400 --> 30:26.000
These crash dumps as visible feature a basic stack trace and some registers but nothing much.

30:26.000 --> 30:42.460
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.

30:43.140 --> 30:50.500
And we're going to use the bad rdelect vulnerability variant that Moshe talked about earlier.

30:51.260 --> 31:09.140
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.

31:09.140 --> 31:33.480
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.

31:34.460 --> 31:39.640
So the track heap as implemented on the Schneider Electric QPS is slightly different.

31:39.660 --> 31:53.400
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.

31:54.340 --> 32:43.200
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,

32:43.200 --> 32:48.340
so we chose to go in a different way by overflowing data structures this time.

32:50.980 --> 33:03.280
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.

33:04.660 --> 33:08.800
So that is a good exploitation primitive.

33:09.800 --> 33:17.600
Also, three requests is very good for us for the heap exploit, since interactivity is always advantageous.

33:17.600 --> 33:22.340
It allows us a bit more flexibility in shaping our heap exactly the way we want.

33:22.860 --> 33:29.920
And we don't really mind that this happens only on device boot, as we probably would have to crash the device anyway.

33:29.920 --> 33:37.440
As said, we had limited debugging capabilities during this research, so not much insight into the heap and how it is shaped.

33:37.440 --> 33:42.820
So we would like to get the heap into a state that is as deterministic as possible.

33:43.600 --> 33:46.180
So crashing is good in this manner.

33:46.260 --> 33:54.640
And crashing the UPS network card has a very low penalty, as it doesn't affect the actual UPS operation in any way.

33:54.640 --> 33:57.760
It doesn't affect the power supply.

33:57.760 --> 34:04.240
And the network card will automatically boot right up after a few seconds, so the penalty is very low.

34:05.360 --> 34:07.680
So overflowing data structures.

34:07.680 --> 34:11.420
We chose to overflow a structure called tsDNSCacheEntry.

34:11.420 --> 34:16.620
This structure holds information about the DNS request-response pair.

34:16.780 --> 34:19.760
It has all sorts of interesting fields.

34:19.760 --> 34:25.180
For example, the dnseAdderInfoPtr field that holds a list of AdderInfo structs.

34:25.180 --> 34:29.540
These structs hold information about a certain DNS response.

34:29.560 --> 34:35.820
So for example, if you resolve the name, this structure has a field that will hold a pointer to the name that was resolved.

34:37.220 --> 34:45.900
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.

34:46.000 --> 34:49.020
And these are always interesting from an exploiter point of view.

34:49.900 --> 34:54.980
And this structure is referenced often in DNS response parsing, which is a logic we can easily trigger.

34:55.060 --> 34:57.460
So this was a natural candidate for us.

34:57.760 --> 35:01.460
So assuming we can overflow this structure, what can we do with it?

35:02.170 --> 35:07.440
This is a pseudocode snippet from the parsing logic.

35:07.820 --> 35:11.540
It specifically shows how CNAME records are parsed.

35:11.680 --> 35:19.400
So we can see that, first thing, a pointer is taken from the DNS cache entry into a stack variable.

35:19.400 --> 35:23.580
Then the CNAME is allocated on the heap and the data is copied.

35:23.580 --> 35:27.320
This is, of course, data we control as we provide the DNS response.

35:27.480 --> 35:37.560
And subsequently, the pointer to the new CNAME allocated on the heap is placed into what the stack variable points to.

35:37.600 --> 35:42.420
Again, this pointer was taken from the DNS cache entry.

35:42.540 --> 35:49.400
So if we overflow the cache entry, we can control to where this heap pointer to our CNAME is written to memory.

35:49.680 --> 35:51.920
This is a control pointer, right, essentially?

35:51.920 --> 35:55.580
We can write a four-byte pointer to data we control on the heap.

35:55.580 --> 35:59.440
In x86 16-bit, this is a two-byte offset and two-byte segment.

35:59.920 --> 36:04.320
We can write this to any alphanumeric address as our overflow is alphanumeric.

36:04.380 --> 36:06.980
This is a relatively strong exploitation primitive.

36:06.980 --> 36:12.240
Writing data into places you're not supposed to write into is always good when you try to exploit.

36:12.260 --> 36:15.720
So this is the primitive we chose to continue with.

36:16.720 --> 36:28.620
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.

36:28.620 --> 36:34.540
So we would like, naturally, the cache entry to be placed after the MXNAME buffer.

36:34.640 --> 36:41.140
Because of the heap structure in this specific trick implementation, we have all sorts of limitations.

36:41.140 --> 36:46.400
For example, the cache entry is allocated on request creation.

36:46.420 --> 36:50.080
However, the MXNAME buffer is allocated on DNS response parsing.

36:50.080 --> 36:56.980
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.

36:56.980 --> 37:05.880
Also, because free blocks are checked very often in this heap implementation for corruption, we cannot write free data.

37:05.880 --> 37:08.400
We must overwrite only allocated data.

37:08.400 --> 37:13.760
It will be best if we can overflow directly into the structure without corrupting any heap data in the way.

37:14.440 --> 37:18.380
So we need to shape the heap in some way to get this to happen.

37:19.260 --> 37:26.400
A specific call pattern is preferable as we would like to overcome the chronological problem.

37:26.400 --> 37:28.840
We can do this because of type fit preference.

37:28.840 --> 37:37.220
So if we create, for example, hole two, as seen in this diagram, this is the size of the DNS cache entry structure.

37:37.800 --> 37:42.320
And before it, we have another hole the size of the MXNAME buffer we're going to allocate.

37:42.580 --> 37:45.980
We can overflow from the MXNAME buffer into the cache entry.

37:46.220 --> 37:54.760
We do need to separate them with some allocated separator to prevent the two free blocks from being coalesced together and ruining our shape.

37:55.280 --> 37:57.840
So we need some allocation primitives to create this.

37:57.840 --> 38:01.900
We need an allocation to create holes and an allocation to create the separator.

38:02.940 --> 38:06.960
So to create the holes, we can use a temporary allocation primitive.

38:07.580 --> 38:16.260
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.

38:16.260 --> 38:19.460
So either MX or PDR or CNAMES, they all have name fields.

38:19.460 --> 38:21.740
And these names cause allocation on the heap.

38:21.740 --> 38:27.880
So the allocation is the size of the name we provide and its data is, of course, alphanumeric and controlled.

38:28.600 --> 38:33.420
This allocation will also cause a small other infrastructure to be allocated as well.

38:33.420 --> 38:35.600
We need to take this into account when shaping.

38:36.460 --> 38:42.320
This allocation is freed when DNS parsing fails or when the record TTL expires.

38:42.900 --> 38:49.180
So this allocation is perfect for creating free regions of arbitrary size, which is what we need.

38:49.180 --> 38:58.460
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.

38:58.760 --> 39:08.300
And just to recap shortly, we can see that an AdderInfo structure is allocated and only then the record validity is checked.

39:08.300 --> 39:14.000
And if it's not valid, we will exit through an error flow and that will not free the allocation.

39:14.000 --> 39:20.800
So we basically have a memory leak of a known size and we can use this as a separator between our two free buffers.

39:21.780 --> 39:26.080
So we managed to shape the heap into the specific shape we want.

39:26.080 --> 39:31.260
And this allows us to reliably overflow the DNS cache entry every time.

39:32.360 --> 39:40.120
So assuming we can do this, and we just showed we can, what can we do with the CNAME pointer override primitive?

39:40.460 --> 39:41.880
What can we override?

39:42.380 --> 39:45.900
So this pointer write has all sorts of limitations.

39:46.100 --> 39:52.600
It's written to an address we overflowed into the cache entry, so naturally only alphanumeric addresses are allowed.

39:52.660 --> 39:55.500
However, this is a string overflow vulnerability.

39:55.500 --> 39:59.200
After all, we're overflowing with a name, so we do have a trailing null byte.

39:59.400 --> 40:09.840
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.

40:11.160 --> 40:23.520
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.

40:24.460 --> 40:40.360
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.

40:40.360 --> 40:44.500
For example, we choose the segment to be null byte 4b, which is allowed.

40:44.800 --> 40:58.080
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.

40:58.160 --> 41:11.000
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.

41:11.000 --> 41:18.140
Again, this is a 16-bit real mode, so we don't have DEP or ASLR, allowing us to overwrite code with our primitive.

41:19.080 --> 41:31.420
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.

41:31.480 --> 41:40.980
So if we can overwrite this destination address with our pointer, we can cause the execution flow to move into our CNAME buffer.

41:41.100 --> 41:46.840
So we can patch the far call using our primitive and execute our controlled payload.

41:47.560 --> 41:54.840
So we do this exactly, we patch a far call in the free air flow, which is called when metadata corruption is detected.

41:54.880 --> 42:01.700
Naturally, we do corrupt the heap when overflowing, so this air flow will take place when our allocated blocks are freed.

42:02.960 --> 42:06.040
So let's shortly recap what we did here.

42:06.040 --> 42:14.160
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.

42:14.380 --> 42:21.800
We then overflow to write the DNAC other info PTR to point into a far call in the free function.

42:22.560 --> 42:40.400
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,

42:40.400 --> 42:46.660
overwriting a far call destination, directing it to our evil payload, which can be, for example, some alphanumeric shellcode.

42:47.760 --> 42:58.120
Triggering this payload is rather easy, the free air flow will be triggered when the MXNAME record we overflowed from is free.

42:58.860 --> 43:15.040
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.

43:15.400 --> 43:22.840
Specifically, our payload just turns off power to all UPS outlets, turning off any critical device that was supposed to stay on.

43:23.000 --> 43:26.840
And we will now see a short demo of this payload execution.

44:02.930 --> 44:05.590
Thanks for listening and tuning into our presentation.

44:05.590 --> 44:07.950
We'll be glad if you join us in the Q&A.

