WEBVTT

00:00.940 --> 00:06.080
Hello everyone, and thank you for joining my talk today called Don't Be Silly, It's Only a Lightbulb.

00:06.080 --> 00:08.680
Before we begin, just a brief introduction.

00:09.280 --> 00:11.440
My name is Eyal Itkin.

00:11.440 --> 00:15.060
I'm a vulnerability researcher working at Checkpoints Research Group.

00:15.240 --> 00:22.680
In my research projects, I tend to focus on embedded devices and protocols, and in which case I combine these both together.

00:23.100 --> 00:27.360
You can usually read more about my recent publications on my Twitter account.

00:28.500 --> 00:37.760
The motivation for this specific research started a few years ago in 2016, when I heard of a new smart IoT device, smart lightbulbs.

00:37.940 --> 00:49.620
I was already quite familiar with many smart gadgets and even smart air conditioner, which allows you to activate it remotely on your way home so the temperature will be just fine when you arrive.

00:49.920 --> 00:53.540
But why would I need a smart lightbulb?

00:53.540 --> 01:01.520
I only need a light when I'm physically in a room and I'm fully capable of toggling the switch on and off when I enter and leave the room.

01:01.520 --> 01:03.480
Why would I need an app for that?

01:03.480 --> 01:08.400
Well, I figured out that it won't catch, people won't buy it, and that's it.

01:08.600 --> 01:19.740
And apparently I was wrong, because according to a report from the end of 2018, more than 400,000 households in the UK were already using smart lighting solutions.

01:19.740 --> 01:22.360
So people are apparently buying this gadget.

01:23.000 --> 01:32.440
Okay, but if I connect my lightbulb to the mobile phone or to the internet, there will be some security risk about it, right?

01:32.920 --> 01:37.220
But when I ask people about it, I always receive the same response.

01:37.260 --> 01:40.820
Don't be silly, it's only a lightbulb, it's fine.

01:42.060 --> 01:48.400
Obviously, I'm not the first to study this test case and there is some great firework before me.

01:49.200 --> 01:57.260
We will start with a lightbulb worm by Colin Flynn and Eyal Ronen, which presented their research on Blackhead USA on 2016.

01:57.580 --> 02:04.160
Later on, they developed the research into IoT goes nuclear, creating a ZigBee chain reaction.

02:04.540 --> 02:11.500
This time they were joined by Adi Shamir and Achior Weingarten, and you can read all about it on their website.

02:11.860 --> 02:21.600
I really recommend you reading about the research because it was so cool, they actually took over all of the lightbulbs on campus in a war flying demo from their drone.

02:22.320 --> 02:37.720
Since I already was familiar with Eyal Ronen's work and both Eyal Ronen and Achior are all colleagues of mine, I talked with Eyal Ronen and together we decided that with his help, I'm going to continue on the research and take it to the next level.

02:38.160 --> 02:40.280
So, what did they find?

02:40.880 --> 02:47.920
Well, it turns out that attackers can remotely steal a lightbulb from a given ZigBee network and force it to join their own.

02:48.300 --> 02:54.820
Once it joined the network, attackers can send the lightbulb a malicious firmware update to fully take over it.

02:55.220 --> 03:04.980
And if that wasn't enough, it turns out that even a regular lightbulb can steal other lightbulbs and you don't need any fancy RF equipment or an antenna in order to do it.

03:05.840 --> 03:13.420
Using these three primitives together, the researchers claim that you can take over a selected set of lightbulbs in a given city.

03:13.420 --> 03:26.080
From each lightbulb, you'll take over the other lightbulbs in close proximity and so on and so forth in order to propagate and take over all of the lightbulbs in a given city in a nuclear-like chain reaction.

03:26.640 --> 03:32.940
Sadly for us, the vendor did fix the last vulnerability and we were only left with the first two.

03:33.840 --> 03:38.420
If you put it visually, we can see the network diagram over here.

03:39.300 --> 03:45.980
On the right, we can see a smart lightbulb which communicates with its bridge, the controller, over ZigBee radio.

03:46.060 --> 03:56.580
The bridge is connected to both the radio network and the computer network with an ethernet cable so we could control the lightbulb from our mobile phone through the controller.

03:57.040 --> 04:07.280
The attack itself will start when the attacker will transmit ZigBee factory reset messages to the lightbulb in order to confuse it and convince it to join their own network.

04:07.500 --> 04:15.220
Once it joined the network, attackers can send a malicious over-the-air firmware update to the lightbulb to fully take over the lightbulb itself.

04:16.300 --> 04:25.460
And while the original research only focused on the lightbulbs, we are focused on a much broader target and we want to take their research to the next step.

04:25.460 --> 04:27.520
We want to infiltrate the network.

04:28.340 --> 04:40.640
In order to infiltrate the network, we are hoping to find a vulnerability so that our lightbulb will be able to remotely exploit the ZigBee vulnerability in the bridge itself in order to take over it.

04:41.240 --> 04:49.740
Once we took over the bridge, the bridge can launch attack inside a computer network and hopefully allow us to take over the computers inside the network itself.

04:50.300 --> 04:54.240
Once we understood our attack vector, it's time to get started.

04:59.540 --> 05:03.000
But first, some preliminary slides on ZigBee.

05:03.460 --> 05:07.740
ZigBee is a suite of high-level protocols for closed proximity networks.

05:07.880 --> 05:15.540
It's an IEEE 802.154-based specs that defines a low-range, low-power radio.

05:16.000 --> 05:21.840
Not to be confused with IEEE 802.11, which stands for the more commonly used Wi-Fi.

05:22.720 --> 05:29.280
The maximal transmission unit, or the MTU, over ZigBee is only 127 bytes long.

05:29.280 --> 05:30.460
And I repeat that.

05:30.460 --> 05:36.680
The maximal packet we can transmit over the air contains only 127 bytes.

05:36.680 --> 05:45.100
And with this harsh limit, we should be able to somehow exploit vulnerabilities that we hope to find in the bridge.

05:46.540 --> 05:48.760
ZigBee has a full network stack of its own.

05:49.360 --> 06:00.200
Like any other network stack, it starts with the lower levels, which is the physical layer and the medium access control layer, which acts as the internet acts in normal computer networks.

06:01.040 --> 06:07.060
On top of that, we have the network access layer, which is responsible for both routing and encryption.

06:07.820 --> 06:15.380
Above that, we have the application sub-layer, which is responsible for routing our packet to the respective application in the upper layers.

06:15.820 --> 06:24.840
This application could be ZDP, which stands for ZigBee Device Profile and responsible for managing different aspects of the ZigBee protocol.

06:25.280 --> 06:32.960
It could be ZCL, ZigBee Cluster Library, which is responsible for managing the configurations of the different devices.

06:33.660 --> 06:37.880
And it could be whatever other application that the vendor decided to implement.

06:38.500 --> 06:42.420
Now that we understood how ZigBee works, it's time to meet our target.

06:43.360 --> 06:49.160
The target for this research is going to be the Philips Hue Smart Lighting, now under Signify.

06:50.400 --> 06:55.600
Signify controls approximately one third of the market in the UK, so it's a broad target.

06:56.040 --> 07:05.280
And we're going to focus specifically on the bridge because as we mentioned earlier, the bridge is connected to both the radio network and the computer network, and we hope to bridge the two.

07:07.000 --> 07:14.380
Specifically, we're going to focus on a second generation of hardware for the bridge, which is the rectangular version of the bridge as seen on the right.

07:15.460 --> 07:19.140
If you remove the plastic cover from the bridge, we can see the board.

07:19.760 --> 07:27.060
When looking on a board, we can see on the right, the main CPU, which is a Qualcomm CPU usually found on Wi-Fi enabled devices.

07:27.960 --> 07:34.080
Over the left, we can see an Atmel CPU, which we'll refer to as the ZigBee modem.

07:34.080 --> 07:42.440
And like any other embedded device, we have a clear pinout for serial debug, which turned out to be quite useful throughout this research.

07:44.120 --> 07:47.720
The main CPU is a MIPS architecture, which is quite unique.

07:47.800 --> 07:50.560
And the operating system is Linux.

07:50.620 --> 07:59.280
This is quite refreshing because in prior research projects, we mainly had to handle real-time operating systems, which are harder to debug.

07:59.340 --> 08:14.940
This time, since we have Linux, we hope to be able to root it and to access it directly using an SSH connection in order to extract the file of the firmware itself, and in order to hopefully install a remote GDB server in order to debug the device.

08:15.580 --> 08:24.300
And luckily enough, Colin O'Flynn details in his blog exactly how to root the bridge with a step-by-step guide followed by a video.

08:25.000 --> 08:28.400
Once finished, we will get a root SSH connection.

08:29.840 --> 08:37.720
When I started this research, LLNN told me that the bridge is using a single huge process that's pretty much doing everything.

08:37.720 --> 08:39.560
And this is the IP bridge.

08:40.460 --> 08:44.120
The IP bridge is a single process that acts as the brain of the device.

08:44.120 --> 08:53.160
It is responsible for parsing incoming ZigBee messages, maintaining the different ZigBee state machines, and pretty much doing everything it needs.

08:54.200 --> 09:04.040
And like any other embedded device, IP bridge runs with root privileges, which means that once we took over this process, we don't need any other vulnerability.

09:04.860 --> 09:13.120
Now that we saw all of these data, it's time to start looking for vulnerabilities and IP bridge is going to be our target.

09:17.320 --> 09:22.300
We had quite a slow start to begin with because due to technical issues, I couldn't root the bridge.

09:22.300 --> 09:27.840
It turns out that I don't have the right hardware equipment as my younger brother rightfully pointed out.

09:28.400 --> 09:35.320
And the package we ordered got delayed like pretty much any other research project I've done thus far.

09:35.780 --> 09:42.880
Instead of sitting idle, we decided to start working on the old firmware version, which we received from the original research team.

09:43.240 --> 09:50.680
This is a firmware version, which is a few years old, and we really hope that it didn't change a lot throughout the years, but we really don't know.

09:50.680 --> 09:54.300
We simply have to cross our fingers and hope for good.

09:55.980 --> 10:00.420
When analyzing IP bridge, we soon enough saw something quite odd.

10:00.920 --> 10:16.800
The code expects strings to be found inside incoming messages instead of bits, because we can see the strings as such as ZDP or ZCL or even connection, but the maximum message size is only 127 bytes long.

10:16.800 --> 10:20.180
We don't have enough room for textual strings.

10:20.180 --> 10:23.880
We have enough room for mere single bits and that's it.

10:23.880 --> 10:26.260
So what's going on?

10:26.780 --> 10:29.300
And things got complicated.

10:29.980 --> 10:32.700
It turns out we forgot about the ZigBee modem.

10:32.940 --> 10:40.900
The ZigBee modem uses Atmel's BitCloud SDK in order to parse incoming ZigBee messages before they arrive to the main CPU.

10:42.080 --> 10:48.060
Effectively, it acts as a co-processor that handles the ZigBee lower network layers instead of the main CPU.

10:48.840 --> 10:55.520
It converts the parse messages to textual form and sends them over serial to the main CPU to be handled.

10:56.200 --> 11:00.400
And of course we don't have the firmware for it, so it's a black box.

11:02.520 --> 11:08.880
The modem reduces the attack surface on the main CPU because complex parsing is offloaded to the modem.

11:08.880 --> 11:14.620
And if the main CPU doesn't handle a specific task, it can be vulnerable when doing so.

11:15.020 --> 11:22.520
And we don't fully control the messages which are sent to the main CPU because they are constructed on the modem itself.

11:23.960 --> 11:31.100
And if that wasn't enough, it puts a huge question mark on everything we find because maybe the modem checks it.

11:31.840 --> 11:33.660
Maybe there's no check at all.

11:33.880 --> 11:38.480
So we'll have to add this to the list of ongoing issues and really hope for the best.

11:40.100 --> 11:46.340
Our first vulnerability attempt focus on ZigBee cluster library, which is ZCL.

11:46.540 --> 11:54.460
ZCL is responsible for managing configurations and it really is similar to SNMP in traditional IT networks.

11:54.780 --> 12:00.660
It offers a read attribute, write attribute interface and it supports multiple data types.

12:00.880 --> 12:03.620
The first data type is Boolean.

12:03.640 --> 12:11.080
Then we have an unsigned integer of eight bits, an unsigned integer of 32 bits, and even a dynamic array.

12:11.760 --> 12:17.740
And a variable size data type in an embedded device is a sure recipe for vulnerabilities.

12:17.960 --> 12:19.680
It looks promising.

12:20.020 --> 12:26.280
When we dived in to look how ZCL arrays are being parsed in the code, we saw the following code snippet.

12:27.220 --> 12:37.300
Here we can see that at first, a one byte length field is read from the message and then a fixed buffer is allocated over the heap with 33 bytes.

12:37.940 --> 12:40.800
Don't be confused by the delay slot in MIPS.

12:40.800 --> 12:50.520
The assignment into register A0 is done chronologically before the invocation of the malloc function and that's why it's called a delay slot.

12:50.820 --> 12:54.820
Effectively, we allocate a fixed size buffer over the heap and that's it.

12:55.520 --> 13:01.920
Later on, we'll read the entire blob from our attackers packet into the fixed size buffer.

13:02.320 --> 13:03.860
And what does it mean?

13:03.860 --> 13:11.380
Well, a controlled one byte name copy into a fixed size heap buffer and that's the definition of a heap-based buffer overflow.

13:12.740 --> 13:17.700
Sadly for us, it isn't a vulnerability until we have a working proof of concept to trigger it.

13:17.840 --> 13:21.240
We don't have the latest firmware and things might get changed.

13:21.700 --> 13:24.780
The modem might block large ZCL arrays because we don't know.

13:25.140 --> 13:30.380
And we can't test it because we don't have the radio equipment to transmit the attack and check it.

13:30.900 --> 13:39.220
Luckily enough, the package arrived quite in time and we can now load the bridge and extract the latest firmware and check if something got changed.

13:39.560 --> 13:41.680
And of course it did.

13:42.040 --> 13:50.000
It turns out that now ZCL arrays are no longer supported and instead there was an additional support for ZCL strings.

13:50.000 --> 13:56.520
But if we look on the support of strings, we can see that the following code was changed.

13:56.520 --> 14:04.760
Now we allocate a dynamic buffer on the heap using the one byte length field plus an additional one byte for the added null terminator.

14:05.260 --> 14:09.240
This means we don't have any vulnerability this time, which is bad.

14:10.280 --> 14:18.260
Of course, we still have some good news because as you can see, the firmware was shipped with debug symbols.

14:18.660 --> 14:32.340
For some unknown reason, the new firmware version supports and was shipped with all of the names of all of the functions in the firmware, which was really quite useful for us for reverse engineering the new firmware.

14:33.220 --> 14:34.740
So what do we have?

14:35.000 --> 14:42.660
The latest firmware version dropped support for the ZCL arrays because now they use strings instead and strings don't have a vulnerability in them.

14:43.440 --> 14:46.620
It's a good time to search for other vulnerabilities.

14:47.000 --> 14:48.560
And we did try.

14:48.560 --> 14:54.840
We really, really did try and we covered most of the firmware and still we found pretty much nothing.

14:55.800 --> 15:02.680
And then remember, the ZCL strings were handled correctly and sent to another thread.

15:02.680 --> 15:07.600
But what does this thread do with the incoming ZCL strings?

15:07.600 --> 15:11.160
So we traced it down to this code snippet.

15:11.160 --> 15:19.580
In this code snippet, we can see the same memory location over the heap using the same fixed size we saw earlier.

15:19.580 --> 15:22.300
So that's quite suspicious.

15:22.920 --> 15:34.280
We can even see that later on, the buffer is being copied to the newly allocated heap buffer using the length field stored in the struct itself without any side checks.

15:34.560 --> 15:43.320
The only condition for reaching this code snippet is to have the fixed size of code of 16 stored in our struct.

15:43.540 --> 15:51.600
And when we go back to how we initialize the ZCL string struct, we can see that it indeed uses the same opcode, 16.

15:51.600 --> 15:58.140
So it turns out that someone forgot to finish the migration from arrays to strings.

15:58.180 --> 16:05.620
Strings should have been marked with F for strings and then be duplicated using a call to strdup.

16:05.620 --> 16:18.040
Instead, for some reason, internally they are still marked with 16 for arrays and are handled using the same vulnerable code snippet we saw in the earlier firmware version, which is quite good from our perspective.

16:18.040 --> 16:21.540
So the original vulnerability still exists.

16:21.540 --> 16:24.760
It is just buried deeper, that's all.

16:25.520 --> 16:29.540
Time to start transmitting ZigBee messages and hope to trigger the vulnerability.

16:38.300 --> 16:47.200
In order to play around with ZigBee, like the previous research, we decided to use the ATmega256RFR2x probe board.

16:47.340 --> 16:51.740
It enables us to send and receive ZigBee frames over the radio.

16:53.200 --> 17:01.080
It should be computationally equivalent to a light bulb, which is quite important because we are going to do all of our exploit from this board.

17:01.080 --> 17:08.700
And if we will succeed, then the same exploit could have been performed from a light bulb we took over using the previous research findings.

17:10.220 --> 17:17.840
In addition, it turned out to be even more important because there's really strict timing constraints in ZigBee.

17:17.840 --> 17:22.200
And the timing constraints dictate that we actually use C code from the board itself.

17:22.200 --> 17:25.040
We can't use Python from our laptop, for example.

17:25.980 --> 17:31.620
This means that the entire ZigBee code, the entire ZigBee stack that we need to implement should be in C.

17:31.620 --> 17:35.020
And the entire exploit should also be implemented in C.

17:35.020 --> 17:39.080
So it's not going to be easy, but we'll have to live with this.

17:40.260 --> 17:44.840
And the vulnerable flow is only accessible during a process called commissioning.

17:44.940 --> 17:47.120
And what is commissioning?

17:47.120 --> 17:52.340
Well, commissioning is a process of pairing and associating a new light bulb.

17:52.340 --> 17:54.260
We have two types of commissioning.

17:54.260 --> 17:59.280
We have classic commissioning and we have touch link commissioning.

17:59.280 --> 18:04.260
The Philips Hue app on Android, which we use, initiates a classic commissioning process.

18:04.260 --> 18:06.120
And so we focus on this one.

18:06.740 --> 18:11.260
Technically, in theory, the ZigBee specs explain the entire process.

18:12.080 --> 18:17.340
In practice, there's a lot of room for vendors to do as they wish, and trust me, they do.

18:18.960 --> 18:27.980
When analyzing the protocol, we got quite stuck because there was no documented flow, or at least no documented flow we could find.

18:27.980 --> 18:40.540
There was enough documentation for the structure of each specific message, but we don't know what message is supposed to be sent and when, as a response for what, so we don't have any clear flow for the commissioning.

18:41.300 --> 18:51.780
In addition, we can't sniff a full conversation from our light bulb and the bridge because too many messages are sent and they are sent too fast, and we simply miss some of them.

18:52.920 --> 18:56.780
If that wasn't enough, we need a cryptographic key.

18:56.900 --> 19:03.920
There is a transport key, which is shared between all of the ZigBee Lite devices, but we don't have the key inside.

19:03.920 --> 19:05.280
So how do we get it?

19:05.280 --> 19:09.860
We need to decrypt the messages really from an early phase in the commissioning.

19:09.880 --> 19:11.660
We will need the key.

19:13.040 --> 19:27.260
Luckily for us, we are not the first to tackle this issue, and in this blog post, you can find all of the information for all of the keys used in touch link commissioning and in classic commissioning with the values of the keys themselves.

19:27.440 --> 19:34.340
So we took the keys, we inserted them to Wireshark, and Wireshark can now automatically decrypt the messages we sniff.

19:35.740 --> 19:43.100
The analysis and the implementation part took a lot of effort, a lot of work days, but eventually it worked.

19:43.560 --> 19:45.940
We managed to fake our own light bulb.

19:45.940 --> 19:51.100
You can see a brand new checkpoint research light bulb with the model CPR123.

19:51.100 --> 20:01.180
So now our board is doing all of the classic commissioning phase, faking our own light bulb, so we could continue on.

20:01.480 --> 20:10.360
In order to help other researchers with this task, we decided to open source the full pickup of the classic commissioning as we sent it from our board.

20:10.360 --> 20:20.000
So instead of other researchers having to manually analyze the commissioning phase, you can now look on the pickup, the decrypted pickup with all of the messages we've sent.

20:21.940 --> 20:32.120
The lessons we learned thus far are, without user interaction, the bridge won't accept new light bulbs, which is a good security precaution, but it doesn't help us.

20:32.120 --> 20:36.340
We will need somehow to bypass this limitation.

20:36.900 --> 20:42.500
In addition, we have approximately one minute to commission as many light bulbs as we want.

20:42.500 --> 20:44.860
There is no check about it.

20:45.180 --> 20:57.280
The user will only see the light bulbs in the app after they finished a specific phase we labeled ZCL phase, which is pretty much the last phase in the commissioning.

20:57.780 --> 21:05.840
If we fake other light bulbs and we stop before the ZCL phase, the user won't be aware of that, which is pretty good for our attack.

21:06.520 --> 21:13.680
And the really good news are, we managed to trigger the vulnerability during the ZCL phase, so we have a success.

21:13.680 --> 21:15.300
We really have a vulnerability.

21:16.600 --> 21:18.820
There is no state machine check in place.

21:18.820 --> 21:25.220
We can simply send whatever ZCL strings we want, whenever we want, without any request to get them.

21:25.220 --> 21:35.260
So we can pretty much do anything we want with the long ZCL strings, as long as we do it during the ZCL phase, and specifically during the commissioning.

21:35.260 --> 21:40.560
Once the light bulb was fully added to the network, it can no longer trigger the vulnerability.

21:40.580 --> 21:44.440
The vulnerability will only be accessible during the commissioning phase.

21:45.560 --> 21:47.700
Time to start the exploit.

21:52.430 --> 21:56.090
Just a basic recap on our vulnerability before we continue.

21:56.730 --> 22:00.310
We have a linear-based buffer overflow over the heap.

22:00.850 --> 22:04.110
Our buffer size is limited to 70 controllable bytes.

22:04.270 --> 22:12.470
And this is because we only started with 127 bytes, which is the MTU on ZigBee, and the ZCL is quite high on the ZigBee stack.

22:12.470 --> 22:14.790
So we lost a lot of bytes due to headers.

22:15.230 --> 22:18.450
We will have to settle down with the 70 bytes we have.

22:19.330 --> 22:22.490
We don't have any byte constraints on our payload, which is good.

22:22.490 --> 22:26.950
We can send null bytes, non-printable bytes, pretty much everything we want.

22:27.730 --> 22:34.270
And the destination buffer is allocated over the heap using a fixed size buffer of 43 bytes.

22:35.870 --> 22:37.570
Time to focus the heap.

22:38.450 --> 22:44.190
The bridge uses uclibc, which is an old embedded libc implementation used in embedded devices.

22:44.830 --> 22:51.110
The chosen heap implementation inside uclibc is the malloc standard, which is dlmalloc behind the scene.

22:52.210 --> 22:57.230
Effectively, it is much like glibc, but with way less sanity checks.

22:57.510 --> 23:01.670
And this is because uclibc stands for micro-libc.

23:01.670 --> 23:09.610
It is meant to be used by devices without a lot of RAM, without a lot of CPU power, so it has to be slim.

23:09.750 --> 23:14.150
And being slim means there's less code and less sanity checks.

23:15.750 --> 23:19.730
All of our free buffers will fall into the range of what is called fast bins.

23:20.330 --> 23:26.490
There's a bin for each buffer size in multiple of eight bytes, starting from 16 being the minimal size.

23:26.490 --> 23:39.790
So we have a bin for all of the buffers up to 16 bytes, a bin for 16 to 24, a bin for 24 to 32, and so on and so forth.

23:40.210 --> 23:44.670
Inside each bin, we will have a singly-linked list of all of the free buffers.

23:45.910 --> 23:51.850
When examining the code for free in uclibc, we saw the following check.

23:52.110 --> 24:01.830
If size is less than or equal to some configurable amount called max-fast, we will use the fast bins and store our buffer inside them.

24:01.830 --> 24:08.870
In order to find the respective fast bin for our buffer, we will use the fast bin index macro we can see over here.

24:09.450 --> 24:17.070
In short, the macro divides the size by eight and subtracts two, because 16 should be the minimal size.

24:17.070 --> 24:24.190
But there's no check in place that the size indeed is bigger or equal to 16.

24:24.190 --> 24:36.970
So if we will be able to corrupt a buffer over the heap and modify the size, we could change the size to be eight or even zero, giving us the indices of minus one and minus two, respectively.

24:37.450 --> 24:47.330
In order to understand what will happen if we will use negative indices, we can see the malloc state start over here and the variables before the fast bins array.

24:47.810 --> 24:55.970
So if we will use index minus one, we will store our buffer on top of the max-fast before the fast bin array.

24:56.410 --> 25:02.690
Storing a pointer over the configurable size for fast bins will be too risky.

25:02.690 --> 25:04.690
It will simply ruin the heap.

25:04.830 --> 25:07.950
But what will happen if we'll use index minus two?

25:08.030 --> 25:14.730
Well, index minus two will store the pointer to our buffer before the malloc state variable.

25:14.730 --> 25:22.390
And before the malloc state start in memory, it turns out to be an unused variable, simply nothing.

25:22.470 --> 25:31.630
So if we free a buffer with size of zero, it will be stored somewhere in memory before the malloc state, and no one will reach it.

25:31.770 --> 25:38.750
The next buffer will be joined to it, creating a ghost link list we can refer to as the dev null fast bin.

25:38.850 --> 25:43.710
Since malloc is not aware of this bin, it won't extract buffers from it.

25:43.710 --> 25:55.750
And all of the buffers we free will be joined to this bin, but won't be returned to the heap itself, effectively enabling us to gradually shape the heap in the desired structure.

25:57.510 --> 26:00.710
The plan for our heap overflow is quite simple.

26:00.730 --> 26:06.050
We can see our buffer in blue and a buffer adjacent to it in memory in purple.

26:07.190 --> 26:11.830
The plan is to modify only the size and the pointer of the adjacent buffer.

26:12.890 --> 26:29.650
If you change the size to one, which means a size of zero bytes and one for the previous buffer is in use, then the buffer will go to the dev null link list because size zero will reflect it to index minus two.

26:29.930 --> 26:34.450
And the pointer will be changed to be our arbitrary address.

26:35.210 --> 26:37.870
Effectively, we want to get this structure.

26:37.870 --> 26:50.630
We want to corrupt the singly linked list inside fastbin so that an arbitrary buffer will be stored, or at least malloc will think that an arbitrary buffer will be stored in our arbitrary address.

26:51.090 --> 27:04.250
The goal is we want to confuse malloc so it will allocate a buffer at an arbitrary address and then will let us write our data into this buffer, effectively giving us a write-what-where exploit primitive.

27:05.070 --> 27:08.270
The heap shaping strategy is as follows.

27:08.570 --> 27:12.930
If we overflow this free fastbin buffer buffer, then we won.

27:12.930 --> 27:14.610
This is exactly what we aim at.

27:14.610 --> 27:19.510
We corrupted the singly linked list inside the fastbin and we will get our allocation.

27:20.490 --> 27:28.230
If we overflow the use buffer, then when it will be freed, it will go to the dev null fastbin and we won't see it any longer.

27:29.570 --> 27:36.630
If we overflow the use buffer that lives forever, oh, well, nothing would happen and no one will notice it.

27:37.650 --> 27:41.790
But if we overflow the free large buffer, then we're in trouble.

27:41.790 --> 27:45.450
We will probably crash soon, so we really don't want to do that.

27:46.150 --> 27:50.010
If done correctly, we will get the desired malloc-where primitive.

27:51.330 --> 27:54.850
malloc-where will grant us the ability to write over the got.

27:54.850 --> 27:58.030
The got is the global offset table.

27:58.090 --> 28:09.170
It's a global table of function pointers used to execute library functions, such as free, malloc, sleep, memcpy, and pretty much every other library call.

28:09.270 --> 28:12.530
And luckily for us, the got is at a fixed address.

28:13.630 --> 28:22.750
The modified function pointer will jump to our shell code, or at least in theory, because it sounds easy on paper, but it's way harder in real life.

28:28.190 --> 28:30.400
Time to construct the shell code.

28:32.310 --> 28:34.730
Location, location, location.

28:35.230 --> 28:39.470
We need to store a binary shell code in a fixed global address.

28:40.050 --> 28:50.190
The problem is we only get textual messages from the ZigBee modem, so we don't get any controlled binary data into the main CPU.

28:50.770 --> 28:57.450
We found only one good candidate for such a possible buffer, and this is the ZigBee phone book.

28:57.990 --> 29:03.470
This is an array of ZigBee addresses seen by, or advertised to, the bridge.

29:04.430 --> 29:12.870
It can hold up to 65 records of 16 byte each, giving us an upper bound of approximately one kilobyte for the entire shell code.

29:13.550 --> 29:17.130
It's not a lot of bytes, but it should be enough for us.

29:18.350 --> 29:23.410
The neighbor record inside the ZigBee phone book is constructed as follows.

29:23.470 --> 29:28.830
The first eight bytes are for the extended network address, which we fully control.

29:28.910 --> 29:35.190
After that, we have additional two bytes for the short network address, again, fully controlled.

29:35.890 --> 29:47.870
And our stroke of luck ended, which is quite reasonable, sorry, because now we have six bytes for miscellaneous fields, and we don't really control them.

29:47.870 --> 29:53.010
So we have 10 consecutive bytes we control, but we don't control the entire memory.

29:54.970 --> 29:57.310
Oh, and about that.

29:57.310 --> 30:06.730
It turns out the bridge is unstable when it gets more than 20 records, so this is going to be quite a small shell code.

30:07.790 --> 30:11.370
The initial plan was simple.

30:11.610 --> 30:16.690
It looks infeasible to restore the execution flow, because we pretty much ruined the heap.

30:17.350 --> 30:23.870
Instead, our shell code will try to patch the binary on the file system and install a backdoor.

30:23.870 --> 30:33.390
After the process will crash, a daemon will reboot the binary with our backdoor, and then we will trigger it from another light bulb.

30:34.410 --> 30:44.510
Problem is, both the patch and the file path don't fit in 10 consecutive bytes each, so we can't decode them properly in the ZigBee phone book.

30:44.530 --> 30:48.990
The solution for this problem is purely academic.

30:49.890 --> 31:02.570
An ideal shell code by reduction will be, use the 10 consecutive bytes per record to build a decoder, and the decoder will decode the rest of the records to be nicely ordered in memory.

31:03.430 --> 31:12.730
In MIPS 16, a jump to the next record will only cost us two bytes, giving us eight spare bytes in each record to construct our decoder.

31:14.530 --> 31:17.110
But there are problems with this plan.

31:17.650 --> 31:26.650
We need to clear the instruction cache before jumping to the unpacked shell code, and because this is a MIPS CPU and we crashed when trying.

31:27.590 --> 31:35.550
And if we use sleep to clear the cache, as people usually do, the watchdog will kill the process because it doesn't respond.

31:36.430 --> 31:47.270
And we don't have enough records to silence the watchdog, sleep, and use a decoder, so the ideal shell code is good on paper, but it's not practical.

31:48.350 --> 31:51.390
Instead, we went for a more bold design.

31:52.120 --> 31:53.930
We will restore the execution flow.

31:53.930 --> 31:56.130
We really don't have any choice.

31:56.830 --> 32:10.270
This means we will call mprotect to modify the permissions of the code in memory from executable to be executable and writable, and we will install the backdoor in RAM during the execution.

32:12.390 --> 32:20.450
A few days and one hand-crafted shell code later on, we have a shell code that fully restores the execution.

32:20.450 --> 32:25.390
It restores the GOT, it restores the HEAP, and it restores pretty much everything we need.

32:26.250 --> 32:32.570
In addition, the shell code will only cost us 16 records, which is well in budget, less than 20.

32:33.810 --> 32:37.090
And here we can see the shell code in IDA.

32:37.490 --> 32:48.010
From the first record, we will jump to the second, and then we'll jump to the third, and so on and so forth until we install the backdoor and restore the execution flow.

32:49.630 --> 32:55.810
When we connect all of the dots together, the backdoor shell code will give us an arbitrary write primitive.

32:56.590 --> 33:06.510
Our exploit will fake an additional legitimate light bulb, but we'll leverage this arbitrary write primitive we installed and we'll use it to write Scout's loader to memory.

33:07.450 --> 33:13.450
Upon execution, Scout will load the full payload, which in this case, we've chosen to use EternalBlue.

33:14.590 --> 33:29.830
Here we can see that Scout dropped the file on tmp.exploit with root privileges, and this tmp.exploit file will use EternalBlue to attack computers inside a computer network, which is a good time for our demo.

