| From 06e7e776ca4d36547e503279aeff996cbb292c16 Mon Sep 17 00:00:00 2001 |
| From: Ben Seri <ben@armis.com> |
| Date: Fri, 8 Dec 2017 15:14:47 +0100 |
| Subject: Bluetooth: Prevent stack info leak from the EFS element. |
| |
| From: Ben Seri <ben@armis.com> |
| |
| commit 06e7e776ca4d36547e503279aeff996cbb292c16 upstream. |
| |
| In the function l2cap_parse_conf_rsp and in the function |
| l2cap_parse_conf_req the following variable is declared without |
| initialization: |
| |
| struct l2cap_conf_efs efs; |
| |
| In addition, when parsing input configuration parameters in both of |
| these functions, the switch case for handling EFS elements may skip the |
| memcpy call that will write to the efs variable: |
| |
| ... |
| case L2CAP_CONF_EFS: |
| if (olen == sizeof(efs)) |
| memcpy(&efs, (void *)val, olen); |
| ... |
| |
| The olen in the above if is attacker controlled, and regardless of that |
| if, in both of these functions the efs variable would eventually be |
| added to the outgoing configuration request that is being built: |
| |
| l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); |
| |
| So by sending a configuration request, or response, that contains an |
| L2CAP_CONF_EFS element, but with an element length that is not |
| sizeof(efs) - the memcpy to the uninitialized efs variable can be |
| avoided, and the uninitialized variable would be returned to the |
| attacker (16 bytes). |
| |
| This issue has been assigned CVE-2017-1000410 |
| |
| Cc: Marcel Holtmann <marcel@holtmann.org> |
| Cc: Gustavo Padovan <gustavo@padovan.org> |
| Cc: Johan Hedberg <johan.hedberg@gmail.com> |
| Signed-off-by: Ben Seri <ben@armis.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/bluetooth/l2cap_core.c | 20 +++++++++++--------- |
| 1 file changed, 11 insertions(+), 9 deletions(-) |
| |
| --- a/net/bluetooth/l2cap_core.c |
| +++ b/net/bluetooth/l2cap_core.c |
| @@ -3353,9 +3353,10 @@ static int l2cap_parse_conf_req(struct l |
| break; |
| |
| case L2CAP_CONF_EFS: |
| - remote_efs = 1; |
| - if (olen == sizeof(efs)) |
| + if (olen == sizeof(efs)) { |
| + remote_efs = 1; |
| memcpy(&efs, (void *) val, olen); |
| + } |
| break; |
| |
| case L2CAP_CONF_EWS: |
| @@ -3574,16 +3575,17 @@ static int l2cap_parse_conf_rsp(struct l |
| break; |
| |
| case L2CAP_CONF_EFS: |
| - if (olen == sizeof(efs)) |
| + if (olen == sizeof(efs)) { |
| memcpy(&efs, (void *)val, olen); |
| |
| - if (chan->local_stype != L2CAP_SERV_NOTRAFIC && |
| - efs.stype != L2CAP_SERV_NOTRAFIC && |
| - efs.stype != chan->local_stype) |
| - return -ECONNREFUSED; |
| + if (chan->local_stype != L2CAP_SERV_NOTRAFIC && |
| + efs.stype != L2CAP_SERV_NOTRAFIC && |
| + efs.stype != chan->local_stype) |
| + return -ECONNREFUSED; |
| |
| - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), |
| - (unsigned long) &efs, endptr - ptr); |
| + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), |
| + (unsigned long) &efs, endptr - ptr); |
| + } |
| break; |
| |
| case L2CAP_CONF_FCS: |