Re: [PATCH] Bluetooth: Fix the vulnerable issue on enc key size

From: Marcel Holtmann
Date: Sun Sep 27 2020 - 08:04:57 EST


Hi Alex,

>>>>> When someone attacks the service provider, it creates connection,
>>>>> authenticates. Then it requests key size of one byte and it identifies
>>>>> the key with brute force methods.
>>>>>
>>>>> After l2cap info req/resp exchange is complete. the attacker sends l2cap
>>>>> connect with specific PSM.
>>>>>
>>>>> In above procedure, there is no chance for the service provider to check
>>>>> the encryption key size before l2cap_connect(). Because the state of
>>>>> l2cap chan in conn->chan_l is BT_LISTEN, there is no l2cap chan with the
>>>>> state of BT_CONNECT or BT_CONNECT2.
>>>>>
>>>>> So service provider should check the encryption key size in
>>>>> l2cap_connect()
>>>>>
>>>>> Signed-off-by: Alex Lu <alex_lu@xxxxxxxxxxxxxx>
>>>>> ---
>>>>> net/bluetooth/l2cap_core.c | 7 +++++++
>>>>> 1 file changed, 7 insertions(+)
>>>>>
>>>>> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
>>>>> index ade83e224567..63df961d402d 100644
>>>>> --- a/net/bluetooth/l2cap_core.c
>>>>> +++ b/net/bluetooth/l2cap_core.c
>>>>> @@ -4150,6 +4150,13 @@ static struct l2cap_chan *l2cap_connect(struct
>>>> l2cap_conn *conn,
>>>>>
>>>>> if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
>>>>> if (l2cap_chan_check_security(chan, false)) {
>>>>> + if (!l2cap_check_enc_key_size(conn->hcon)) {
>>>>> + l2cap_state_change(chan, BT_DISCONN);
>>>>> + __set_chan_timer(chan,
>>>> L2CAP_DISC_TIMEOUT);
>>>>> + result = L2CAP_CR_SEC_BLOCK;
>>>>> + status = L2CAP_CS_NO_INFO;
>>>>> + goto response;
>>>>> + }
>>>>> if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
>>>>> l2cap_state_change(chan, BT_CONNECT2);
>>>>> result = L2CAP_CR_PEND;
>>>>
>>>> I am not following what you are trying to fix here. Can you show this with
>> a
>>>> btmon trace from an attacking device?
>>>>
>>>> Regards
>>>>
>>>> Marcel
>>>>
>>>>
>>>
>>> I'm sorry, I didn't have btmon trace from an attacking device.
>>> I didn't have the real attacking device. I just simulate the attacking.
>>> I have a device that can create one byte size encryption key.
>>> It uses the link key that was produced by pairing with the service provider.
>> Actually the KNOB (Key Negotiation of Bluetooth Attack) says, the link key is
>> unnecessary for the reconnection.
>>> I use this device to reconnect to service provider, and then initiate the Key
>> Negotiation for one byte size encryption key. Actually the attacker identified
>> the encryption key with some brute force methods.
>>>
>>> I want to provide the trace on service provider side.
>>
>> what kernel version are you running? I wonder if we should always return
>> L2CAP_CR_PEND here. Do you have a reproducer code?
>
> I'm running kernel 5.8.0-rc6 on acceptor and kernel 5.8.5 on the initiator which acts as an attacker.
> For the attack simulation, some code needs to be changed on each size.
> On the acceptor, the master parameter for bt_io_listen() in bluetoothd should be changed to FALSE in profiles/audio/a2dp.c a2dp_server_listen() and profiles/audio/avctp.c avctp_server_socket().
> The change makes the kernel not to change the role to master when it receives hci conn req event.
> I did the change in order to make the controller to send LMP_ENCRYPTION_KEY_SIZE_REQ PDU for one byte key size.
>
> On the initiator, the below encryption key size check should be removed.
> @@ -1622,10 +1624,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
> continue;
> }
>
> - if (l2cap_check_enc_key_size(conn->hcon))
> - l2cap_start_connection(chan);
> - else
> - l2cap_chan_close(chan, ECONNREFUSED);
> + /* Just simulate KNOB */
> + l2cap_start_connection(chan);
> + /* if (l2cap_check_enc_key_size(conn->hcon))
> + * l2cap_start_connection(chan);
> + * else
> + * l2cap_chan_close(chan, ECONNREFUSED);
> + */
>
> At last, I did the test as below:
> 1. On the initiator, pair acceptor
> 2. Run l2test -r -P 3 on the acceptor
> 3. Run l2test -n -P 3 <bdaddr> on the initiator
>
>>
>> The problem really is that the MASK_REQ_DONE indication is not enough to
>> make a decision for the key size. We have to ensure that also the key size is
>> actually available. If that is not yet done, then we should not check it. This
>> means that any response to L2CAP_Connect_Request PDU needs to be
>> delayed until the key size has been read.
>
> In my test case, the key size has been read from controller before the l2cap conn request PDU is received.
>
> < HCI Command: Read Encryption Key Size (0x05|0x0008) plen 2 #22 [hci0] 43.089859
> Handle: 1
>> HCI Event: Command Complete (0x0e) plen 7 #23 [hci0] 43.091528
> Read Encryption Key Size (0x05|0x0008) ncmd 2
> Status: Success (0x00)
> Handle: 1
> Key size: 1
>> ACL Data RX: Handle 1 flags 0x02 dlen 10 #24 [hci0] 43.140888
> L2CAP: Information Request (0x0a) ident 1 len 2
> Type: Extended features supported (0x0002)
> ......
>> ACL Data RX: Handle 1 flags 0x02 dlen 12 #34 [hci0] 43.148405
> L2CAP: Connection Request (0x02) ident 3 len 4
> PSM: 3 (0x0003)
> Source CID: 64

the easiest way to fake this is just to assign a different value than the one returned by Read Encryption Key Size on the acceptor side. No need to mess with LMP details.

diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 1df95145f574..741b7ad31ff8 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3034,7 +3034,7 @@ static void read_enc_key_size_complete(struct hci_dev *hdev, u8 status,
handle);
conn->enc_key_size = 0;
} else {
- conn->enc_key_size = rp->key_size;
+ conn->enc_key_size = 1;
}

If you add this change to both sides, what are the steps to reproduce this and what does btmon show? You might have to also enable dynamic_debug for l2cap.ko so that we see the function call trace.

I am a bit pedantic with this one, since it is critical to understand where the current changes to handle anything KNOB related have their shortcomings. I spent so much time testing every single corner case. Certainly I could have missed something, but if I really did, this time around I want to either simplify the code or properly comment it at least.

Please see the other email thread / patch from Archie trying to also add another encryption key size check.

Regards

Marcel