33:39.100 --> 33:42.200
Here we can see the light bulb and the user using it.

33:42.560 --> 33:43.920
We can see that it works.

33:43.920 --> 33:45.380
The app works fine.

33:45.380 --> 33:49.000
And the user can modify the color of the light bulb as he wishes.

33:54.900 --> 34:00.440
When the attacker starts, he will try to hijack the light bulb and steal it from the network.

34:00.820 --> 34:11.160
And once he joined the attacker's network, the attacker will change the light bulb to a hideous color to annoy the user, so that the user will see that something is wrong with his light bulb.

34:12.680 --> 34:16.500
So we took over the light bulb and now it's ours.

34:17.180 --> 34:28.240
When the user sees that something is wrong, you can see that he no longer controls the light bulb, he can't change the light bulb's color, and it seems to be unreachable when it sees it over the app.

34:29.120 --> 34:35.200
The only way to reinstall the light bulb is to delete it and tell the bridge to search for new light bulbs.

34:35.200 --> 34:38.160
And this is where our attack will officially start.

34:38.880 --> 34:53.500
We will fake a legitimate light bulb, so the user will be happy, this is our light bulb, and we will fake it as the user actually configures the light bulb as he wishes, so the user won't see that something is wrong with our fake light bulb.

34:56.270 --> 34:59.030
Behind the scene, the full attack will take place.

35:02.640 --> 35:08.780
Here we can see that the bridge is connected to the laptop, so it's connected to the computer network.

35:09.440 --> 35:14.140
And now behind the scene, we will use our vulnerability, shape the heap and take over the bridge.

35:14.140 --> 35:20.300
And from the bridge, we will use EternalBlue to attack the computer inside the network and pop a cock.

35:20.440 --> 35:22.480
So our attack worked.

35:24.420 --> 35:26.920
Time for the coordinated disclosure.

35:27.700 --> 35:32.120
The vulnerability was reported to Signify on the 5th of November, 2019.

35:32.780 --> 35:36.660
The vendor confirmed the vulnerability on the same day, which is quite impressive.

35:36.660 --> 35:52.320
It's even more impressive because not only the vendor sent its acknowledgement on the same day when it received our report, it actually acknowledged the existence of the vulnerability, which it found in the code according to our report.

35:52.380 --> 35:58.000
So the vendor did pretty much everything in a single day, which is very impressive.

35:59.700 --> 36:03.520
Later on, Signify issued a patch via an automatic update in January.

36:04.260 --> 36:07.680
And you can find the full details and advisory in our blog post.

36:07.680 --> 36:14.940
And the official CVE issued for this specific vulnerability is CVE-2020-6007.

36:15.580 --> 36:21.140
And if you have Philips Hue lighting in your office or in your home, don't worry.

36:21.140 --> 36:24.380
All of the products should have received the update by now.

36:25.660 --> 36:26.860
Conclusion.

36:27.960 --> 36:35.180
Even with an MTU of 127 bytes, ZigBee vulnerabilities are indeed exploitable as we've shown in our demo.

36:36.580 --> 36:41.000
Security mitigations only work when they are on by default.

36:41.480 --> 36:45.740
The IP bridge binary was compiled to use static fixed addresses.

36:45.740 --> 36:52.160
So we were able to target the GOT which was on a fixed address, even without an information leak vulnerability.

36:52.860 --> 36:55.780
The binary was not compiled with any stacking areas.

36:55.780 --> 37:02.000
The GOT was writable, so we could do pretty much everything we want to do once we had an initial vulnerability.

37:02.620 --> 37:10.200
Still, there was ASLR in place for the heap, for the stack, and for the loaded libraries, and that's thanks to Linux.

37:10.200 --> 37:18.580
So if it's on by default, you will get it, but if you need to manually do something in your makefile, you won't get it in most common cases.

37:20.000 --> 37:26.200
And small devices are becoming popular by the minute, and yet we can't even trust our own light bulbs.

37:26.200 --> 37:28.780
So maybe we should do something about it.

37:30.060 --> 37:37.500
We can't finish this research and this presentation without saying special thanks to everyone that helped make this research possible.

37:37.500 --> 37:42.840
So we have Eyal Ronyan for the research idea and the active guidance.

37:43.580 --> 37:48.860
Colin O'Flynn for the detailed write-ups on routing the bridge and the entire hardware analysis of it.

37:49.640 --> 37:56.100
For Peter, for publishing the ZigBee transport keys for the light bulbs, which we used and couldn't continue without.

37:56.960 --> 38:01.440
And for Yaron Itkin, for the crucial hardware support along the way.

38:01.560 --> 38:02.980
Thanks, little brother.

38:03.540 --> 38:10.760
And last but not least, for the entire CPR research team and for their support alongside this very long research.

38:11.340 --> 38:14.460
Until the next time, thank you for joining this talk.

