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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux